Skip to content

Commit 608c42a

Browse files
committed
Make flex array pointer launder more clear
1 parent 8b1a9c0 commit 608c42a

1 file changed

Lines changed: 21 additions & 11 deletions

File tree

ecosystem/redis.h

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)