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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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 |
. |
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 |
...