3 ms·
> There is no OoB access of an array Yes, there is. It does not matter that storage happens to be allocated beyond the end of said array. Strict aliasing impli
by el_pollo_diablo 1mo ago
> There is no OoB access of an array
Yes, there is. It does not matter that storage happens to be allocated beyond the end of said array. Strict aliasing implies that it is UB to reinterpret the array as anything else. And it is UB to access an array out of bounds.
Flexible array members specifically exist for these dynamically-allocated trailing arrays. They do not solve the strict aliasing problem, though.
> if the payload type has an alignment that's greater than the size of a pointer
The amount of padding is implementation-defined. The only portable guarantee is that 'payload' is aligned for its element type, char. To over-align, use _Alignas, as in:
struct node {
struct node *next;
_Alignas(max_align_t) char payload[]; // Satisfies all fundamental alignment requirements
};
- Joker_vD 1mo ago> It does not matter that storage happens to be allocated beyond the end of said array. It does matter, for malloc-returned storage. You can put whatever objects you want into that storage as long as it fits and the pointer is properly aligned. > Strict aliasing ...is not violated; memcpy takes a void pointer as its destination, sets the effective type of the storage behind it, and the treats it as an array of unsigned chars.
- el_pollo_diablo 1mo ago> It does matter, for malloc-returned storage. You can put whatever objects you want into that storage as long as it fits and the pointer is properly aligned. You can certainly store an object of arbitrary type, but here it is done through a pointer to an object with pointer arithmetic going beyond the allowed bounds. > memcpy takes a void pointer as its destination, sets the effective type of the storage behind it And, in doing so, may very well overwrite the unspecified padding following 'payload' in the structure, thus instantly destroying the effective type of the structure object itself. Subsequent accesses to the structure or its members will be UB. It seems to me that your argument hinges on two assumptions: - there is no padding following 'payload' (this would have to be statically asserted), - the pointer to 'payload' is indistinguishable from the pointer past the structure; in particular, provenance is not an issue. That is a very interesting discussion.
- drdexebtjl 1mo agoIf payload was ever dereferenced as a char array as well, I would buy the strict aliasing argument. But it’s not, it exists as a char pointer solely for pointer arithmetic. AFAIK The purpose of strict aliasing rules is to let the compiler assume that dereferencing pointers of different types never refer to the same memory. If ISO C treats this as UB, shouldn’t ISO C be fixed?
- dcrazy 1mo agoNo, you should fix your code to be compliant with ISO C. The optimizer isn’t going to wait for you to convince WG14.
- drdexebtjl 1mo agoI’m not chasing theoretical portability and checking off a box saying my code is 100% pure ISO C. If that’s important to you, don’t do this (and also don’t use pretty much any allocator!) Every sufficiently useful C codebase assumes specific implementations.
- Joker_vD 1mo ago> but here it is done through a pointer to an object with pointer arithmetic going beyond the allowed bounds. When you do "void *x = malloc(sizeof(struct node))", the returned storage doesn't have struct node object in it, it has an object of no effective type in it, with size "sizeof(struct node)". Taking the pointer to payload[] field is in no way different from doing "(char*) x + offsetof(struct node, payload)" — it takes a char pointer into the object storage, adds a number (less than the object's size) and so produces another char pointer that points somewhere inside into the object storage — and no, since there has been no actual referencing of the object's value, constructing such a pointer does not set the effective type of that object; and a char pointer is explicitly allowed to alias whatever storage. Then the memcpy sets the effective type, done. No UB anywhere.
- el_pollo_diablo 1mo ago