From: Martin Sebor Date: Fri, 22 Feb 2019 17:38:11 +0000 (-0700) Subject: PR tree-optimization/88993 - GCC 9 -Wformat-overflow=2 should reflect real libc limits X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=eb319c505d0bc3497170b5cdaeaf774a67594ae3;p=gcc.git PR tree-optimization/88993 - GCC 9 -Wformat-overflow=2 should reflect real libc limits PR tree-optimization/88993 - GCC 9 -Wformat-overflow=2 should reflect real libc limits PR tree-optimization/88835 - overly aggressive -Werror=format-overflow for printf gcc/ChangeLog: PR tree-optimization/88993 PR tree-optimization/88853 * gimple-ssa-sprintf.c (sprintf_dom_walker::call_info::is_file_func): New helper. (sprintf_dom_walker::call_info::is_string_func): New helper. (format_directive): Only issue "may exceed" 4095/INT_MAX warnings for formatted string functions. (sprintf_dom_walker::handle_gimple_call): Fix a typo in a comment. gcc/testsuite/ChangeLog: PR tree-optimization/88993 PR tree-optimization/88853 * gcc.dg/tree-ssa/builtin-fprintf-warn-2.c: New test. * gcc.dg/tree-ssa/builtin-printf-warn-2.c: New test. * gcc.dg/tree-ssa/builtin-snprintf-warn-3.c: Adjust. * gcc.dg/tree-ssa/builtin-sprintf-warn-18.c: Same. From-SVN: r269125 --- diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 4fe666ff0de..e40e0db427c 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -945,6 +945,29 @@ struct sprintf_dom_walker::call_info { return bounded ? OPT_Wformat_truncation_ : OPT_Wformat_overflow_; } + + /* Return true for calls to file formatted functions. */ + bool is_file_func () const + { + return (fncode == BUILT_IN_FPRINTF + || fncode == BUILT_IN_FPRINTF_CHK + || fncode == BUILT_IN_FPRINTF_UNLOCKED + || fncode == BUILT_IN_VFPRINTF + || fncode == BUILT_IN_VFPRINTF_CHK); + } + + /* Return true for calls to string formatted functions. */ + bool is_string_func () const + { + return (fncode == BUILT_IN_SPRINTF + || fncode == BUILT_IN_SPRINTF_CHK + || fncode == BUILT_IN_SNPRINTF + || fncode == BUILT_IN_SNPRINTF_CHK + || fncode == BUILT_IN_VSPRINTF + || fncode == BUILT_IN_VSPRINTF_CHK + || fncode == BUILT_IN_VSNPRINTF + || fncode == BUILT_IN_VSNPRINTF_CHK); + } }; /* Return the result of formatting a no-op directive (such as '%n'). */ @@ -2841,6 +2864,8 @@ format_directive (const sprintf_dom_walker::call_info &info, if (!warned /* Only warn at level 2. */ && warn_level > 1 + /* Only warn for string functions. */ + && info.is_string_func () && (!minunder4k || (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX))) { @@ -2849,7 +2874,9 @@ format_directive (const sprintf_dom_walker::call_info &info, of C11. Warn on this only at level 2 but remember this and prevent folding the return value when done. This allows for the possibility of the actual libc call failing due to ENOMEM - (like Glibc does under some conditions). */ + (like Glibc does with very large precision or width). + Issue the "may exceed" warning only for string functions and + not for fprintf or printf. */ if (fmtres.range.min == fmtres.range.max) warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), @@ -2857,14 +2884,18 @@ format_directive (const sprintf_dom_walker::call_info &info, "minimum required size of 4095", dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg), fmtres.range.min); - else + else if (!minunder4k) + warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), + "%<%.*s%> directive output between %wu and %wu " + "bytes exceeds minimum required size of 4095", + dirlen, + target_to_host (hostdir, sizeof hostdir, dir.beg), + fmtres.range.min, fmtres.range.max); + else if (!info.retval_used () && info.is_string_func ()) warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), - minunder4k - ? G_("%<%.*s%> directive output between %wu and %wu " - "bytes may exceed minimum required size of " - "4095") - : G_("%<%.*s%> directive output between %wu and %wu " - "bytes exceeds minimum required size of 4095"), + "%<%.*s%> directive output between %wu and %wu " + "bytes may exceed minimum required size of " + "4095", dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg), fmtres.range.min, fmtres.range.max); @@ -2887,24 +2918,48 @@ format_directive (const sprintf_dom_walker::call_info &info, && maxximax && fmtres.range.max < HOST_WIDE_INT_MAX))) { - /* The directive output causes the total length of output - to exceed INT_MAX bytes. */ - - if (fmtres.range.min == fmtres.range.max) - warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), - "%<%.*s%> directive output of %wu bytes causes " - "result to exceed %", dirlen, - target_to_host (hostdir, sizeof hostdir, dir.beg), - fmtres.range.min); - else + if (fmtres.range.min > target_int_max ()) + { + /* The directive output exceeds INT_MAX bytes. */ + if (fmtres.range.min == fmtres.range.max) + warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), + "%<%.*s%> directive output of %wu bytes exceeds " + "%", dirlen, + target_to_host (hostdir, sizeof hostdir, dir.beg), + fmtres.range.min); + else + warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), + "%<%.*s%> directive output between %wu and " + "%wu bytes exceeds %", dirlen, + target_to_host (hostdir, sizeof hostdir, dir.beg), + fmtres.range.min, fmtres.range.max); + } + else if (res->range.min > target_int_max ()) + { + /* The directive output is under INT_MAX but causes the result + to exceed INT_MAX bytes. */ + if (fmtres.range.min == fmtres.range.max) + warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), + "%<%.*s%> directive output of %wu bytes causes " + "result to exceed %", dirlen, + target_to_host (hostdir, sizeof hostdir, dir.beg), + fmtres.range.min); + else + warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), + "%<%.*s%> directive output between %wu and " + "%wu bytes causes result to exceed %", + dirlen, + target_to_host (hostdir, sizeof hostdir, dir.beg), + fmtres.range.min, fmtres.range.max); + } + else if ((!info.retval_used () || !info.bounded) + && (info.is_string_func ())) + /* Warn for calls to string functions that either aren't bounded + (sprintf) or whose return value isn't used. */ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), - fmtres.range.min > target_int_max () - ? G_("%<%.*s%> directive output between %wu and " - "%wu bytes causes result to exceed " - "%") - : G_("%<%.*s%> directive output between %wu and " - "%wu bytes may cause result to exceed " - "%"), dirlen, + "%<%.*s%> directive output between %wu and " + "%wu bytes may cause result to exceed " + "%", dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg), fmtres.range.min, fmtres.range.max); } @@ -2944,37 +2999,65 @@ format_directive (const sprintf_dom_walker::call_info &info, res->warned |= warned; - if (!dir.beg[0] && res->warned && info.objsize < HOST_WIDE_INT_MAX) + if (!dir.beg[0] && res->warned) { - /* If a warning has been issued for buffer overflow or truncation - (but not otherwise) help the user figure out how big a buffer - they need. */ - location_t callloc = gimple_location (info.callstmt); unsigned HOST_WIDE_INT min = res->range.min; unsigned HOST_WIDE_INT max = res->range.max; - if (min == max) - inform (callloc, - (min == 1 - ? G_("%qE output %wu byte into a destination of size %wu") - : G_("%qE output %wu bytes into a destination of size %wu")), - info.func, min, info.objsize); - else if (max < HOST_WIDE_INT_MAX) - inform (callloc, - "%qE output between %wu and %wu bytes into " - "a destination of size %wu", - info.func, min, max, info.objsize); - else if (min < res->range.likely && res->range.likely < max) - inform (callloc, - "%qE output %wu or more bytes (assuming %wu) into " - "a destination of size %wu", - info.func, min, res->range.likely, info.objsize); - else - inform (callloc, - "%qE output %wu or more bytes into a destination of size %wu", - info.func, min, info.objsize); + if (info.objsize < HOST_WIDE_INT_MAX) + { + /* If a warning has been issued for buffer overflow or truncation + help the user figure out how big a buffer they need. */ + + if (min == max) + inform (callloc, + (min == 1 + ? G_("%qE output %wu byte into a destination of size %wu") + : G_("%qE output %wu bytes into a destination of size " + "%wu")), + info.func, min, info.objsize); + else if (max < HOST_WIDE_INT_MAX) + inform (callloc, + "%qE output between %wu and %wu bytes into " + "a destination of size %wu", + info.func, min, max, info.objsize); + else if (min < res->range.likely && res->range.likely < max) + inform (callloc, + "%qE output %wu or more bytes (assuming %wu) into " + "a destination of size %wu", + info.func, min, res->range.likely, info.objsize); + else + inform (callloc, + "%qE output %wu or more bytes into a destination of size " + "%wu", + info.func, min, info.objsize); + } + else if (!info.is_string_func ()) + { + /* If the warning is for a file function function like fprintf + of printf with no destination size just print the computed + result. */ + if (min == max) + inform (callloc, + (min == 1 + ? G_("%qE output %wu byte") + : G_("%qE output %wu bytes")), + info.func, min); + else if (max < HOST_WIDE_INT_MAX) + inform (callloc, + "%qE output between %wu and %wu bytes", + info.func, min, max); + else if (min < res->range.likely && res->range.likely < max) + inform (callloc, + "%qE output %wu or more bytes (assuming %wu)", + info.func, min, res->range.likely); + else + inform (callloc, + "%qE output %wu or more bytes", + info.func, min); + } } if (dump_file && *dir.beg) @@ -3501,14 +3584,14 @@ sprintf_dom_walker::compute_format_length (call_info &info, } /* Return the size of the object referenced by the expression DEST if - available, or -1 otherwise. */ + available, or the maximum possible size otherwise. */ static unsigned HOST_WIDE_INT get_destination_size (tree dest) { - /* When there is no destination return -1. */ + /* When there is no destination return the maximum. */ if (!dest) - return HOST_WIDE_INT_M1U; + return HOST_WIDE_INT_MAX; /* Initialize object size info before trying to compute it. */ init_object_sizes (); @@ -3523,7 +3606,7 @@ get_destination_size (tree dest) if (compute_builtin_object_size (dest, ost, &size)) return size; - return HOST_WIDE_INT_M1U; + return HOST_WIDE_INT_MAX; } /* Return true if the call described by INFO with result RES safe to @@ -3844,7 +3927,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) case BUILT_IN_PRINTF_CHK: // Signature: - // __builtin_printf_chk (it, format, ...) + // __builtin_printf_chk (ost, format, ...) idx_format = 1; info.argidx = 2; idx_dstptr = -1; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c new file mode 100644 index 00000000000..4422e4c86f6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c @@ -0,0 +1,324 @@ +/* PR middle-end/88993 - GCC 9 -Wformat-overflow=2 should reflect real + libc limits + Verify that -Wformat-overflow=2 "may exceed" warnings are not issued + for printf family of functions. + { dg-do compile } + { dg-options "-O -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } + { dg-require-effective-target int32plus } */ + +#define INT_MAX __INT_MAX__ + +typedef __SIZE_TYPE__ size_t; + +#if !__cplusplus +typedef __WCHAR_TYPE__ wchar_t; +#endif + +typedef struct FILE FILE; + +FILE *fp; + +#define T(...) __builtin_fprintf (__VA_ARGS__) + +/* Exercise the "%c" directive with constant arguments. */ + +void test_fprintf_c_const (int width) +{ + /* Verify that a warning is only issued when the output is definitely + exceeded but not when exceeding it is possible but not inevitable. + Also verify that a note is printed with amount of output produced + by the call (the result - 1). */ + T (fp, "%2147483647c", '1'); + T (fp, "X%2147483647c", '2'); /* { dg-warning ".%2147483647c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + /* { dg-message ".__builtin_fprintf. output 2147483649 bytes" "note" { target *-*-* } .-1 } */ + T (fp, "%2147483647cY", '3'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "%2147483648c", '1'); /* { dg-warning ".%2147483648c. directive output of 2147483648 bytes exceeds .INT_MAX." } */ + T (fp, "X%2147483649c", '2'); /* { dg-warning ".%2147483649c. directive output of 2147483649 bytes exceeds .INT_MAX." } */ + T (fp, "%2147483650cY", '3'); /* { dg-warning ".%2147483650c. directive output of 2147483650 bytes exceeds .INT_MAX." } */ + + T (fp, "%*c", INT_MAX, '1'); + T (fp, "X%*c", INT_MAX, '1'); /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T (fp, "%*cY", INT_MAX, '1'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "X%*c", INT_MAX - 1, '1'); + T (fp, "%*cY", INT_MAX - 1, '1'); + + T (fp, "%*cY", INT_MAX, '1'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T (fp, "X%*c", INT_MAX, '1'); /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ +} + +/* Exercise the "%c" directive with arguments in a known range. */ + +void test_fprintf_c_range (int width) +{ + /* Verify that an known upper bound doesn't trigger a warning. */ + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*c", width, '1'); + T (fp, "X%*c", width, '1'); + T (fp, "%*cY", width, '1'); + + T (fp, "%*c", width, '1'); + T (fp, "X%*c", width, '1'); + T (fp, "%*cY", width, '1'); + + T (fp, "%*c%*c", width, '1', width, '2'); + T (fp, "X%*cY%*cZ", width, '1', width, '2'); + + /* Verify that a lower bound in excess of 4095 doesn't trigger + a warning. */ + if (width < 4096) + width = 4096; + + T (fp, "%*c", width, '1'); + T (fp, "X%*c", width, '1'); + T (fp, "%*cY", width, '1'); + + /* Verify that a large lower bound triggers a warning when the total + result of the function definitely exceeds INT_MAX. */ + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*c", width, '1'); + T (fp, "X%*c", width, '2'); + T (fp, "%*cY", width, '3'); + T (fp, "X%*cY", width, '4'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + /* { dg-message ".__builtin_fprintf. output 2147483649 bytes" "note" { target *-*-* } .-1 } */ +} + + +/* Exercise the "%s" directive. */ + +void test_fprintf_s_const (int width, const char *s) +{ + T (fp, "%2147483647s", s); + T (fp, "%2147483647s", "1"); + + T (fp, "%2147483647.2147483647s", s); + T (fp, "%2147483647.2147483647s", "12"); + + T (fp, "X%2147483647s", s); /* { dg-warning ".%2147483647s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T (fp, "%2147483647sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "X%2147483647.1s", s); /* { dg-warning ".%2147483647\\\.1s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (fp, "%2147483647.2sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "X%1.2147483647s", s); + T (fp, "%2.2147483647sY", s); + + T (fp, "X%1.2147483647s", "123"); + T (fp, "%2.2147483647sY", "1234"); + + T (fp, "%2147483648s", s); /* { dg-warning "%2147483648s. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T (fp, "X%2147483649s", s); /* { dg-warning "%2147483649s. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T (fp, "%2147483650sY", s); /* { dg-warning ".%2147483650s. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + + T (fp, "%*s", INT_MAX, s); + T (fp, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T (fp, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "X%*s", INT_MAX - 1, s); + T (fp, "%*sY", INT_MAX - 1, s); + + T (fp, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T (fp, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + + T (fp, "%*s%*s", width, s, width, s); + T (fp, "X%*sY%*sZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + T (fp, "X%*sY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + +/* Exercise the "%ls" directive. */ + +void test_fprintf_ls_const (int width, const wchar_t *s) +{ + T (fp, "%2147483647ls", s); + T (fp, "X%2147483647ls", s); /* { dg-warning ".%2147483647ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T (fp, "%2147483647lsY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "%2147483648ls", s); /* { dg-warning "%2147483648ls. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T (fp, "X%2147483649ls", s); /* { dg-warning "%2147483649ls. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T (fp, "%2147483650lsY", s); /* { dg-warning ".%2147483650ls. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + + T (fp, "%*ls", INT_MAX, s); + T (fp, "X%*ls", INT_MAX, s); /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T (fp, "%*lsY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "X%*ls", INT_MAX - 1, s); + T (fp, "%*lsY", INT_MAX - 1, s); + + T (fp, "%*lsY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T (fp, "X%*ls", INT_MAX, s); /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*ls", width, s); + T (fp, "X%*ls", width, s); + T (fp, "%*lsY", width, s); + + T (fp, "%*ls", width, s); + T (fp, "X%*ls", width, s); + T (fp, "%*lsY", width, s); + + T (fp, "%*ls%*ls", width, s, width, s); + T (fp, "X%*lsY%*lsZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T (fp, "%*ls", width, s); + T (fp, "X%*ls", width, s); + T (fp, "%*lsY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*ls", width, s); + T (fp, "X%*ls", width, s); + T (fp, "%*lsY", width, s); + T (fp, "X%*lsY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + + +/* Also exercise fprintf_chk. */ + +#undef T +#define T(...) __builtin___fprintf_chk (__VA_ARGS__) + +void test_fprintf_chk_s_const (int width) +{ + const char *s = "0123456789"; + + T (fp, 0, "%2147483647s", s); + T (fp, 0, "X%2147483647s", s); /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (fp, 0, "%2147483647sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, 0, "%2147483648s", s); /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */ + T (fp, 0, "X%2147483649s", s); /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */ + T (fp, 0, "%2147483650sY", s); /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */ + + T (fp, 0, "%*s", INT_MAX, s); + T (fp, 0, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (fp, 0, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, 0, "X%*s", INT_MAX - 1, s); + T (fp, 0, "%*sY", INT_MAX - 1, s); + + T (fp, 0, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T (fp, 0, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, 0, "%*s", width, s); + T (fp, 0, "X%*s", width, s); + T (fp, 0, "%*sY", width, s); + + T (fp, 0, "%*s", width, s); + T (fp, 0, "X%*s", width, s); + T (fp, 0, "%*sY", width, s); + + T (fp, 0, "%*s%*s", width, s, width, s); + T (fp, 0, "X%*sY%*sZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T (fp, 0, "%*s", width, s); + T (fp, 0, "X%*s", width, s); + T (fp, 0, "%*sY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, 0, "%*s", width, s); + T (fp, 0, "X%*s", width, s); + T (fp, 0, "%*sY", width, s); + T (fp, 0, "X%*sY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + + +/* And finally exercise fprintf_unlocked. */ + +#undef T +#define T(...) __builtin_fprintf_unlocked (__VA_ARGS__) + +void test_fprintf_unlocked_s_const (int width) +{ + const char *s = "0123456789"; + + T (fp, "%2147483647s", s); + T (fp, "X%2147483647s", s); /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (fp, "%2147483647sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "%2147483648s", s); /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */ + T (fp, "X%2147483649s", s); /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */ + T (fp, "%2147483650sY", s); /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */ + + T (fp, "%*s", INT_MAX, s); + T (fp, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (fp, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (fp, "X%*s", INT_MAX - 1, s); + T (fp, "%*sY", INT_MAX - 1, s); + + T (fp, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T (fp, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + + T (fp, "%*s%*s", width, s, width, s); + T (fp, "X%*sY%*sZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T (fp, "%*s", width, s); + T (fp, "X%*s", width, s); + T (fp, "%*sY", width, s); + T (fp, "X%*sY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c new file mode 100644 index 00000000000..31d16bc9969 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c @@ -0,0 +1,293 @@ +/* PR middle-end/88993 - GCC 9 -Wformat-overflow=2 should reflect real + libc limits + Verify that -Wformat-overflow=2 "may exceed" warnings are not issued + for printf family of functions. + { dg-do compile } + { dg-options "-O -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } + { dg-require-effective-target int32plus } */ + + +#define INT_MAX __INT_MAX__ + +typedef __SIZE_TYPE__ size_t; + +#if !__cplusplus +typedef __WCHAR_TYPE__ wchar_t; +#endif + +#define T(...) __builtin_printf (__VA_ARGS__) + +/* Exercise the "%c" directive with constant arguments. */ + +void test_printf_c_const (int width) +{ + /* Verify that a warning is only issued when the output is definitely + exceeded but not when exceeding it is possible but not inevitable. */ + T ("%2147483647c", '1'); + T ("X%2147483647c", '2'); /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T ("%2147483647cY", '3'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("%2147483648c", '1'); /* { dg-warning ".%2147483648c. directive output of 2147483648 bytes exceeds .INT_MAX." } */ + T ("X%2147483649c", '2'); /* { dg-warning ".%2147483649c. directive output of 2147483649 bytes exceeds .INT_MAX." } */ + T ("%2147483650cY", '3'); /* { dg-warning ".%2147483650c. directive output of 2147483650 bytes exceeds .INT_MAX." } */ + + T ("%*c", INT_MAX, '1'); + T ("X%*c", INT_MAX, '1'); /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T ("%*cY", INT_MAX, '1'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("X%*c", INT_MAX - 1, '1'); + T ("%*cY", INT_MAX - 1, '1'); + + T ("%*cY", INT_MAX, '1'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T ("X%*c", INT_MAX, '1'); /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*c", width, '1'); + T ("X%*c", width, '1'); + T ("%*cY", width, '1'); + + T ("%*c", width, '1'); + T ("X%*c", width, '1'); + T ("%*cY", width, '1'); + + T ("%*c%*c", width, '1', width, '2'); + T ("X%*cY%*cZ", width, '1', width, '2'); + + if (width < 4096) + width = 4096; + + T ("%*c", width, '1'); + T ("X%*c", width, '1'); + T ("%*cY", width, '1'); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*c", width, '1'); + T ("X%*c", width, '2'); + T ("%*cY", width, '3'); + T ("X%*cY", width, '4'); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + + +/* Exercise the "%s" directive with constant arguments. */ + +void test_printf_s_const (int width, const char *s) +{ + T ("%2147483647s", s); + T ("X%2147483647s", s); /* { dg-warning ".%2147483647s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T ("%2147483647sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("%2147483648s", s); /* { dg-warning "%2147483648s. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T ("X%2147483649s", s); /* { dg-warning "%2147483649s. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T ("%2147483650sY", s); /* { dg-warning ".%2147483650s. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + + T ("%*s", INT_MAX, s); + T ("X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T ("%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("X%*s", INT_MAX - 1, s); + T ("%*sY", INT_MAX - 1, s); + + T ("%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T ("X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + + T ("%*s%*s", width, s, width, s); + T ("X%*sY%*sZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + T ("X%*sY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + +/* Exercise the "%ls" directive with constant arguments. */ + +void test_printf_ls_const (int width, const wchar_t *s) +{ + T ("%2147483647ls", s); + T ("X%2147483647ls", s); /* { dg-warning ".%2147483647ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T ("%2147483647lsY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("%2147483648ls", s); /* { dg-warning "%2147483648ls. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T ("X%2147483649ls", s); /* { dg-warning "%2147483649ls. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + T ("%2147483650lsY", s); /* { dg-warning ".%2147483650ls. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */ + + T ("%*ls", INT_MAX, s); + T ("X%*ls", INT_MAX, s); /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + T ("%*lsY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("X%*ls", INT_MAX - 1, s); + T ("%*lsY", INT_MAX - 1, s); + + T ("%*lsY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T ("X%*ls", INT_MAX, s); /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*ls", width, s); + T ("X%*ls", width, s); + T ("%*lsY", width, s); + + T ("%*ls", width, s); + T ("X%*ls", width, s); + T ("%*lsY", width, s); + + T ("%*ls%*ls", width, s, width, s); + T ("X%*lsY%*lsZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T ("%*ls", width, s); + T ("X%*ls", width, s); + T ("%*lsY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*ls", width, s); + T ("X%*ls", width, s); + T ("%*lsY", width, s); + T ("X%*lsY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + + +/* Also exercise printf_chk. */ + +#undef T +#define T(...) __builtin___printf_chk (__VA_ARGS__) + +void test_printf_chk_s_const (int width) +{ + const char *s = "0123456789"; + + T (0, "%2147483647s", s); + T (0, "X%2147483647s", s); /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (0, "%2147483647sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (0, "%2147483648s", s); /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */ + T (0, "X%2147483649s", s); /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */ + T (0, "%2147483650sY", s); /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */ + + T (0, "%*s", INT_MAX, s); + T (0, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T (0, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T (0, "X%*s", INT_MAX - 1, s); + T (0, "%*sY", INT_MAX - 1, s); + + T (0, "%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T (0, "X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T (0, "%*s", width, s); + T (0, "X%*s", width, s); + T (0, "%*sY", width, s); + + T (0, "%*s", width, s); + T (0, "X%*s", width, s); + T (0, "%*sY", width, s); + + T (0, "%*s%*s", width, s, width, s); + T (0, "X%*sY%*sZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T (0, "%*s", width, s); + T (0, "X%*s", width, s); + T (0, "%*sY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T (0, "%*s", width, s); + T (0, "X%*s", width, s); + T (0, "%*sY", width, s); + T (0, "X%*sY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} + + +/* And finally exercise printf_unlocked. */ + +#undef T +#define T(...) __builtin_printf_unlocked (__VA_ARGS__) + +void test_printf_unlocked_s_const (int width) +{ + const char *s = "0123456789"; + + T ("%2147483647s", s); + T ("X%2147483647s", s); /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T ("%2147483647sY", s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("%2147483648s", s); /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */ + T ("X%2147483649s", s); /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */ + T ("%2147483650sY", s); /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */ + + T ("%*s", INT_MAX, s); + T ("X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + T ("%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + + T ("X%*s", INT_MAX - 1, s); + T ("%*sY", INT_MAX - 1, s); + + T ("%*sY", INT_MAX, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ + T ("X%*s", INT_MAX, s); /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */ + + if (width > INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + + T ("%*s%*s", width, s, width, s); + T ("X%*sY%*sZ", width, s, width, s); + + if (width < 4096) + width = 4096; + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + + if (width < INT_MAX - 1) + width = INT_MAX - 1; + + T ("%*s", width, s); + T ("X%*s", width, s); + T ("%*sY", width, s); + T ("X%*sY", width, s); /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */ +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c index 32c73d0edae..3f2b7562cad 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c @@ -1,6 +1,6 @@ /* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning { dg-do compile } - { dg-options "-O2 -Wformat -Wformat-truncation=2 -ftrack-macro-expansion=0" } + { dg-options "-O2 -Wformat -Wformat-truncation=2 -ftrack-macro-expansion=0" } { dg-require-effective-target ptr32plus } */ typedef __SIZE_TYPE__ size_t; @@ -166,11 +166,17 @@ void test_string_checked (const char *s, const struct Arrays *ar) T (-1, "%s%s", ar->a4k, ar->ax); /* Verify that an array that fits a string longer than 4095 bytes - does trigger a warning. */ - T (-1, "%-s", ar->a4kp1); /* { dg-warning "directive output between 0 and 4096 bytes may exceed minimum required size of 4095" } */ - - /* Also verify that a %s directive with width greater than 4095 - triggers a warning even if the argument is not longer than 4k. */ + does not trigger a warning. (No known implementation has trouble + with this). */ + T (-1, "%s", ar->a4kp1); + + /* Verify that a %s directive with width greater than 4095 does + trigger a warning even if the string argument is not longer + than 4k. Glibc only has trouble with directives whose width + or precision exceeds 64K or so: + https://bugzilla.redhat.com/show_bug.cgi?id=441945 * + but hardcoding that as the limit and assuming no other + implementation has a lower one seems unwise. */ T (-1, "%*s", 4096, ar->a4k); /* { dg-warning "directive output of 4096 bytes exceeds minimum required size of 4095" } */ /* Verify that precision constrains the putput and suppresses the 4k @@ -190,5 +196,7 @@ void test_string_checked (const char *s, const struct Arrays *ar) T (-1, "%s %s %s", ar->a4k, ar->a4k, ar->a4k); T (-1, "%s %s %s", ar->ax, ar->ax, ar->ax); - T (-1, "%-s", ar->amax); /* { dg-warning "directive output between 0 and \[0-9\]+ bytes may exceed minimum required size of 4095" } */ + /* Similar to the above, verify there's no warning for an array + just because its size is INT_MAX bytes. */ + T (-1, "%s", ar->amax); } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c index 2203e5263f7..6a18f1776a7 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c @@ -118,9 +118,9 @@ void test_width_and_precision_out_of_range (char *d) /* The range here happens to be a property of the compiler, not one of the target. */ T ("%9223372036854775808i", 0); /* { dg-warning "width out of range" "first" } */ - /* { dg-warning "result to exceed .INT_MAX." "second" { target *-*-* } .-1 } */ + /* { dg-warning "exceeds .INT_MAX." "second" { target *-*-* } .-1 } */ T ("%.9223372036854775808i", 0); /* { dg-warning "precision out of range" "first" } */ - /* { dg-warning "causes result to exceed .INT_MAX." "second" { target *-*-* } .-1 } */ + /* { dg-warning "exceeds .INT_MAX." "second" { target *-*-* } .-1 } */ /* The following is diagnosed by -Wformat (disabled here). */ /* T ("%9223372036854775808$i", 0); */