Commit eb319c50 by Martin Sebor

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
parent cfed471a
...@@ -945,6 +945,29 @@ struct sprintf_dom_walker::call_info ...@@ -945,6 +945,29 @@ struct sprintf_dom_walker::call_info
{ {
return bounded ? OPT_Wformat_truncation_ : OPT_Wformat_overflow_; 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'). */ /* 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, ...@@ -2841,6 +2864,8 @@ format_directive (const sprintf_dom_walker::call_info &info,
if (!warned if (!warned
/* Only warn at level 2. */ /* Only warn at level 2. */
&& warn_level > 1 && warn_level > 1
/* Only warn for string functions. */
&& info.is_string_func ()
&& (!minunder4k && (!minunder4k
|| (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX))) || (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX)))
{ {
...@@ -2849,7 +2874,9 @@ format_directive (const sprintf_dom_walker::call_info &info, ...@@ -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 of C11. Warn on this only at level 2 but remember this and
prevent folding the return value when done. This allows for prevent folding the return value when done. This allows for
the possibility of the actual libc call failing due to ENOMEM 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) if (fmtres.range.min == fmtres.range.max)
warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
...@@ -2857,14 +2884,18 @@ format_directive (const sprintf_dom_walker::call_info &info, ...@@ -2857,14 +2884,18 @@ format_directive (const sprintf_dom_walker::call_info &info,
"minimum required size of 4095", dirlen, "minimum required size of 4095", dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg), target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min); 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 (), warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
minunder4k "%<%.*s%> directive output between %wu and %wu "
? G_("%<%.*s%> directive output between %wu and %wu " "bytes may exceed minimum required size of "
"bytes may exceed minimum required size of " "4095",
"4095")
: G_("%<%.*s%> directive output between %wu and %wu "
"bytes exceeds minimum required size of 4095"),
dirlen, dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg), target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min, fmtres.range.max); fmtres.range.min, fmtres.range.max);
...@@ -2887,24 +2918,48 @@ format_directive (const sprintf_dom_walker::call_info &info, ...@@ -2887,24 +2918,48 @@ format_directive (const sprintf_dom_walker::call_info &info,
&& maxximax && maxximax
&& fmtres.range.max < HOST_WIDE_INT_MAX))) && fmtres.range.max < HOST_WIDE_INT_MAX)))
{ {
/* The directive output causes the total length of output if (fmtres.range.min > target_int_max ())
to exceed INT_MAX bytes. */ {
/* The directive output exceeds INT_MAX bytes. */
if (fmtres.range.min == fmtres.range.max) if (fmtres.range.min == fmtres.range.max)
warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (), warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
"%<%.*s%> directive output of %wu bytes causes " "%<%.*s%> directive output of %wu bytes exceeds "
"result to exceed %<INT_MAX%>", dirlen, "%<INT_MAX%>", dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg), target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min); fmtres.range.min);
else else
warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
"%<%.*s%> directive output between %wu and "
"%wu bytes exceeds %<INT_MAX%>", 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 %<INT_MAX%>", 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 %<INT_MAX%>",
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 (), warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
fmtres.range.min > target_int_max () "%<%.*s%> directive output between %wu and "
? G_("%<%.*s%> directive output between %wu and " "%wu bytes may cause result to exceed "
"%wu bytes causes result to exceed " "%<INT_MAX%>", dirlen,
"%<INT_MAX%>")
: G_("%<%.*s%> directive output between %wu and "
"%wu bytes may cause result to exceed "
"%<INT_MAX%>"), dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg), target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min, fmtres.range.max); fmtres.range.min, fmtres.range.max);
} }
...@@ -2944,37 +2999,65 @@ format_directive (const sprintf_dom_walker::call_info &info, ...@@ -2944,37 +2999,65 @@ format_directive (const sprintf_dom_walker::call_info &info,
res->warned |= warned; 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); location_t callloc = gimple_location (info.callstmt);
unsigned HOST_WIDE_INT min = res->range.min; unsigned HOST_WIDE_INT min = res->range.min;
unsigned HOST_WIDE_INT max = res->range.max; unsigned HOST_WIDE_INT max = res->range.max;
if (min == max) if (info.objsize < HOST_WIDE_INT_MAX)
inform (callloc, {
(min == 1 /* If a warning has been issued for buffer overflow or truncation
? G_("%qE output %wu byte into a destination of size %wu") help the user figure out how big a buffer they need. */
: G_("%qE output %wu bytes into a destination of size %wu")),
info.func, min, info.objsize); if (min == max)
else if (max < HOST_WIDE_INT_MAX) inform (callloc,
inform (callloc, (min == 1
"%qE output between %wu and %wu bytes into " ? G_("%qE output %wu byte into a destination of size %wu")
"a destination of size %wu", : G_("%qE output %wu bytes into a destination of size "
info.func, min, max, info.objsize); "%wu")),
else if (min < res->range.likely && res->range.likely < max) info.func, min, info.objsize);
inform (callloc, else if (max < HOST_WIDE_INT_MAX)
"%qE output %wu or more bytes (assuming %wu) into " inform (callloc,
"a destination of size %wu", "%qE output between %wu and %wu bytes into "
info.func, min, res->range.likely, info.objsize); "a destination of size %wu",
else info.func, min, max, info.objsize);
inform (callloc, else if (min < res->range.likely && res->range.likely < max)
"%qE output %wu or more bytes into a destination of size %wu", inform (callloc,
info.func, min, info.objsize); "%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) if (dump_file && *dir.beg)
...@@ -3501,14 +3584,14 @@ sprintf_dom_walker::compute_format_length (call_info &info, ...@@ -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 /* 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 static unsigned HOST_WIDE_INT
get_destination_size (tree dest) get_destination_size (tree dest)
{ {
/* When there is no destination return -1. */ /* When there is no destination return the maximum. */
if (!dest) if (!dest)
return HOST_WIDE_INT_M1U; return HOST_WIDE_INT_MAX;
/* Initialize object size info before trying to compute it. */ /* Initialize object size info before trying to compute it. */
init_object_sizes (); init_object_sizes ();
...@@ -3523,7 +3606,7 @@ get_destination_size (tree dest) ...@@ -3523,7 +3606,7 @@ get_destination_size (tree dest)
if (compute_builtin_object_size (dest, ost, &size)) if (compute_builtin_object_size (dest, ost, &size))
return 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 /* 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) ...@@ -3844,7 +3927,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
case BUILT_IN_PRINTF_CHK: case BUILT_IN_PRINTF_CHK:
// Signature: // Signature:
// __builtin_printf_chk (it, format, ...) // __builtin_printf_chk (ost, format, ...)
idx_format = 1; idx_format = 1;
info.argidx = 2; info.argidx = 2;
idx_dstptr = -1; idx_dstptr = -1;
......
/* 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." } */
}
/* 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." } */
}
/* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning /* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning
{ dg-do compile } { 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 } */ { dg-require-effective-target ptr32plus } */
typedef __SIZE_TYPE__ size_t; typedef __SIZE_TYPE__ size_t;
...@@ -166,11 +166,17 @@ void test_string_checked (const char *s, const struct Arrays *ar) ...@@ -166,11 +166,17 @@ void test_string_checked (const char *s, const struct Arrays *ar)
T (-1, "%s%s", ar->a4k, ar->ax); T (-1, "%s%s", ar->a4k, ar->ax);
/* Verify that an array that fits a string longer than 4095 bytes /* Verify that an array that fits a string longer than 4095 bytes
does trigger a warning. */ does not trigger a warning. (No known implementation has trouble
T (-1, "%-s", ar->a4kp1); /* { dg-warning "directive output between 0 and 4096 bytes may exceed minimum required size of 4095" } */ with this). */
T (-1, "%s", ar->a4kp1);
/* Also verify that a %s directive with width greater than 4095
triggers a warning even if the argument is not longer than 4k. */ /* 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" } */ 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 /* 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) ...@@ -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->a4k, ar->a4k, ar->a4k);
T (-1, "%s %s %s", ar->ax, ar->ax, ar->ax); 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);
} }
...@@ -118,9 +118,9 @@ void test_width_and_precision_out_of_range (char *d) ...@@ -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 /* The range here happens to be a property of the compiler, not
one of the target. */ one of the target. */
T ("%9223372036854775808i", 0); /* { dg-warning "width out of range" "first" } */ 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" } */ 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). */ /* The following is diagnosed by -Wformat (disabled here). */
/* T ("%9223372036854775808$i", 0); */ /* T ("%9223372036854775808$i", 0); */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment