[clang] [Clang][counted_by] Load the count through unions by byte offset (PR #228309)
Michael Bommarito via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 9 05:02:02 PDT 2026
mjbommar wrote:
Hi all,
Just following up from the lkml post below to say that I hit this on 7.3-rc5 through xz -> `landlock_merge_ruleset()` on 26.04.
https://lore.kernel.org/llvm/20261009115108.17819-1-michael.bommarito@gmail.com/
My build is x86_64 clang 21.1.8.
In case it's helpful, here's some Claudeslop below from debugging the crash and getting here:
<CLAUDE>
Reduced userspace reproducer:
```c
#include <stdio.h>
#include <stdlib.h>
struct m { unsigned int v; };
struct dom {
void *pad[4];
union {
long w[4]; /* largest member, like work_struct */
struct {
unsigned int usage;
unsigned int num_layers;
struct m fam[] __attribute__((counted_by(num_layers)));
};
};
};
__attribute__((noinline)) static size_t bdos(struct dom *d)
{
return __builtin_dynamic_object_size(d->fam, 1);
}
int main(void)
{
struct dom *d = calloc(1, sizeof(*d) + 2 * sizeof(struct m));
d->num_layers = 2; /* expect 8 */
d->fam[0].v = 0x77; /* 476 if fam[0] is read as the count */
printf("%zu\n", bdos(d));
}
```
| compiler, `-O2` | result |
|---|---|
| clang 21.1.8 | **476** (0x77 × 4: the count is read from `fam[0]`) |
| gcc 15.2.0 | 8 (correct) |
Layout variants with clang 21.1.8:
| counter + FAM placed in | result |
|---|---|
| anonymous struct inside a union | 476 (wrong) |
| **named** struct inside a union (`d->s.fam`) | 476 (wrong) |
| anonymous struct, no union | 8 (correct) |
So the trigger is the union, not the anonymous struct. That fits the description that `getGEPIndicesToField()` walks the union's IR type (its largest member).
In the real kernel object, x86_64 `landlock_merge_ruleset()` loads the count with `mov 0x28(%rbx),%esi`, which is `handled_masks[0]`, while `num_layers` is at `0x24`. This matches the arm64 offsets in the PR description.
</CLAUDE>
https://github.com/llvm/llvm-project/pull/228309
More information about the cfe-commits
mailing list