From 2d9837c1fbf4658f199eae02681f08f040dfe3a8 Mon Sep 17 00:00:00 2001 From: "Gabriel F. T. Gomes" Date: Wed, 19 Dec 2018 18:01:14 -0200 Subject: Set behavior of sprintf-like functions with overlapping source and destination According to ISO C99, passing the same buffer as source and destination to sprintf, snprintf, vsprintf, or vsnprintf has undefined behavior. Until the commit commit 4e2f43f842ef5e253cc23383645adbaa03cedb86 Author: Zack Weinberg Date: Wed Mar 7 14:32:03 2018 -0500 Use PRINTF_FORTIFY instead of _IO_FLAGS2_FORTIFY (bug 11319) a call to sprintf or vsprintf with overlapping buffers, for instance vsprintf (buf, "%sTEXT", buf), would append `TEXT' into buf, while a call to snprintf or vsnprintf would override the contents of buf. After the aforementioned commit, the behavior of sprintf and vsprintf changed (so that they also override the contents of buf). This patch reverts this behavioral change, because it will likely break applications that rely on the previous behavior, even though it is undefined by ISO C. As noted by Szabolcs Nagy, this is used in SPEC2017 507.cactuBSSN_r/src/PUGH/PughUtils.c: sprintf(mess," Size:"); for (i=0;iGFExtras[dim]->nsize[i]); } More important to notice is the fact that the overwriting of the destination buffer is not the only behavior affected by the refactoring. Before the refactoring, sprintf and vsprintf would use _IO_str_jumps, whereas __sprintf_chk and __vsprintf_chk would use _IO_str_chk_jumps. After the refactoring, all use _IO_str_chk_jumps, which would make sprintf and vsprintf report buffer overflows and terminate the program. This patch also reverts this behavior, by installing the appropriate jump table for each *sprintf functions. Apart from reverting the changes, this patch adds a test case that has the old behavior hardcoded, so that regressions are noticed if something else unintentionally changes the behavior. Tested for powerpc64le. --- debug/sprintf_chk.c | 4 ++++ debug/vsprintf_chk.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'debug') diff --git a/debug/sprintf_chk.c b/debug/sprintf_chk.c index 54d4a64255..7ef01653ee 100644 --- a/debug/sprintf_chk.c +++ b/debug/sprintf_chk.c @@ -29,6 +29,10 @@ ___sprintf_chk (char *s, int flag, size_t slen, const char *format, ...) va_list ap; int ret; + /* Regardless of the value of flag, let __vsprintf_internal know that + this is a call from *printf_chk. */ + mode |= PRINTF_CHK; + if (slen == 0) __chk_fail (); diff --git a/debug/vsprintf_chk.c b/debug/vsprintf_chk.c index 7ef8cf38bc..c93ca4efb1 100644 --- a/debug/vsprintf_chk.c +++ b/debug/vsprintf_chk.c @@ -25,6 +25,10 @@ ___vsprintf_chk (char *s, int flag, size_t slen, const char *format, can only come from read-only format strings. */ unsigned int mode = (flag > 0) ? PRINTF_FORTIFY : 0; + /* Regardless of the value of flag, let __vsprintf_internal know that + this is a call from *printf_chk. */ + mode |= PRINTF_CHK; + if (slen == 0) __chk_fail (); -- cgit v1.2.3