Commit 573aa7d4 by Martin Sebor Committed by Martin Sebor

PR middle-end/78622 - -Wformat-length/-fprintf-return-value incorrect with overflow/wrapping

gcc/ChangeLog:

	PR middle-end/78622
	PR middle-end78606
	* gimple-ssa-sprintf.c (min_bytes_remaining): Use res.knownrange
	rather than res.bounded.
	(get_width_and_precision): Set precision to -1 when negative.
	(adjust_range_for_overflow): New function.
	(format_integer): Correct the handling of the space, plus, and pound
	flags, and the special case of zero precision.
	Always set res.bounded to true unless either precision or width
	is specified and unknown.
	Call adjust_range_for_overflow.
	Avoid use zero as the shortest value when precision is specified
	but unknown.
	(format_directive): Remove vestigial quoting.  Always inform of
	argument value or range when it's available.
	(add_bytes): Correct the computation of boundrange used to
	decide whether a warning is of a "maybe" or "defnitely" kind.

gcc/testsuite/ChangeLog:

	PR middle-end/78622
	PR middle-end78606
	* gcc.c-torture/execute/pr78622.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf-2.c: Remove "benign" undefined
	behavior inadvertently introduced in a previous commit.  Tighten
	up final checking.
	* gcc.dg/tree-ssa/builtin-sprintf-5.c: Rename macros for clarity.
	Add test cases.
	* gcc.dg/tree-ssa/builtin-sprintf-6.c: Add test cases.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-5.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: Remove xfails and
	add a final optimization check.
	* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.
	* gcc.dg/tree-ssa/pr78622.c: New test.

