[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