[clang] [Clang] Fix __builtin_dynamic_object_size off by 4 (PR #111015)

Jan Hendrik Farr via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 4 17:28:56 PDT 2024


Cydox wrote:

> AIUI, GCC considers this to be a bug in the face of `counted_by`. The reason GCC returns "unknown" (`SIZE_MAX`) in the case of a pointer like that is because (prior to `counted_by`) if the pointer wasn't to local storage, it could no know if it was a singleton or not. i.e. it may be pointing into a larger array of objects, so it cannot know the size. (This is most clear for `char *` variables, for example.)

> This shows how GCC will adjust the `__bdos` when it is certain of the object being a singleton, but it still missing the "counted_by requires a singleton" logic. (See also `-Wflexible-array-member-not-at-end` in GCC). It also shows that Clang's `__bdos` can be lied to.

You can also lie to gcc if you do `__bdos(p->array, 1)`:
https://godbolt.org/z/ME5bd7nr9
```C
// gcc and clang: 40
void local_storage_lies_fam(void)
{
    struct foo local = { .counter = 10 };
    struct foo *p = &local;

    emit_length(__builtin_dynamic_object_size(p->array, 1));
}
```

I did some more digging and at least the comments in the tests from gcc seem to confirm the difference in behavior between lookind at `__bdos` of the FAM vs the whole struct:

Whole struct only uses the size from a known call to malloc, while bdos on FAM uses the counted_by attribute.

https://github.com/gcc-mirror/gcc/blob/3f10a2421c2b9c41e7c1b1c0d956743709f5d0be/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c#L113-L141:

```C
  p->foo = index + SIZE_BUMP;

  /* When checking the observed access p->array, we have info on both
    observered allocation and observed access,
    A.1 from observed allocation:
    	allocated_size - offsetof (struct annotated, array[0])
    A.2 from the counted-by attribute:
    	p->foo * sizeof (char)

    We always use the latest value that is hold by the counted-by field.
   */

  EXPECT(__builtin_dynamic_object_size(p->array, 0),
	 (p->foo) * sizeof(char));

  EXPECT(__builtin_dynamic_object_size(p->array, 1),
	 (p->foo) * sizeof(char));

  EXPECT(__builtin_dynamic_object_size(p->array, 2),
	 (p->foo) * sizeof(char));

  EXPECT(__builtin_dynamic_object_size(p->array, 3),
	 (p->foo) * sizeof(char));

  /* When checking the pointer p, we only have info on the observed
    allocation. So, the object size info can only been obtained from
    the call to malloc.  */
  EXPECT(__builtin_dynamic_object_size(p, 0), allocated_size);
  EXPECT(__builtin_dynamic_object_size(p, 1), allocated_size);
  EXPECT(__builtin_dynamic_object_size(p, 2), allocated_size);
  EXPECT(__builtin_dynamic_object_size(p, 3), allocated_size);
```

clang on the other hand will always do it's counted_by calculation if the attribute is there. Both for bdos on the FAM, as well as bdos on the whole struct.

https://github.com/llvm/llvm-project/pull/111015


More information about the cfe-commits mailing list