https://gcc.gnu.org/g:d5e7633c5a4e91f2c51cc12ca4995e51179ab96e
commit r17-5468-gd5e7633c5a4e91f2c51cc12ca4995e51179ab96e Author: Richard Biener <[email protected]> Date: Wed Oct 21 16:48:33 2026 +0200 tree-optimization/128052 - miscompilation of unaligned loads from covered stores When vn_reference_lookup_3 interprets a load fully covered by a single store of a constant, the bits needed start at bit (offseti - offset2i) % BITS_PER_UNIT of the first encoded byte and thus span that remainder plus maxsizei bits. The code only checked that maxsizei bits were encoded, and with a 64 byte encoding limit an unaligned load of up to 512 bits (for example a 510-bit _BitInt bit-field at bit offset 3) could need a 65th byte that was not encoded, which then was zero-filled before the shift, dropping bits from the stored value. The following accounts for the bit remainder in the check, makes room for encoding one extra byte and caps the access size at bufsize * BITS_PER_UNIT to retain the previous limit. PR tree-optimization/128052 * tree-ssa-sccvn.cc (vn_reference_lookup_3): Guard single-store lookup with maxsizei <= bufsize * BITS_PER_UNIT. Increase buffer size to bufsize + 2. Account for the bit remainder (offseti - offset2i) % BITS_PER_UNIT when verifying that sufficient bits were encoded. * gcc.dg/torture/bitint-115.c: New test. Diff: --- gcc/testsuite/gcc.dg/torture/bitint-115.c | 95 +++++++++++++++++++++++++++++++ gcc/tree-ssa-sccvn.cc | 12 +++- 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/gcc/testsuite/gcc.dg/torture/bitint-115.c b/gcc/testsuite/gcc.dg/torture/bitint-115.c new file mode 100644 index 000000000000..e675fbb96415 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/bitint-115.c @@ -0,0 +1,95 @@ +/* { dg-do run { target { bitint && le } } } */ + +#if __BITINT_MAXWIDTH__ >= 1024 +union U1 { + _BitInt(1024) big; + struct __attribute__((packed)) { unsigned a : 3; unsigned _BitInt(510) f : 510; } s; +}; + +union U2 { + _BitInt(1024) big; + struct __attribute__((packed)) { unsigned a : 1; unsigned _BitInt(512) f : 512; } s; +}; + +union U3 { + _BitInt(1024) big; + struct __attribute__((packed)) { unsigned a : 5; unsigned _BitInt(508) f : 508; } s; +}; + +union U4 { + _BitInt(1024) big; + struct __attribute__((packed)) { unsigned a : 7; unsigned _BitInt(506) f : 506; } s; +}; + +__attribute__((noinline)) void +test1 (void) +{ + union U1 u; + u.big = (_BitInt(1024))1 << 512; + if (u.s.f >> 509 != 1) + __builtin_abort (); + + u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | ((_BitInt(1024))1 << 3); + if ((u.s.f & 1) != 1) + __builtin_abort (); + if ((u.s.f >> 508) != 3) + __builtin_abort (); +} + +__attribute__((noinline)) void +test2 (void) +{ + union U2 u; + u.big = (_BitInt(1024))1 << 512; + if (u.s.f >> 511 != 1) + __builtin_abort (); + + u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | ((_BitInt(1024))1 << 1); + if ((u.s.f & 1) != 1) + __builtin_abort (); + if ((u.s.f >> 510) != 3) + __builtin_abort (); +} + +__attribute__((noinline)) void +test3 (void) +{ + union U3 u; + u.big = (_BitInt(1024))1 << 512; + if (u.s.f >> 507 != 1) + __builtin_abort (); + + u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | ((_BitInt(1024))1 << 5); + if ((u.s.f & 1) != 1) + __builtin_abort (); + if ((u.s.f >> 506) != 3) + __builtin_abort (); +} + +__attribute__((noinline)) void +test4 (void) +{ + union U4 u; + u.big = (_BitInt(1024))1 << 512; + if (u.s.f >> 505 != 1) + __builtin_abort (); + + u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | ((_BitInt(1024))1 << 7); + if ((u.s.f & 1) != 1) + __builtin_abort (); + if ((u.s.f >> 504) != 3) + __builtin_abort (); +} +#endif + +int +main (void) +{ +#if __BITINT_MAXWIDTH__ >= 1024 + test1 (); + test2 (); + test3 (); + test4 (); +#endif + return 0; +} diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc index ac9cb6e7e4c1..6d7f0076266b 100644 --- a/gcc/tree-ssa-sccvn.cc +++ b/gcc/tree-ssa-sccvn.cc @@ -3267,10 +3267,14 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *data_, && size2.is_constant (&size2i)) { if (data->partial_defs.is_empty () + && (maxsizei + <= (HOST_WIDE_INT) vn_walk_cb_data::bufsize * BITS_PER_UNIT) && known_subrange_p (offseti, maxsizei, offset2, size2)) { - /* We support up to 512-bit values (for V8DFmode). */ - unsigned char buffer[65]; + /* We support up to 512-bit values (for V8DFmode). An + unaligned access can need one more byte from the + encoding, plus one for the shift carry-over. */ + unsigned char buffer[vn_walk_cb_data::bufsize + 2]; int len; tree rhs = gimple_assign_rhs1 (def_stmt); @@ -3279,7 +3283,9 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *data_, len = native_encode_expr (rhs, buffer, sizeof (buffer) - 1, (offseti - offset2i) / BITS_PER_UNIT); - if (len > 0 && len * BITS_PER_UNIT >= maxsizei) + if (len > 0 + && (len * BITS_PER_UNIT + >= ((offseti - offset2i) % BITS_PER_UNIT) + maxsizei)) { tree type = vr->type; unsigned char *buf = buffer;