From-SVN: r243582
parent 068b961b
2016-12-12 Martin Sebor <msebor@redhat.com>
PR middle-end/78622
PR middle-end78606
* gimple-ssa-sprintf.c (min_bytes_remaining): Use res.knownrange
rather than res.bounded.
(get_width_and_precision): Set precision to -1 when negative.
(adjust_range_for_overflow): New function.
(format_integer): Correct the handling of the space, plus, and pound
flags, and the special case of zero precision.
Always set res.bounded to true unless either precision or width
is specified and unknown.
Call adjust_range_for_overflow.
Avoid use zero as the shortest value when precision is specified
but unknown.
(format_directive): Remove vestigial quoting. Always inform of
argument value or range when it's available.
(add_bytes): Correct the computation of boundrange used to
decide whether a warning is of a "maybe" or "defnitely" kind.
2016-12-12 Dominik Vogt <vogt@linux.vnet.ibm.com>
* combine.c (change_zero_ext): Handle mode expanding zero_extracts.
2016-12-12 Martin Sebor <msebor@redhat.com>
PR middle-end/78622
PR middle-end78606
* gcc.c-torture/execute/pr78622.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-2.c: Remove "benign" undefined
behavior inadvertently introduced in a previous commit. Tighten
up final checking.
* gcc.dg/tree-ssa/builtin-sprintf-5.c: Rename macros for clarity.
Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-6.c: Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-5.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: Remove xfails and
add a final optimization check.
* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.
* gcc.dg/tree-ssa/pr78622.c: New test.
2016-12-12 Janus Weil <janus@gcc.gnu.org>
PR fortran/78392
......
/* PR middle-end/78622 - [7 Regression] -Wformat-length/-fprintf-return-value
incorrect with overflow/wrapping
{ dg-additional-options "-Wformat-length=2" } */
__attribute__((noinline, noclone)) int
foo (int x)
{
if (x < 4096 + 8 || x >= 4096 + 256 + 8)
return -1;
char buf[5];
int n = __builtin_snprintf (buf, sizeof buf, "%hhd", x + 1);
__builtin_printf ("\"%hhd\" => %i\n", x + 1, n);
return n;
}
int
main (void)
{
if (__SCHAR_MAX__ != 127 || __CHAR_BIT__ != 8 || __SIZEOF_INT__ != 4)
return 0;
if (foo (4095 + 9) != 1
|| foo (4095 + 32) != 2
|| foo (4095 + 127) != 3
|| foo (4095 + 128) != 4
|| foo (4095 + 240) != 3
|| foo (4095 + 248) != 2
|| foo (4095 + 255) != 2
|| foo (4095 + 256) != 1)
__builtin_abort ();
return 0;
}
......@@ -25,6 +25,9 @@ char buf8k [8192];
#define concat(a, b) a ## b
#define CAT(a, b) concat (a, b)
/* Calls to this function must not be eliminated. */
void must_not_eliminate (void);
#define EQL(expect, size, fmt, ...) \
void __attribute__ ((noinline, noclone)) \
CAT (test_on_line_, __LINE__)(void) \
......@@ -34,7 +37,7 @@ char buf8k [8192];
char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \
int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \
if (result != expect) \
__builtin_abort (); \
must_not_eliminate (); \
} \
}
......@@ -50,7 +53,7 @@ char buf8k [8192];
char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \
int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \
if (result < min || max < result) \
__builtin_abort (); \
must_not_eliminate (); \
} \
}
......@@ -75,6 +78,8 @@ EQL (0, 0, "%-s", "");
EQL (1, 1, "%c", 'x');
EQL (1, 1, "%-s", "x");
EQL (1, 2, "%c", 'x');
EQL (4, 4, "%4c", 'x');
/* Verify that exceeding the environmental limit of 4095 bytes for
......@@ -179,7 +184,7 @@ RNG (4, 4, 32, "%zu", sz)
/* Exercise bug 78586. */
RNG (4, 4, 32, "%lu", (unsigned long)i)
RNG (4, 4, 32, "%lu", (unsigned)u)
RNG (4, 4, 32, "%lu", (unsigned long)u)
RNG (4, 4, 32, "%lu", (unsigned long)li)
RNG (4, 4, 32, "%lu", (unsigned long)lu)
RNG (4, 4, 32, "%lu", (unsigned long)sz)
......@@ -259,21 +264,24 @@ RNG (0, 6, 8, "%s%ls", "1", L"2");
/* Verify that no call to abort has been eliminated and that each call
is at the beginning of a basic block (and thus the result of a branch).
This latter test tries to verify that the test preceding the call to
abort has not been eliminated either.
the must_not_eliminate() function has not been eliminated either.
The expected output looks something like this:
<bb 2>:
result_3 = __builtin_sprintf (&MEM[(void *)&buf8k + 8192B], "%c", 32);
if (result_3 != 0)
goto <bb 3>;
goto <bb 3>; [50.0%]
else
goto <bb 4>;
goto <bb 4>; [50.0%]
<bb 3>:
__builtin_abort ();
<bb 3>[50.0%]:
must_not_eliminate ();
*/
/* { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *__builtin_abort" 124 "optimized" { target { ilp32 || lp64 } } } } */
/* { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *__builtin_abort" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */
/* Only conditional calls to abort should be made (with any probability):
{ dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 124 "optimized" { target { ilp32 || lp64 } } } }
{ dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } }
No unconditional calls to abort should be made:
{ dg-final { scan-tree-dump-not ";\n *must_not_eliminate" "optimized" } } */
......@@ -8,18 +8,20 @@
#define FAIL(line) CAT (failure_on_line_, line)
#define PASS(line) CAT (success_on_line_, line)
/* Emit a call to a function named failure_on_line_NNN when EXPR is false. */
#define ASSERT(value, expect) \
/* Emit a call to a function named failure_on_line_NNN when VALUE
is not equal to the constant EXPECTED, otherwise emit a call to
function success_on_line_NNN. */
#define ASSERT(value, expected) \
do { \
extern void FAIL (__LINE__)(int); \
extern void PASS (__LINE__)(int); \
if (value == expect) \
if (value == expected) \
PASS (__LINE__)(value); \
else \
FAIL (__LINE__)(value); \
} while (0)
#define T(expect, ...) \
#define EQL(expect, ...) \
do { \
int n = __builtin_snprintf (0, 0, __VA_ARGS__); \
ASSERT (n, expect); \
......@@ -27,88 +29,118 @@
int ival (int i) { return i; }
/* Generate a signed int value in the specified range. */
static int
int_range (int min, int max)
{
extern int int_value (void);
int val = int_value ();
if (val < min || max < val)
val = min;
return val;
}
#define R(min, max) int_range (min, max)
void test_arg_int (int i, int n)
{
T (1, "%i", ival (0));
T (1, "%i", ival (1));
T (2, "%i%i", ival (0), ival (1));
T (3, "%i%i%i", ival (0), ival (1), ival (9));
T (5, "%i %i %i", ival (0), ival (1), ival (9));
EQL (1, "%i", ival (0));
EQL (1, "%i", ival (1));
EQL (2, "%i%i", ival (0), ival (1));
EQL (3, "%i%i%i", ival (0), ival (1), ival (9));
EQL (5, "%i %i %i", ival (0), ival (1), ival (9));
T (5, "%i %i %i", ival (0), ival (1), ival (9));
EQL (5, "%i %i %i", ival (0), ival (1), ival (9));
T (13, "%hhu.%hhu.%hhu.%hhu", ival (23), ival (78), ival (216), ival (135));
EQL (13, "%hhu.%hhu.%hhu.%hhu", ival (23), ival (78), ival (216), ival (135));
for (i = 0; i != 9; ++i)
T (1, "%i", i);
EQL (1, "%i", i);
for (i = -n; i != n; ++i)
T (8, "%08x", i);
EQL (8, "%08x", i);
/* As a special case, a precision of zero with an argument of zero
results in zero bytes (unless modified by width). */
T (0, "%.0d", ival (0));
T (0, "%.0i", ival (0));
T (0, "%.0o", ival (0));
T (0, "%.0u", ival (0));
T (0, "%.0x", ival (0));
T (0, "%.*d", 0, ival (0));
T (0, "%.*i", 0, ival (0));
T (0, "%.*o", 0, ival (0));
T (0, "%.*u", 0, ival (0));
T (0, "%.*x", 0, ival (0));
T (1, "%1.0d", ival (0));
T (1, "%1.0i", ival (0));
T (1, "%1.0o", ival (0));
T (1, "%1.0u", ival (0));
T (1, "%1.0x", ival (0));
EQL (0, "%.0d", ival (0));
EQL (0, "%.0i", ival (0));
EQL (0, "%.0o", ival (0));
EQL (0, "%.0u", ival (0));
EQL (0, "%.0x", ival (0));
EQL (0, "%.*d", 0, ival (0));
EQL (0, "%.*i", 0, ival (0));
EQL (0, "%.*o", 0, ival (0));
EQL (0, "%.*u", 0, ival (0));
EQL (0, "%.*x", 0, ival (0));
EQL (1, "%1.0d", ival (0));
EQL (1, "%1.0i", ival (0));
EQL (1, "%1.0o", ival (0));
EQL (1, "%1.0u", ival (0));
EQL (1, "%1.0x", ival (0));
EQL (4, "%hhi", R (-128, -127));
EQL (3, "%hhi", R ( -99, -10));
EQL (2, "%hhi", R ( -9, -1));
EQL (1, "%hhi", R ( 0, 9));
EQL (1, "%hhi", R ( 0, 9));
EQL (1, "%1.0hhi", R ( 0, 1));
EQL (1, "%1.1hhi", R ( 0, 9));
EQL (2, "%1.2hhi", R ( 0, 9));
EQL (3, "%1.3hhi", R ( 0, 9));
EQL (1, "%hhi", R (1024, 1033));
EQL (2, "%hhi", R (1034, 1123));
EQL (1, "%hhu", R (1024, 1033));
EQL (2, "%hhu", R (1034, 1123));
}
void test_arg_string (const char *s)
{
T ( 0, "%-s", "");
T ( 1, "%%");
T ( 1, "%-s", "1");
T ( 2, "%-s", "12");
T ( 3, "%-s", "123");
T ( 5, "s=%s", "123");
T (10, "%%s=\"%s\"", "12345");
T ( 1, "%.*s", 1, "123");
T ( 2, "%.*s", 2, "123");
T ( 3, "%.*s", 3, "123");
T ( 3, "%.*s", 4, "123");
T ( 1, "%1.*s", 1, "123");
T ( 2, "%1.*s", 2, "123");
T ( 3, "%1.*s", 3, "123");
T ( 3, "%1.*s", 4, "123");
T ( 4, "%4.*s", 1, "123");
T ( 4, "%4.*s", 2, "123");
T ( 4, "%4.*s", 3, "123");
T ( 4, "%4.*s", 4, "123");
T ( 4, "%4.*s", 5, "123");
EQL ( 0, "%-s", "");
EQL ( 1, "%%");
EQL ( 1, "%-s", "1");
EQL ( 2, "%-s", "12");
EQL ( 3, "%-s", "123");
EQL ( 5, "s=%s", "123");
EQL (10, "%%s=\"%s\"", "12345");
EQL ( 1, "%.*s", 1, "123");
EQL ( 2, "%.*s", 2, "123");
EQL ( 3, "%.*s", 3, "123");
EQL ( 3, "%.*s", 4, "123");
EQL ( 1, "%1.*s", 1, "123");
EQL ( 2, "%1.*s", 2, "123");
EQL ( 3, "%1.*s", 3, "123");
EQL ( 3, "%1.*s", 4, "123");
EQL ( 4, "%4.*s", 1, "123");
EQL ( 4, "%4.*s", 2, "123");
EQL ( 4, "%4.*s", 3, "123");
EQL ( 4, "%4.*s", 4, "123");
EQL ( 4, "%4.*s", 5, "123");
const char *a = "123";
const char *b = "456";
T ( 3, "%-s", s ? a : b);
T ( 0, "%.0s", s);
T ( 1, "%1.1s", s);
T ( 2, "%2.2s", s);
T ( 2, "%2.1s", s);
EQL ( 3, "%-s", s ? a : b);
EQL ( 0, "%.0s", s);
EQL ( 1, "%1.1s", s);
EQL ( 2, "%2.2s", s);
EQL ( 2, "%2.1s", s);
}
void test_arg_multiarg (int i, double d)
{
T (16, "%i %f %s", 123, 3.14, "abc");
T (16, "%12i %s", i, "abc");
T (16, "%*i %s", 12, i, "abc");
EQL (16, "%i %f %s", 123, 3.14, "abc");
EQL (16, "%12i %s", i, "abc");
EQL (16, "%*i %s", 12, i, "abc");
}
#define TV(expect, fmt, va) \
#define EQLv(expect, fmt, va) \
do { \
int n = __builtin_vsnprintf (0, 0, fmt, va); \
ASSERT (n, expect); \
......@@ -116,21 +148,21 @@ void test_arg_multiarg (int i, double d)
void test_va_int (__builtin_va_list va)
{
TV ( 2, "%02hhx", va);
TV ( 2, "%02.*hhx", va);
TV ( 4, "%04hx", va);
TV ( 4, "%04.*hx", va);
EQLv ( 2, "%02hhx", va);
EQLv ( 2, "%02.*hhx", va);
EQLv ( 4, "%04hx", va);
EQLv ( 4, "%04.*hx", va);
}
void test_va_multiarg (__builtin_va_list va)
{
TV ( 8, "%8x", va);
TV ( 8, "% 8x", va);
TV ( 9, "%9x", va);
TV (11, "%11o", va);
TV (12, "%12o", va);
EQLv ( 8, "%8x", va);
EQLv ( 8, "% 8x", va);
EQLv ( 9, "%9x", va);
EQLv (11, "%11o", va);
EQLv (12, "%12o", va);
TV (16, "%12i %3.2s", va);
EQLv (16, "%12i %3.2s", va);
}
......
......@@ -8,6 +8,8 @@
{ dg-options "-O2 -Wformat -fdump-tree-optimized" }
{ dg-require-effective-target int32plus } */
typedef __SIZE_TYPE__ size_t;
#define CONCAT(a, b) a ## b
#define CAT(a, b) CONCAT (a, b)
......@@ -50,6 +52,19 @@ void test_arg_int (int width, int prec, int i, int n)
T ("%i", R (1, 10));
/* Each of the bounds of the ranges below results in just one byte
on output because they convert to the value 9 in type char, yet
other values in those ranges can result in up to four bytes.
For example, 4240 converts to -112. Verify that the output
isn't folded into a constant. This assumes __CHAR_BIT__ of 8. */
T ("%hhi", R (4104, 4360) + 1);
T ("%hhu", R (4104, 4360) + 1);
/* Here, the range includes all possible lengths of output for
a 16-bit short and 32-bit int. */
T ("%hi", R (65536, 65536 * 2));
T ("%hu", R (65536, 65536 * 2));
T ("%'i", 1234567);
for (i = -n; i != n; ++i)
......@@ -104,6 +119,7 @@ void test_invalid_directive (void)
T ("abc%Q"); /* { dg-warning "unknown conversion type character .Q." } */
}
/* Use 'grep "^ *T (" builtin-sprintf-6.c | wc -l' to determine
the count for the directive below.
{ dg-final { scan-tree-dump-times "snprintf" 42 "optimized"} } */
{ dg-final { scan-tree-dump-times "snprintf" 46 "optimized"} } */
......@@ -429,11 +429,11 @@ void test_sprintf_chk_hh_const (void)
T (4, "%hhi %hhi", 11, 12); /* { dg-warning "into a region" } */
/* As a special case, a precision of zero with an argument of zero
results in zero bytes (unless modified by width). */
results in zero bytes (unless modified by flags and/or width). */
T (1, "%.0hhd", 0);
T (1, "%+.0hhd", 0);
T (1, "%+.0hhd", 0); /* { dg-warning "nul past the end" } */
T (1, "%-.0hhd", 0);
T (1, "% .0hhd", 0);
T (1, "% .0hhd", 0); /* { dg-warning "nul past the end" } */
T (1, "%0.0hhd", 0); /* { dg-warning ".0. flag ignored with precision" } */
T (1, "%00.0hhd", 0); /* { dg-warning "repeated .0. flag in format" } */
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
......@@ -441,7 +441,8 @@ void test_sprintf_chk_hh_const (void)
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
T (1, "%.0hhi", 0);
T (1, "%.0hho", 0);
T (1, "%#.0hho", 0);
/* As a special case, '#' in base 8 results in 1 byte (the zero). */
T (1, "%#.0hho", 0); /* { dg-warning "nul past the end" } */
T (1, "%.0hhx", 0);
T (1, "%.0hhX", 0);
T (1, "%#.0hhX", 0);
......@@ -545,9 +546,9 @@ void test_sprintf_chk_h_const (void)
/* As a special case, a precision of zero with an argument of zero
results in zero bytes (unless modified by width). */
T (1, "%.0hd", 0);
T (1, "%+.0hd", 0);
T (1, "%+.0hd", 0); /* { dg-warning "nul past the end" } */
T (1, "%-.0hd", 0);
T (1, "% .0hd", 0);
T (1, "% .0hd", 0); /* { dg-warning "nul past the end" } */
T (1, "%0.0hd", 0); /* { dg-warning ".0. flag ignored with precision" } */
T (1, "%00.0hd", 0); /* { dg-warning "repeated .0. flag in format" } */
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
......@@ -555,7 +556,7 @@ void test_sprintf_chk_h_const (void)
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
T (1, "%.0hi", 0);
T (1, "%.0ho", 0);
T (1, "%#.0ho", 0);
T (1, "%#.0ho", 0); /* { dg-warning "nul past the end" } */
T (1, "%.0hx", 0);
T (1, "%.0hX", 0);
T (1, "%#.0hX", 0);
......@@ -628,9 +629,9 @@ void test_sprintf_chk_integer_const (void)
/* As a special case, a precision of zero with an argument of zero
results in zero bytes (unless modified by width). */
T (1, "%.0d", 0);
T (1, "%+.0d", 0);
T (1, "%+.0d", 0); /* { dg-warning "nul past the end" } */
T (1, "%-.0d", 0);
T (1, "% .0d", 0);
T (1, "% .0d", 0); /* { dg-warning "nul past the end" } */
T (1, "%0.0d", 0); /* { dg-warning ".0. flag ignored with precision" } */
T (1, "%00.0d", 0); /* { dg-warning "repeated .0. flag in format" } */
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
......@@ -638,7 +639,7 @@ void test_sprintf_chk_integer_const (void)
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
T (1, "%.0i", 0);
T (1, "%.0o", 0);
T (1, "%#.0o", 0);
T (1, "%#.0o", 0); /* { dg-warning "nul past the end" } */
T (1, "%.0x", 0);
T (1, "%.0X", 0);
T (1, "%#.0X", 0);
......@@ -727,9 +728,11 @@ void test_sprintf_chk_j_const (void)
/* As a special case, a precision of zero with an argument of zero
results in zero bytes (unless modified by width). */
T (1, "%.0jd", I (0));
T (1, "%+.0jd", I (0));
T (1, "%+.0jd", I (0)); /* { dg-warning "nul past the end" } */
T (1, "%+.0ju", I (0)); /* { dg-warning ".\\+. flag used" } */
T (1, "%-.0jd", I (0));
T (1, "% .0jd", I (0));
T (1, "% .0jd", I (0)); /* { dg-warning "nul past the end" } */
T (1, "% .0ju", I (0)); /* { dg-warning ". . flag used" } */
T (1, "%0.0jd", I (0)); /* { dg-warning ".0. flag ignored with precision" } */
T (1, "%00.0jd", I (0)); /* { dg-warning "repeated .0. flag in format" } */
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
......@@ -737,7 +740,7 @@ void test_sprintf_chk_j_const (void)
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
T (1, "%.0ji", I (0));
T (1, "%.0jo", I (0));
T (1, "%#.0jo", I (0));
T (1, "%#.0jo", I (0)); /* { dg-warning "nul past the end" } */
T (1, "%.0jx", I (0));
T (1, "%.0jX", I (0));
T (1, "%#.0jX", I (0));
......@@ -801,9 +804,11 @@ void test_sprintf_chk_l_const (void)
/* As a special case, a precision of zero with an argument of zero
results in zero bytes (unless modified by width). */
T (1, "%.0ld", 0L);
T (1, "%+.0ld", 0L);
T (1, "%+.0ld", 0L); /* { dg-warning "nul past the end" } */
T (1, "%+.0lu", 0L); /* { dg-warning ".\\+. flag used" } */
T (1, "%-.0ld", 0L);
T (1, "% .0ld", 0L);
T (1, "% .0ld", 0L); /* { dg-warning "nul past the end" } */
T (1, "% .0lu", 0L); /* { dg-warning ". . flag used" } */
T (1, "%0.0ld", 0L); /* { dg-warning ".0. flag ignored with precision" } */
T (1, "%00.0ld", 0L); /* { dg-warning "repeated .0. flag in format" } */
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
......@@ -811,7 +816,7 @@ void test_sprintf_chk_l_const (void)
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
T (1, "%.0li", 0L);
T (1, "%.0lo", 0L);
T (1, "%#.0lo", 0L);
T (1, "%#.0lo", 0L); /* { dg-warning "nul past the end" } */
T (1, "%.0lx", 0L);
T (1, "%.0lX", 0L);
T (1, "%#.0lX", 0L);
......@@ -1039,8 +1044,8 @@ void test_sprintf_chk_s_nonconst (int w, int p, const char *s)
is not. */
T ( 1, "%*s", w, "");
T ( 1, "%*s", w, "1"); /* { dg-warning "nul past the end" } */
T ( 1, "%.*s", w, "");
T ( 1, "%.*s", w, "1"); /* { dg-warning "may write a terminating nul" } */
T ( 1, "%.*s", p, "");
T ( 1, "%.*s", p, "1"); /* { dg-warning "may write a terminating nul" } */
T ( 1, "%.*s", w, "123"); /* { dg-warning "writing between 0 and 3 bytes into a region of size 1" } */
T ( 1, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 1" } */
......@@ -1294,6 +1299,12 @@ void test_sprintf_chk_int_nonconst (int w, int p, int a)
T (3, "%2x", a);
T (1, "%.*d", p, a);
T (4, "%i %i", a, a);
/* The following will definitely be "writing a terminating nul past the end"
(i.e., not "may write".) */
T (4, "%i %i ", a, a); /* { dg-warning "writing a terminating nul past the end" } */
T (4, "%i %i %i", a, a, a); /* { dg-warning "into a region" }*/
}
void test_sprintf_chk_e_nonconst (int w, int p, double d)
......
......@@ -125,12 +125,61 @@ void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a)
/* Exercise buffer overflow detection with non-const integer arguments. */
void test_hh_nonconst (int x)
void test_hh_nonconst (int w, int p, int x, unsigned y)
{
T (1, "%hhi", x); /* { dg-warning "into a region" } */
T (2, "%hhi", x); /* { dg-warning "into a region" } */
T (3, "%hhi", x); /* { dg-warning "into a region" } */
T (4, "%hhi", x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T (1, "%hhi", y); /* { dg-warning "between 1 and 4 bytes" } */
T (2, "%hhi", y); /* { dg-warning "into a region" } */
T (3, "%hhi", y); /* { dg-warning "into a region" } */
T (4, "%hhi", y); /* { dg-warning "may write a terminating nul past the end of the destination" } */
/* Negative precision is treated as if none were specified. */
T (1, "%.*hhi", -1, x); /* { dg-warning "between 1 and 4 bytes" } */
T (2, "%.*hhi", -1, x); /* { dg-warning "into a region" } */
T (3, "%.*hhi", -1, x); /* { dg-warning "into a region" } */
T (4, "%.*hhi", -1, x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
/* Zero precision means that zero argument formats as no bytes unless
length or flags make it otherwise. */
T (1, "%.*hhi", 0, x); /* { dg-warning "between 0 and 4 bytes" } */
T (2, "%.*hhi", 0, x); /* { dg-warning "between 0 and 4 bytes" } */
T (3, "%.*hhi", 0, x); /* { dg-warning "between 0 and 4 bytes" } */
T (4, "%.*hhi", 0, x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T (1, "%.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */
T (2, "%.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */
T (3, "%.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */
T (4, "%.*hhi", 0, y); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T (1, "%#.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */
T (1, "%-.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */
T (1, "% .*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */
T (1, "%#.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhi", 1, y); /* { dg-warning "between 2 and 4 bytes" } */
T (1, "%-.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */
T (1, "% .*hhi", 1, y); /* { dg-warning "between 2 and 4 bytes" } */
T (1, "%#.*hhi", p, y); /* { dg-warning "writing 0 or more bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhi", p, y); /* { dg-warning "writing 1 or more bytes" } */
T (1, "%-.*hhi", p, y); /* { dg-warning "writing 0 or more bytes" } */
T (1, "% .*hhi", p, y); /* { dg-warning "writing 1 or more bytes" } */
T (1, "%#.*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */
/* { dg-warning ".\\+. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%-.*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */
T (1, "% .*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */
/* { dg-warning ". . flag used" "-Wformat" { target *-*-* } .-1 } */
}
void test_h_nonconst (int x)
......
......@@ -2,15 +2,18 @@
Test to verify that the correct range information is made available to the
-Wformat-lenght check to prevent warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wformat -Wformat-length" } */
/* { dg-options "-O2 -Wformat -Wformat-length -fdump-tree-optimized" } */
void abort (void);
int snprintf (char*, __SIZE_TYPE__, const char*, ...);
void fuchar (unsigned char j, char *p)
{
if (j > 99)
return;
snprintf (p, 4, "%3hu", j);
if (3 != snprintf (p, 4, "%3hu", j))
abort ();
}
void fschar (signed char j, char *p)
......@@ -20,14 +23,17 @@ void fschar (signed char j, char *p)
if (k > 99)
return;
snprintf (p, 3, "%3hhu", k); /* { dg-bogus "" "unsigned char" { xfail *-*-* } } */
if (3 != snprintf (p, 4, "%3hhu", k))
abort ();
}
void fushrt (unsigned short j, char *p)
{
if (j > 999)
return;
snprintf (p, 4, "%3hu", j);
if (3 != snprintf (p, 4, "%3hu", j))
abort ();
}
void fshrt (short j, char *p)
......@@ -37,7 +43,8 @@ void fshrt (short j, char *p)
if (k > 999)
return;
snprintf (p, 4, "%3hu", k);
if (3 != snprintf (p, 4, "%3hu", k))
abort ();
}
void fuint (unsigned j, char *p)
......@@ -54,13 +61,16 @@ void fint (int j, char *p)
if (k > 999)
return;
snprintf (p, 4, "%3u", k); /* { dg-bogus "" "unsigned int" { xfail *-*-* } } */
/* Range info isn't available here. */
snprintf (p, 4, "%3u", k);
}
void fulong (unsigned long j, char *p)
{
if (j > 999)
return;
/* Range info isn't available here. */
snprintf (p, 4, "%3lu", j);
}
......@@ -71,13 +81,16 @@ void flong (long j, char *p)
if (k > 999)
return;
snprintf (p, 4, "%3lu", k); /* { dg-bogus "" "unsigned long" { xfail *-*-* } } */
/* Range info isn't available here. */
snprintf (p, 4, "%3lu", k);
}
void fullong (unsigned long long j, char *p)
{
if (j > 999)
return;
/* Range info isn't available here. */
snprintf (p, 4, "%3llu", j);
}
......@@ -88,5 +101,7 @@ void fllong (long j, char *p)
if (k > 999)
return;
snprintf (p, 4, "%3llu", k); /* { dg-bogus "" "unsigned long long" { xfail lp64 } } */
snprintf (p, 4, "%3llu", k);
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
......@@ -166,27 +166,48 @@ test_c (char c)
EQL (5, 6, "%c %c %c", c, c, c);
}
/* Generate a pseudo-random value in the specified range. The return
value must be unsigned char to work around limitations in the GCC
range information. Similarly for the declaration of rand() whose
correct return value should be int, but that also prevents the range
information from making it to the printf pass. */
/* Generate a pseudo-random unsigned value. */
unsigned char uchar_range (unsigned min, unsigned max)
unsigned __attribute__ ((noclone, noinline))
unsigned_value (void)
{
extern unsigned rand (void);
extern int rand ();
return rand ();
}
unsigned x;
x = rand ();
/* Generate a pseudo-random signed value. */
if (x < min)
x = min;
else if (max < x)
x = max;
int __attribute__ ((noclone, noinline))
int_value (void)
{
extern int rand ();
return rand ();
}
/* Generate an unsigned char value in the specified range. */
static unsigned char
uchar_range (unsigned min, unsigned max)
{
unsigned x = unsigned_value ();
if (x < min || max < x)
x = min;
return x;
}
/* Generate a signed int value in the specified range. */
static int
int_range (int min, int max)
{
int val = int_value ();
if (val < min || max < val)
val = min;
return val;
}
#define IR(min, max) int_range (min, max)
static void __attribute__ ((noinline, noclone))
test_d_i (int i, long li)
{
......@@ -266,9 +287,35 @@ test_d_i (int i, long li)
RNG ( 1, 4, 5, "%hhi", i);
RNG ( 1, 3, 4, "%hhu", i);
RNG ( 3, 4, 5, "%hhi", IR (-128, -10));
RNG ( 2, 4, 5, "%hhi", IR (-128, -1));
RNG ( 1, 4, 5, "%hhi", IR (-128, 0));
RNG ( 1, 4, 5, "%1hhi", IR (-128, 0));
RNG ( 1, 4, 5, "%2hhi", IR (-128, 0));
RNG ( 1, 4, 5, "%3hhi", IR (-128, 0));
RNG ( 1, 4, 5, "%4hhi", IR (-128, 0));
RNG ( 1, 5, 6, "%5hhi", IR (-128, 0));
RNG ( 1, 6, 7, "%6hhi", IR (-128, 0));
RNG ( 2, 6, 7, "%6hhi", IR (-128, 10));
RNG ( 0, 1, 2, "%.hhi", IR ( 0, 1));
RNG ( 0, 1, 2, "%.0hhi", IR ( 0, 1));
RNG ( 0, 1, 2, "%0.0hhi", IR ( 0, 1)); /* { dg-warning ".0. flag ignored with precision" } */
RNG ( 0, 1, 2, "%*.0hhi", 0, IR ( 0, 1));
RNG ( 1, 2, 3, "%hhi", IR (1024, 1034));
RNG ( 1, 4, 5, "%hhi", IR (1024, 2048));
RNG ( 2, 3, 4, "%hhi", IR (1034, 1151));
RNG ( 1, 2, 3, "%hhu", IR (1024, 1034));
RNG ( 1, 3, 4, "%hhu", IR (1024, 2048));
RNG ( 2, 3, 4, "%hhu", IR (1034, 1151));
#if __SIZEOF_SHORT__ == 2
RNG ( 1, 6, 7, "%hi", i);
RNG ( 1, 5, 6, "%hu", i);
#elif __SIZEOF_SHORT__ == 4
RNG ( 1, 11, 12, "%hi", i);
RNG ( 1, 10, 11, "%hu", i);
......
/* PR middle-end/78622 - [7 Regression] -Wformat-length/-fprintf-return-value
incorrect with overflow/wrapping
{ dg-do compile }
{ dg-options "-Wformat-length=2" } */
char buf[1];
int test_uchar_hhd (unsigned char x)
{
if (x < 64 || x > 2U * __SCHAR_MAX__ - 10)
return -1;
return __builtin_sprintf (buf, "%hhd", x + 1); /* { dg-warning "directive writing between 1 and 4 bytes into a region of size 1" "int32plus" { target { int32plus } } } */
}
int test_uint_hhd (unsigned x)
{
if (x < 64 || x > 2U * __INT_MAX__ - 10)
return -1;
return __builtin_sprintf (buf, "%hhd", x + 1); /* { dg-warning "directive writing between 1 and 4 bytes into a region of size 1" "int32plus" { target { int32plus } } } */
}
int test_schar_hhu (signed char x)
{
if (x < -9 || x > 9)
return -1;
return __builtin_sprintf (buf, "%hhu", x + 1); /* { dg-warning "directive writing between 1 and 3 bytes into a region of size 1" "int32plus" { target { int32plus } } } */
}
int test_ushort_hd (unsigned short x)
{
if (x < 64 || x > 2U * __SHRT_MAX__ - 10)
return -1;
return __builtin_sprintf (buf, "%hd", x + 1); /* { dg-warning "directive writing between 1 and 6 bytes into a region of size 1" "int32plus" { target { int32plus } } } */
}
int test_uint_d (unsigned x)
{
if (x < 64 || x > 2U * __INT_MAX__ - 10)
return -1;
return __builtin_sprintf (buf, "%d", x + 1); /* { dg-warning "directive writing between 1 and 11 bytes into a region of size 1" "int32plus" { target { int32plus } } } */
}
int test_ulong_ld (unsigned long x)
{
if (x < 64 || x > 2LU * __LONG_MAX__ - 10)
return -1;
return __builtin_sprintf (buf, "%ld", x + 1); /* { dg-warning "directive writing between 1 and 11 bytes into a region of size 1" "ilp32" { target { ilp32 } } } */
/* { dg-warning "directive writing between 1 and 20 bytes into a region of size 1" "lp64" { target { lp64 } } .-1 } */
}
int test_ullong_lld (unsigned long long x)
{
if (x < 64 || x > 2LLU * __LONG_LONG_MAX__ - 10)
return -1;
return __builtin_sprintf (buf, "%lld", x + 1); /* { dg-warning "directive writing between 1 and 20 bytes into a region of size 1" "int32plus" { target { int32plus } } } */
}
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