@@ -183,16 +183,16 @@ class _RedisClient {
183183 size_t _sum (T x, const Ts&...xs) {
184184 return x + _sum (xs...);
185185 }
186- // _xbuf is a GCC zero-length flex-array; the actual storage lives in the
187- // derived class __RedisClient<BUF_SIZE>::_buf. Under -D_FORTIFY_SOURCE>=1,
188- // __builtin_object_size(_xbuf, *) yields 0, causing libc fortified wrappers
189- // (__snprintf_chk / __memcpy_chk / __memmove_chk) to falsely report
190- // buffer overflow. Launder the returned pointer through an inline-asm
191- // "+r" constraint so the optimizer/FORTIFY can no longer associate it
192- // with the original [0]-sized array, eliminating both the compile-time
193- // -Wstringop-overflow warning and the runtime buffer-overflow abort.
194- char * ibuf () { char * p = _xbuf; __asm__ ( " " : " +r " (p)); return p ; }
195- char * obuf () { char * p = _xbuf + _bufsize; __asm__ ( " " : " +r " (p)); return p ; }
186+ // Pointer barrier: hide the source of `p` from the optimizer / FORTIFY,
187+ // so that __builtin_object_size() cannot trace the returned pointer back
188+ // to its original declaration. The empty inline-asm with a "+r" constraint
189+ // forces the value to materialise in a register and severs the def-use
190+ // chain. This is the only construct on GCC 15 that reliably defeats
191+ // __builtin_object_size; a (char*)(uintptr_t) cast is NOT enough -- GCC
192+ // 15's IPA-VRP sees through integer round-trips.
193+ static char * _launder ( char * p) { __asm__ ( " " : " +r " (p)); return p; }
194+ char * ibuf () { return _xbuf ; }
195+ char * obuf () { return _xbuf + _bufsize; }
196196 std::string_view __getstring (size_t length);
197197 std::string_view __getline ();
198198
@@ -226,7 +226,17 @@ class _RedisClient {
226226 }
227227 template <typename ...Ts>
228228 std::string_view _snprintf (const char * fmt, const Ts&...args) {
229- auto buf = obuf () + _o;
229+ // `_xbuf` is a GCC zero-length flex-array; the real storage lives in
230+ // the derived class __RedisClient<BUF_SIZE>::_buf. Under
231+ // -D_FORTIFY_SOURCE>=1, __builtin_object_size(_xbuf, *) evaluates to
232+ // 0, so without _launder() the fortified __snprintf_chk wrapper
233+ // would falsely diagnose buffer overflow at both compile time
234+ // (-Wstringop-overflow / -Wformat-truncation) and run time
235+ // (__chk_fail abort). Note: __refill()'s memmove and put()'s memcpy
236+ // also touch the same _xbuf storage; GCC 15 currently does not flag
237+ // them, so we leave them alone -- if a future toolchain regresses,
238+ // wrap those call sites with _launder() too.
239+ auto buf = _launder (obuf () + _o);
230240 auto size = _bufsize - _o;
231241 int ret = snprintf (buf, size - _o, fmt, args...);
232242 assert (0 <= ret && (uint32_t )ret <= size);
0 commit comments