https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66661
Florian Weimer <fw at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |UNCONFIRMED Resolution|INVALID |--- --- Comment #12 from Florian Weimer <fw at gcc dot gnu.org> --- Here's a new test case. It prints for me (without Address Sanitizer): count 2, align 4, minimum size 10, struct size 12, actual size 12 count 5, align 4, minimum size 13, struct size 16, actual size 20 count 0, align 8, minimum size 8, struct size 8, actual size 8 count 3, align 4, minimum size 11, struct size 12, actual size 24 * count 0, align 16, minimum size 8, struct size 16, actual size 8 count 8, align 4, minimum size 16, struct size 16, actual size 16 count 4, align 4, minimum size 12, struct size 12, actual size 12 count 2, align 4, minimum size 10, struct size 12, actual size 12 * count 2, align 8, minimum size 10, struct size 16, actual size 12 count 6, align 4, minimum size 14, struct size 16, actual size 16 count 7, align 4, minimum size 15, struct size 16, actual size 16 count 3, align 4, minimum size 11, struct size 12, actual size 12 count 3, align 4, minimum size 11, struct size 12, actual size 12 count 6, align 4, minimum size 14, struct size 16, actual size 44 * count 5, align 32, minimum size 13, struct size 32, actual size 16 count 8, align 4, minimum size 16, struct size 16, actual size 16 count 9, align 4, minimum size 17, struct size 20, actual size 20 count 7, align 4, minimum size 15, struct size 16, actual size 16 count 1, align 4, minimum size 9, struct size 12, actual size 12 * count 1, align 8, minimum size 9, struct size 16, actual size 12 I believe this shows that GCC has some bug in this area. Whether it's the over-reads (but over-reads can be fine here because they cannot trap, and GCC knows that they won't introduce observable data races), the object allocation in the .data section (again, could be harmless), or the Address Sanitizer report, I'm not sure. #include <stdalign.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> struct flexible { int count; int align; char bytes[]; }; #define ARGS_0 #define ARGS_1 1 #define ARGS_2 1, 2 #define ARGS_3 1, 2, 3 #define ARGS_4 1, 2, 3, 4 #define ARGS_5 1, 2, 3, 4, 5 #define ARGS_6 1, 2, 3, 4, 5, 6 #define ARGS_7 1, 2, 3, 4, 5, 6, 7 #define ARGS_8 1, 2, 3, 4, 5, 6, 7, 8 #define ARGS_9 1, 2, 3, 4, 5, 6, 7, 8, 9 #define DECL(name, count, align) \ _Alignas (align) struct flexible name = {count, align,{ ARGS_##count }} DECL (v4, 4, 4); DECL (v1, 1, 8); DECL (v7, 1, 4); DECL (v17, 7, 4); DECL (v15, 9, 4); DECL (v16, 8, 4); DECL (v11, 5, 32); DECL (v12, 6, 4); DECL (v5, 3, 4); DECL (v9, 3, 4); DECL (v13, 7, 4); DECL (v18, 6, 4); DECL (v2, 2, 8); DECL (v8, 2, 4); DECL (v10, 4, 4); DECL (v14, 8, 4); DECL (v0, 0, 16); DECL (v3, 3, 4); DECL (v5a, 0, 8); DECL (v19, 5, 4); DECL (v6, 2, 4); enum { count = 21 }; static int cmp(const void *a, const void *b) { struct flexible *const *a1 = a; struct flexible *const *b1 = b; if (*a1 < *b1) return -1; if (*a1 > *b1) return 1; return 0; } int main() { struct flexible *p[count] = {&v0, &v1, &v2, &v3, &v4, &v5, &v5a, &v6, &v7, &v8, &v9, &v10, &v11, &v12, &v13, &v14, &v15, &v16, &v17, &v18, &v19}; qsort (p, count, sizeof (p[0]), cmp); for (int i = 0; i < count - 1; ++i) { size_t min_size = offsetof (struct flexible, bytes) + p[i]->count; size_t align_mask = p[i]->align - 1; /* Struct size is the size that a struct with the requested length of the flexible array member would have. */ size_t struct_size = (min_size + align_mask) & ~align_mask; /* The actual size is the offset between this and the next object in the data section. (This can be an over-estimate if other objects not listed above are placed between the listed objects.) */ size_t actual_size = (p[i + 1] - p[i]) * sizeof (*p[i]); /* The lines marked with * have an object whose struct size exceeds the object size. If GCC assumes that objects always have their struct size allocated, this leads to an out-of-bounds acccess. */ printf ("%c count %d, align %d, minimum size %zu, struct size %zu, actual size %zu\n", struct_size > actual_size ? '*' : ' ', p[i]->count, p[i]->align, min_size, struct_size, actual_size); } }