Yes, this turned out to be an instance of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117287
I hit what more and more looks like a bug in the standard Ubuntu 24 gcc version. Here is a minimal reproducer that I with great pain extracted from 12KLOC sources:
struct foo {
int seq;
void *data;
};
struct bar {
struct foo rung[1];
};
static int used;
static char ll(const struct foo *n) {
return *(char *)n->data;
}
int main(int argc, char **argv) {
struct bar p = {};
int result = 0;
used = 0;
__asm__ __volatile__("": : :"memory");
for (int i = 0; i <= used && result == 0; ++i) {
struct foo *r = &p.rung[i];
__attribute__((assume(i <= 0 || ll(r) + 1 == ll(r - 1))));
if (!(r->seq == 0 && (i <= 0 || ll(r) + 1 == ll(r - 1)))) {
result = -1;
}
}
return 0;
}
Compile as gcc -O1 gcc-13-bug.c
, it crashes with SIGSEGV. Note that because the loop iterates only once, i == 0
in the body of the loop, so ll(r)
should not be called. Yet, ll(r)
and ll(r-1)
are both called.
The reproducer is minimal in the sense that it is locally optimal: any small random change eliminates the effect. For example,
Move used = 0
over the asm
-barrier (or simply remove the barrier).
Remove result == 0
from the loop guard.
Replace struct bar p = {}
with struct foo[1] p = {}
mutatis mutandis. This one is especially surprising.