Is it possible to use an out-of-range index with a smaller array that is preempted from a sufficiently large array?
In my day job, I came across a lot of C codes that resemble the following pattern. I am worried if this pattern is safe.
typedef struct
{
unsigned char someField : 4;
unsigned char someOtherField : 4;
unsigned char body[1];
} __attribute__((__packed__, aligned(1))) SomeStruct;
int main()
{
unsigned char pack[16] = {};
SomeStruct* structPack = (SomeStruct*)pack;
structPack->someField = 0xC;
structPack->body[4] = 0x5;
return 0;
}
My concern is that the program is using structPack->body[4]
which is still part of a 16 byte array, but is out of scope if we look at the definition SomeStruct
. So there are two ways to look at this:
- It refers to a valid memory location. There is no danger.
- It's out of band, so the behavior is undefined.
So my questions are:
- According to the C standard (more specifically, C89), is this pattern safe or undefined behavior?
- Also, is it safe for some specific compilers (like GCC) or platform?
- Are there any better alternatives?
Please note that this kind of code mostly runs on microcontrollers and sometimes runs as an application on the Linux desktop.
source to share
Accessing an object with an incompatible lvalue is undefined behavior. Alignment can be resolved by your attribute line, but using a pointer to access the object still breaks strict alias:
unsigned char pack[16] = {};
SomeStruct* structPack = (SomeStruct*)pack;
6.5. p7:
The object must have a stored value that can only be accessed by an lvalue expression that is one of the following types:
- a type compatible with the effective type of the object,
- a qualified version of the type, compatible with the effective type of the object,
- a type that is a signed or unsigned type corresponding to the effective type of the object,
- a type that is a signed or unsigned type corresponding to the qualified version of the effective object type,
- a collection or type of union that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregation or union), or
- the type of symbol.
Where is the effective type:
The effective type of an object to access its stored value is the declared type of the object, if any.
SomeStruct*
not compatible with char array.
The correct way to allocate SomeStruct is to use memory allocators or alloca (which will allocate the stack if it is a concern) if the feature is supported.
However, there is the problem of a member body
that is an array of size one, and the Standard does not allow access to it out of bounds (ie body [1]). c99 introduced a solution which is a flexible array element:
typedef struct
{
unsigned char someField : 4;
unsigned char someOtherField : 4;
unsigned char body[]; //must be last
}...
When you set the size to highlight this structure, you add extra size depending on how large the member needs to be body[]
.
source to share