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);
    }
}

Reply via email to