Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Flexible array members is are a special type of struct, array where the last element of a structure struct with more than one named member is has an incomplete array type; that is, the size of the array is not specified explicitly within the struct.This technique has widely been known as the "struct hack." However, the classical method of the "struct hack" results in undefined behavior, although it may work most of the time

If a dynamically sized structure is needed, flexible array members should be used.

Non-Compliant Code Example

The following is the old, incorrect "struct hack" which results in undefined behaviorIn the following non-compliant code, an array of size 1 is declared, but when the struct itself is instantiated, the size computed for malloc() is modified to take into account the full size of the dynamic array.

Code Block
bgColor#FFcccc
struct hackflexArrayStruct {
     int num;
     char my_char;
     int data[1];
};

...
/* Space is allocated for the struct */
struct hackflexArrayStruct *hackPstructP = malloc(sizeof(struct hackflexArrayStruct) + sizeof(int) * (ARRAY_SIZE - 1));
if (!hackPstructP) {
     /* handle malloc failure */
}
hackPstructP->num = SOME_NUMBER;
hackPstructP->my_char = SOME_CHAR;

/* Access data[] as if it had been allocated as data[ARRAY_SIZE] */
for (i = 0; i < ARRAY_SIZE; i++) {
  hackPstructP->data[i] = i;
}

Wiki Markup
This is, strictly speaking, undefined behavior when accessing {{hackP->data\[iHowever, in the above code, the only member that is guaranteed to be valid, by strict C99 definition, is {{flexArrayStructP\[0\]}}. Thus, for all {{i > 0}}, becausethe thatresults goes beyondof the boundsassignment ofare the arrayundefined.

Compliant Solution

The solution is part of the C99 standard, the This compliant solution uses the flexible array member to achieve a dynamically sized structure.

Code Block
bgColor#ccccff
struct flexArrayStruct{
  int num;
  char my_char;
  int data[];
};

...
/* Space is allocated for the struct */
struct flexArrayStruct *structP = malloc(sizeof(struct flexArrayStruct) + sizeof(int) * ARRAY_SIZE);
if (!structP) {
     /* handle malloc failure */
}

/* Now, it is as if the struct were defined
	struct {int num; char my_char; int data[ARRAY_SIZE];} *structP;
and we can access the elements as if they were so. */

structP->num = SOME_NUMBER;
structP->my_char = SOME_CHAR;
structP->num = SOME_NUMBER;
structP->my_char = SOME_CHAR;

/* Access data[] as if it had been allocated as data[ARRAY_SIZE] */
for (i = 0; i < ARRAY_SIZE; i++) {
  structP->data[i] = i;
}

Wiki Markup
The prior method allows the struct to be treated as if it had declared the member {{data\[\]}} to be {{data\[ARRAY_SIZE\]}} in a way that conforms to the C99 standard
No actual array space is allocated within the struct object. By mallocating space for the struct, the space computation is done at runtime, and allows the programmer to dynamically allocate space for an array within a struct
.

However, some restrictions do apply. The incomplete array type MUST must be the last element within the struct. You also cannot have an array of objects structs if the object has struct contains flexible array members. Nor can such object , and structs that contain a flexible array member cannot be used as a member in the middle of another struct.

Risk Assessment

Although the old "stack hack" non-compliant method results in undefined behavior, it will work under most of the timearchitectures.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MEM38-C

1 (low)

1 (unlikely)

3 (low)

P3

L3

...