Commit 1eb4547b by Martin Sebor Committed by Martin Sebor

PR middle-end/77735 - FAIL: gcc.dg/tree-ssa/builtin-sprintf-warn-1.c

gcc/ChangeLog:

	PR middle-end/77735
	* builtins.c (string_length): New function.
	(c_strlen): Use string_length.  Correctly handle wide strings.
	* gimple-ssa-sprintf.c (target_max_value, target_size_max): New
	functions.
	(target_int_max): Call target_max_value.
	(format_result::knownrange): New data member.
	(fmtresult::fmtresult): Define default constructor.
	(format_integer): Use it and set format_result::knownrange.
	Handle global constants.
	(format_floating_max): Add third argument.
	(format_floating): Recompute maximum value for %a for each argument.
	(get_string_length): Use fmtresult default ctor.
	(format_string): Set format_result::knownrange.
	(format_directive): Check format_result::knownrange.
	(add_bytes): Same.  Correct caret placement in diagnostics.
	(pass_sprintf_length::compute_format_length): Set
	format_result::knownrange.
	(pass_sprintf_length::handle_gimple_call): Use target_size_max.

gcc/testsuite/ChangeLog:

	PR middle-end/77735
	* gcc.dg/tree-ssa/builtin-sprintf-2.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: Adjust/relax.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: Add test cases.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: XFAIL for LP64 only.
	* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.

From-SVN: r241489
parent 94caf860
2016-10-24 Martin Sebor <msebor@redhat.com>
PR middle-end/77735
* builtins.c (string_length): New function.
(c_strlen): Use string_length. Correctly handle wide strings.
* gimple-ssa-sprintf.c (target_max_value, target_size_max): New
functions.
(target_int_max): Call target_max_value.
(format_result::knownrange): New data member.
(fmtresult::fmtresult): Define default constructor.
(format_integer): Use it and set format_result::knownrange.
Handle global constants.
(format_floating_max): Add third argument.
(format_floating): Recompute maximum value for %a for each argument.
(get_string_length): Use fmtresult default ctor.
(format_string): Set format_result::knownrange.
(format_directive): Check format_result::knownrange.
(add_bytes): Same. Correct caret placement in diagnostics.
(pass_sprintf_length::compute_format_length): Set
format_result::knownrange.
(pass_sprintf_length::handle_gimple_call): Use target_size_max.
2016-10-24 Jakub Jelinek <jakub@redhat.com> 2016-10-24 Jakub Jelinek <jakub@redhat.com>
* config/i386/i386.c (ix86_in_large_data_p, ix86_expand_builtin): Use * config/i386/i386.c (ix86_in_large_data_p, ix86_expand_builtin): Use
......
...@@ -503,9 +503,44 @@ get_pointer_alignment (tree exp) ...@@ -503,9 +503,44 @@ get_pointer_alignment (tree exp)
return align; return align;
} }
/* Compute the length of a C string. TREE_STRING_LENGTH is not the right /* Return the number of non-zero elements in the sequence
way, because it could contain a zero byte in the middle. [ PTR, PTR + MAXELTS ) where each element's size is ELTSIZE bytes.
TREE_STRING_LENGTH is the size of the character array, not the string. ELTSIZE must be a power of 2 less than 8. Used by c_strlen. */
static unsigned
string_length (const void *ptr, unsigned eltsize, unsigned maxelts)
{
gcc_checking_assert (eltsize == 1 || eltsize == 2 || eltsize == 4);
unsigned n;
if (eltsize == 1)
{
/* Optimize the common case of plain char. */
for (n = 0; n < maxelts; n++)
{
const char *elt = (const char*) ptr + n;
if (!*elt)
break;
}
}
else
{
for (n = 0; n < maxelts; n++)
{
const char *elt = (const char*) ptr + n * eltsize;
if (!memcmp (elt, "\0\0\0\0", eltsize))
break;
}
}
return n;
}
/* Compute the length of a null-terminated character string or wide
character string handling character sizes of 1, 2, and 4 bytes.
TREE_STRING_LENGTH is not the right way because it evaluates to
the size of the character array in bytes (as opposed to characters)
and because it can contain a zero byte in the middle.
ONLY_VALUE should be nonzero if the result is not going to be emitted ONLY_VALUE should be nonzero if the result is not going to be emitted
into the instruction stream and zero if it is going to be expanded. into the instruction stream and zero if it is going to be expanded.
...@@ -526,12 +561,6 @@ get_pointer_alignment (tree exp) ...@@ -526,12 +561,6 @@ get_pointer_alignment (tree exp)
tree tree
c_strlen (tree src, int only_value) c_strlen (tree src, int only_value)
{ {
tree offset_node;
HOST_WIDE_INT offset;
int max;
const char *ptr;
location_t loc;
STRIP_NOPS (src); STRIP_NOPS (src);
if (TREE_CODE (src) == COND_EXPR if (TREE_CODE (src) == COND_EXPR
&& (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
...@@ -548,25 +577,36 @@ c_strlen (tree src, int only_value) ...@@ -548,25 +577,36 @@ c_strlen (tree src, int only_value)
&& (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
return c_strlen (TREE_OPERAND (src, 1), only_value); return c_strlen (TREE_OPERAND (src, 1), only_value);
loc = EXPR_LOC_OR_LOC (src, input_location); location_t loc = EXPR_LOC_OR_LOC (src, input_location);
src = string_constant (src, &offset_node); /* Offset from the beginning of the string in bytes. */
tree byteoff;
src = string_constant (src, &byteoff);
if (src == 0) if (src == 0)
return NULL_TREE; return NULL_TREE;
max = TREE_STRING_LENGTH (src) - 1; /* Determine the size of the string element. */
ptr = TREE_STRING_POINTER (src); unsigned eltsize
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src))));
/* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible
length of SRC. */
unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1;
if (offset_node && TREE_CODE (offset_node) != INTEGER_CST) /* PTR can point to the byte representation of any string type, including
char* and wchar_t*. */
const char *ptr = TREE_STRING_POINTER (src);
if (byteoff && TREE_CODE (byteoff) != INTEGER_CST)
{ {
/* If the string has an internal zero byte (e.g., "foo\0bar"), we can't /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't
compute the offset to the following null if we don't know where to compute the offset to the following null if we don't know where to
start searching for it. */ start searching for it. */
int i; if (string_length (ptr, eltsize, maxelts) < maxelts)
{
for (i = 0; i < max; i++) /* Return when an embedded null character is found. */
if (ptr[i] == 0)
return NULL_TREE; return NULL_TREE;
}
/* We don't know the starting offset, but we do know that the string /* We don't know the starting offset, but we do know that the string
has no internal zero bytes. We can assume that the offset falls has no internal zero bytes. We can assume that the offset falls
...@@ -575,27 +615,31 @@ c_strlen (tree src, int only_value) ...@@ -575,27 +615,31 @@ c_strlen (tree src, int only_value)
and return that. This would perhaps not be valid if we were dealing and return that. This would perhaps not be valid if we were dealing
with named arrays in addition to literal string constants. */ with named arrays in addition to literal string constants. */
return size_diffop_loc (loc, size_int (max), offset_node); return size_diffop_loc (loc, size_int (maxelts * eltsize), byteoff);
} }
/* Offset from the beginning of the string in elements. */
HOST_WIDE_INT eltoff;
/* We have a known offset into the string. Start searching there for /* We have a known offset into the string. Start searching there for
a null character if we can represent it as a single HOST_WIDE_INT. */ a null character if we can represent it as a single HOST_WIDE_INT. */
if (offset_node == 0) if (byteoff == 0)
offset = 0; eltoff = 0;
else if (! tree_fits_shwi_p (offset_node)) else if (! tree_fits_shwi_p (byteoff))
offset = -1; eltoff = -1;
else else
offset = tree_to_shwi (offset_node); eltoff = tree_to_shwi (byteoff) / eltsize;
/* If the offset is known to be out of bounds, warn, and call strlen at /* If the offset is known to be out of bounds, warn, and call strlen at
runtime. */ runtime. */
if (offset < 0 || offset > max) if (eltoff < 0 || eltoff > maxelts)
{ {
/* Suppress multiple warnings for propagated constant strings. */ /* Suppress multiple warnings for propagated constant strings. */
if (only_value != 2 if (only_value != 2
&& !TREE_NO_WARNING (src)) && !TREE_NO_WARNING (src))
{ {
warning_at (loc, 0, "offset outside bounds of constant string"); warning_at (loc, 0, "offset %qwi outside bounds of constant string",
eltoff);
TREE_NO_WARNING (src) = 1; TREE_NO_WARNING (src) = 1;
} }
return NULL_TREE; return NULL_TREE;
...@@ -605,9 +649,12 @@ c_strlen (tree src, int only_value) ...@@ -605,9 +649,12 @@ c_strlen (tree src, int only_value)
constructed with build_string will have nulls appended, we win even constructed with build_string will have nulls appended, we win even
if we get handed something like (char[4])"abcd". if we get handed something like (char[4])"abcd".
Since OFFSET is our starting index into the string, no further Since ELTOFF is our starting index into the string, no further
calculation is needed. */ calculation is needed. */
return ssize_int (strlen (ptr + offset)); unsigned len = string_length (ptr + eltoff * eltsize, eltsize,
maxelts - eltoff);
return ssize_int (len);
} }
/* Return a constant integer corresponding to target reading /* Return a constant integer corresponding to target reading
......
2016-10-24 Martin Sebor <msebor@redhat.com>
PR middle-end/77735
* gcc.dg/tree-ssa/builtin-sprintf-2.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: Adjust/relax.
* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: XFAIL for LP64 only.
* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.
2016-10-24 Richard Biener <rguenther@suse.de> 2016-10-24 Richard Biener <rguenther@suse.de>
PR testsuite/71491 PR testsuite/71491
......
/* Test to verify that the return value of calls to __builtin_sprintf /* Test to verify that the return value of calls to __builtin_sprintf
is not folded if the call has undefined behavior even if it would is not folded if the call isn't fully specified, even if it would
otherwise produce a known number of bytes on output, and that if otherwise produce a known number of bytes on output, and that if
the return value is in a known range the range is not made the return value is in a known range the range is not made
available to subsequent passes and doesn't affect branching and available to subsequent passes and doesn't affect branching and
...@@ -22,7 +22,8 @@ char buf8k [8192]; ...@@ -22,7 +22,8 @@ char buf8k [8192];
#define CAT(a, b) concat (a, b) #define CAT(a, b) concat (a, b)
#define EQL(expect, size, fmt, ...) \ #define EQL(expect, size, fmt, ...) \
void CAT (test_on_line_, __LINE__)(void) \ void __attribute__ ((noinline, noclone)) \
CAT (test_on_line_, __LINE__)(void) \
{ \ { \
if (!LINE || LINE == __LINE__) \ if (!LINE || LINE == __LINE__) \
{ \ { \
...@@ -37,7 +38,8 @@ char buf8k [8192]; ...@@ -37,7 +38,8 @@ char buf8k [8192];
to the formatted function is not treated as a constant or made available to the formatted function is not treated as a constant or made available
to subsequent optimization passes. */ to subsequent optimization passes. */
#define RNG(min, max, size, fmt, ...) \ #define RNG(min, max, size, fmt, ...) \
void CAT (test_on_line_, __LINE__)(void) \ void __attribute__ ((noinline, noclone)) \
CAT (test_on_line_, __LINE__)(void) \
{ \ { \
if (!LINE || LINE == __LINE__) \ if (!LINE || LINE == __LINE__) \
{ \ { \
...@@ -52,6 +54,9 @@ extern int i; ...@@ -52,6 +54,9 @@ extern int i;
extern long li; extern long li;
extern char *str; extern char *str;
extern double d;
extern long double ld;
/* Verify that overflowing the destination object disables the return /* Verify that overflowing the destination object disables the return
value optimization. */ value optimization. */
EQL (0, 0, "%c", ' '); EQL (0, 0, "%c", ' ');
...@@ -78,7 +83,15 @@ enum { imax2 = (INT_MAX / 2) * 2 }; ...@@ -78,7 +83,15 @@ enum { imax2 = (INT_MAX / 2) * 2 };
EQL (imax2, -1, "%*c%*c", INT_MAX / 2, 'x', INT_MAX / 2, 'y'); EQL (imax2, -1, "%*c%*c", INT_MAX / 2, 'x', INT_MAX / 2, 'y');
/* Verify that range information for calls that overflow the destination /* Verify that range information for calls that overflow the destination
isn't available. */ isn't available.
+-- lower bound of the tested range
| +-- upper bound of the tested range
| | +-- size of destination buffer
| | | +-- format string
| | | | +-- argument(s)
| | | | |
V V V V V */
RNG (0, 0, 0, "%hhi", i) RNG (0, 0, 0, "%hhi", i)
RNG (0, 0, 1, "%hhi", i) RNG (0, 0, 1, "%hhi", i)
RNG (0, 1, 1, "%hhi", i) RNG (0, 1, 1, "%hhi", i)
...@@ -190,11 +203,36 @@ RNG (0, 10, 10, "%i", i) ...@@ -190,11 +203,36 @@ RNG (0, 10, 10, "%i", i)
#endif #endif
/* Verify that the output of a "%a" directive with no precision is not
considered constant or within a known range (the number of digits
after the decimal point is unspecified in this case). The hardcoded
ranges correspond to Glibc values. */
RNG (6, 6, 7, "%a", 0.0) /* Glibc output: "0x0p+0" */
RNG (6, 6, 7, "%a", d)
RNG (6, 6, 7, "%.4096a", d)
RNG (6, 6, 7, "%La", 0.0L) /* Glibc output: "0x0p+0" */
RNG (6, 6, 7, "%La", ld)
RNG (6, 6, 7, "%.4096La", ld)
/* Verify that the result of formatting an unknown string isn't optimized
into a non-negative range. The string could be longer that 4,095 bytes,
resulting in the formatting function having undefined behavior (and
returning a negative value as Glibc can for some directives). */
RNG (0, INT_MAX, -1, "%-s", str);
/* Verify the result of a conditional expression involving a string /* Verify the result of a conditional expression involving a string
literal and an unknown string isn't optimized. */ literal and an unknown string isn't optimized. */
RNG (0, 1, 4, "%-s", i ? str : "123"); RNG (0, 1, 4, "%-s", i ? str : "123");
RNG (0, 1, 4, "%-s", i ? "123" : str); RNG (0, 1, 4, "%-s", i ? "123" : str);
/* Verfy that the output involving wide strings is not optimized
(the output is actually bounded by a function of MB_LEN_MAX
which should be at least 6 to accommodate UTF-8 but this isn't
implemented yet). */
RNG (0, 5, 7, "%ls", L"1");
RNG (0, 6, 8, "%s%ls", "1", L"2");
/* Verify that no call to abort has been eliminated and that each call /* 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). 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 This latter test tries to verify that the test preceding the call to
...@@ -214,5 +252,5 @@ RNG (0, 1, 4, "%-s", i ? "123" : str); ...@@ -214,5 +252,5 @@ RNG (0, 1, 4, "%-s", i ? "123" : str);
*/ */
/* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 105 "optimized" { target { ilp32 || lp64 } } } } */ /* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 114 "optimized" { target { ilp32 || lp64 } } } } */
/* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 74 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */ /* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 83 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */
...@@ -37,6 +37,18 @@ typedef __WINT_TYPE__ wint_t; ...@@ -37,6 +37,18 @@ typedef __WINT_TYPE__ wint_t;
typedef unsigned char UChar; typedef unsigned char UChar;
/* Constants used to verify the pass can determine their values even
without optimization. */
const int cst0 = 0;
const int cst1 = 1;
const int cst10 = 10;
/* Initialized global variables used to verify that the pass doesn't
use their initial values (they could be modified by calls to other
functions). */
int var0 = 0;
int var10 = 10;
const char s0[] = ""; const char s0[] = "";
const char s1[] = "1"; const char s1[] = "1";
const char s2[] = "12"; const char s2[] = "12";
...@@ -372,6 +384,13 @@ void test_sprintf_chk_s_const (void) ...@@ -372,6 +384,13 @@ void test_sprintf_chk_s_const (void)
T (3, "%.0ls", L"1"); T (3, "%.0ls", L"1");
T (3, "%.1ls", L"1"); T (3, "%.1ls", L"1");
T (3, "%.2ls", L"1"); T (3, "%.2ls", L"1");
T (3, "%ls", L"12");
T (3, "%ls", L"123"); /* { dg-warning "nul past the end" } */
T (3, "%.0ls", L"123");
T (3, "%.1ls", L"123");
T (3, "%.2ls", L"123");
T (3, "%.3ls", L"123"); /* { dg-warning "nul past the end" } */
} }
/* Exercise the "%hhd", "%hhi", "%hho", "%hhu", and "%hhx" directives /* Exercise the "%hhd", "%hhi", "%hho", "%hhu", and "%hhx" directives
...@@ -382,7 +401,9 @@ void test_sprintf_chk_hh_const (void) ...@@ -382,7 +401,9 @@ void test_sprintf_chk_hh_const (void)
T (-1, "%hhd", 0); T (-1, "%hhd", 0);
T (1, "%hhd", 0); /* { dg-warning "nul past the end" } */ T (1, "%hhd", 0); /* { dg-warning "nul past the end" } */
T (1, "%hhd", cst0); /* { dg-warning "nul past the end" } */
T (1, "%hhd", 1); /* { dg-warning "nul past the end" } */ T (1, "%hhd", 1); /* { dg-warning "nul past the end" } */
T (1, "%hhd", cst1); /* { dg-warning "nul past the end" } */
T (1, "%hhd", -1); /* { dg-warning "into a region" } */ T (1, "%hhd", -1); /* { dg-warning "into a region" } */
T (1, "%+hhd", 0); /* { dg-warning "into a region" } */ T (1, "%+hhd", 0); /* { dg-warning "into a region" } */
T (1, "%+hhd", 1); /* { dg-warning "into a region" } */ T (1, "%+hhd", 1); /* { dg-warning "into a region" } */
...@@ -402,6 +423,7 @@ void test_sprintf_chk_hh_const (void) ...@@ -402,6 +423,7 @@ void test_sprintf_chk_hh_const (void)
T (2, "%+hhi", 9); /* { dg-warning "nul past the end" } */ T (2, "%+hhi", 9); /* { dg-warning "nul past the end" } */
T (2, "%-hhi", 9); T (2, "%-hhi", 9);
T (2, "%hhi", 10); /* { dg-warning "nul past the end" } */ T (2, "%hhi", 10); /* { dg-warning "nul past the end" } */
T (2, "%hhi", cst10); /* { dg-warning "nul past the end" } */
T (2, "%hhi", -1); /* { dg-warning "nul past the end" } */ T (2, "%hhi", -1); /* { dg-warning "nul past the end" } */
T (2, "% hhi", -1); /* { dg-warning "nul past the end" } */ T (2, "% hhi", -1); /* { dg-warning "nul past the end" } */
T (2, "%+hhi", -1); /* { dg-warning "nul past the end" } */ T (2, "%+hhi", -1); /* { dg-warning "nul past the end" } */
...@@ -863,6 +885,35 @@ void test_sprintf_chk_z_const (void) ...@@ -863,6 +885,35 @@ void test_sprintf_chk_z_const (void)
T ( 2, "%zu", (size_t)10); /* { dg-warning "nul past the end" } */ T ( 2, "%zu", (size_t)10); /* { dg-warning "nul past the end" } */
} }
void test_sprintf_chk_a_const (void)
{
T (-1, "%a", 0.0);
T (-1, "%la", 0.0);
/* The least number of bytes on output is 6 for "0x0p+0". When precision
is missing the number of digits after the decimal point isn't fully
specified by C (it seems like a defect). */
T (0, "%a", 0.0); /* { dg-warning "into a region" } */
T (0, "%la", 0.0); /* { dg-warning "into a region" } */
T (1, "%a", 0.0); /* { dg-warning "into a region" } */
T (2, "%a", 0.0); /* { dg-warning "into a region" } */
T (3, "%a", 0.0); /* { dg-warning "into a region" } */
T (4, "%a", 0.0); /* { dg-warning "into a region" } */
T (5, "%a", 0.0); /* { dg-warning "into a region" } */
T (6, "%a", 0.0); /* { dg-warning "writing a terminating nul" } */
T (7, "%a", 0.0);
T (0, "%.0a", 0.0); /* { dg-warning "into a region" } */
T (0, "%.0la", 0.0); /* { dg-warning "into a region" } */
T (1, "%.0a", 0.0); /* { dg-warning "into a region" } */
T (2, "%.0a", 0.0); /* { dg-warning "into a region" } */
T (3, "%.0a", 0.0); /* { dg-warning "into a region" } */
T (4, "%.0a", 0.0); /* { dg-warning "into a region" } */
T (5, "%.0a", 0.0); /* { dg-warning "into a region" } */
T (6, "%.0a", 0.0); /* { dg-warning "writing a terminating nul" } */
T (7, "%.0a", 0.0);
}
void test_sprintf_chk_e_const (void) void test_sprintf_chk_e_const (void)
{ {
T (-1, "%E", 0.0); T (-1, "%E", 0.0);
...@@ -893,20 +944,23 @@ void test_sprintf_chk_e_const (void) ...@@ -893,20 +944,23 @@ void test_sprintf_chk_e_const (void)
T ( 6, "%.0e", 1.0); T ( 6, "%.0e", 1.0);
/* The actual output of the following directives depends on the rounding /* The actual output of the following directives depends on the rounding
mode. Verify that the warning correctly reflects that. */ mode. Verify that the warning correctly reflects that. At level 1,
T (12, "%e", 9.999999e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */ since the minimum number of bytes output by the directive fits the
T (12, "%e", 9.9999994e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */ space the directive itself isn't diagnosed but the terminating nul
T (12, "%e", 9.9999995e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */ is. The directive is diagnosed at level 2. */
T (12, "%e", 9.9999996e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%e", 9.999999e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999997e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%e", 9.9999994e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999998e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%e", 9.9999995e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999996e+99); /* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999994e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%e", 9.9999997e+99); /* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999995e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%e", 9.9999998e+99); /* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999996e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%Le", 9.9999997e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%Le", 9.9999994e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999998e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%Le", 9.9999995e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999999e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */ T (12, "%Le", 9.9999996e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999997e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999998e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999999e+99L);/* { dg-warning "terminating nul" } */
} }
/* At -Wformat-length level 1 unknown numbers are assumed to have /* At -Wformat-length level 1 unknown numbers are assumed to have
...@@ -936,12 +990,14 @@ void test_sprintf_chk_hh_nonconst (int a) ...@@ -936,12 +990,14 @@ void test_sprintf_chk_hh_nonconst (int a)
{ {
T (-1, "%hhd", a); T (-1, "%hhd", a);
T (0, "%hhd", a); /* { dg-warning "into a region" } */ T (0, "%hhd", a); /* { dg-warning ".%hhd. directive writing between 1 and . bytes into a region of size 0" } */
T (0, "%hhi", var0); /* { dg-warning "into a region" } */
T (0, "%hhi", a); /* { dg-warning "into a region" } */ T (0, "%hhi", a); /* { dg-warning "into a region" } */
T (0, "%hhu", a); /* { dg-warning "into a region" } */ T (0, "%hhu", a); /* { dg-warning "into a region" } */
T (0, "%hhx", a); /* { dg-warning "into a region" } */ T (0, "%hhx", a); /* { dg-warning "into a region" } */
T (1, "%hhd", a); /* { dg-warning "nul past the end" } */ T (1, "%hhd", a); /* { dg-warning "nul past the end" } */
T (1, "%hhd", var0); /* { dg-warning "nul past the end" } */
T (1, "%hhi", a); /* { dg-warning "nul past the end" } */ T (1, "%hhi", a); /* { dg-warning "nul past the end" } */
T (1, "%hhu", a); /* { dg-warning "nul past the end" } */ T (1, "%hhu", a); /* { dg-warning "nul past the end" } */
T (1, "%hhx", a); /* { dg-warning "nul past the end" } */ T (1, "%hhx", a); /* { dg-warning "nul past the end" } */
...@@ -954,19 +1010,23 @@ void test_sprintf_chk_hh_nonconst (int a) ...@@ -954,19 +1010,23 @@ void test_sprintf_chk_hh_nonconst (int a)
T (1, "%-hhi", a); /* { dg-warning "nul past the end" } */ T (1, "%-hhi", a); /* { dg-warning "nul past the end" } */
T (2, "%hhd", a); T (2, "%hhd", a);
T (2, "%hhd", var0);
T (2, "%hhd", var10);
T (2, "%hhi", a); T (2, "%hhi", a);
T (2, "%hho", a); T (2, "%hho", a);
T (2, "%hhu", a); T (2, "%hhu", a);
T (2, "%hhx", a); T (2, "%hhx", a);
T (2, "% hhd", a); /* { dg-warning "nul past the end" } */ T (2, "% hhd", a); /* { dg-warning "nul past the end" } */
T (2, "% hhd", var0); /* { dg-warning "nul past the end" } */
T (2, "% hhd", var10); /* { dg-warning "nul past the end" } */
T (2, "% hhi", a); /* { dg-warning "nul past the end" } */ T (2, "% hhi", a); /* { dg-warning "nul past the end" } */
T (2, "% hho", a); /* { dg-warning ". . flag used with .%o." } */ T (2, "% hho", a); /* { dg-warning ". . flag used with .%o." } */
T (2, "% hhu", a); /* { dg-warning ". . flag used with .%u." } */ T (2, "% hhu", a); /* { dg-warning ". . flag used with .%u." } */
T (2, "% hhx", a); /* { dg-warning ". . flag used with .%x." } */ T (2, "% hhx", a); /* { dg-warning ". . flag used with .%x." } */
T (2, "#%hho", a); /* { dg-warning "nul past the end" } */ T (2, "%#hho", a); /* { dg-warning "nul past the end" } */
T (2, "#%hhx", a); /* { dg-warning "nul past the end" } */ T (2, "%#hhx", a); /* { dg-warning ".%#hhx. directive writing between 3 and . bytes into a region of size 2" } */
T (3, "%2hhd", a); T (3, "%2hhd", a);
T (3, "%2hhi", a); T (3, "%2hhi", a);
...@@ -1086,8 +1146,7 @@ void test_sprintf_chk_e_nonconst (double d) ...@@ -1086,8 +1146,7 @@ void test_sprintf_chk_e_nonconst (double d)
T ( 1, "%e", d); /* { dg-warning "into a region" } */ T ( 1, "%e", d); /* { dg-warning "into a region" } */
T ( 2, "%e", d); /* { dg-warning "into a region" } */ T ( 2, "%e", d); /* { dg-warning "into a region" } */
T ( 3, "%e", d); /* { dg-warning "into a region" } */ T ( 3, "%e", d); /* { dg-warning "into a region" } */
T (12, "%e", d); /* { dg-warning "past the end" } */ T (12, "%e", d); /* { dg-warning "nul past the end" } */
T (12, "%e", d); /* { dg-warning "past the end" } */
T (13, "%E", d); /* 1.000000E+00 */ T (13, "%E", d); /* 1.000000E+00 */
T (13, "%e", d); T (13, "%e", d);
T (14, "%E", d); T (14, "%E", d);
......
...@@ -47,10 +47,13 @@ void test_s_const (void) ...@@ -47,10 +47,13 @@ void test_s_const (void)
T (1, "%*ls", 0, L"\0"); T (1, "%*ls", 0, L"\0");
T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */ T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */
T (1, "%ls", L"1"); /* { dg-warning "nul past the end" } */ T (1, "%ls", L"1"); /* { dg-warning "directive writing between 1 and 6 bytes into a region of size 1" } */
T (1, "%.0ls", L"1"); T (1, "%.0ls", L"1");
T (2, "%.0ls", L"1"); T (2, "%.0ls", L"1");
T (2, "%.1ls", L"1"); T (2, "%.1ls", L"1");
T (2, "%.2ls", L"1"); /* { dg-warning "nul past the end" } */
T (2, "%.3ls", L"1"); /* { dg-warning "directive writing between 1 and 3 bytes into a region of size 2" } */
T (2, "%.2ls", L"12"); /* { dg-warning "nul past the end" } */
/* The "%.2ls" directive below will write at a minimum 1 byte (because /* The "%.2ls" directive below will write at a minimum 1 byte (because
L"1" is known and can be assumed to convert to at least one multibyte L"1" is known and can be assumed to convert to at least one multibyte
...@@ -64,6 +67,12 @@ void test_s_const (void) ...@@ -64,6 +67,12 @@ void test_s_const (void)
T (2, "%.0ls", L"1"); T (2, "%.0ls", L"1");
T (2, "%.1ls", L"1"); T (2, "%.1ls", L"1");
T (3, "%.2ls", L"1"); T (3, "%.2ls", L"1");
T (3, "%.2ls", L"12");
T (3, "%.3ls", L"12"); /* { dg-warning "nul past the end" } */
T (4, "%.3ls", L"123");
T (4, "%.4ls", L"123"); /* { dg-warning "nul past the end" } */
T (4, "%.5ls", L"123"); /* { dg-warning "directive writing between 3 and 5 bytes into a region of size 4" } */
T (4, "%.6ls", L"123"); /* { dg-warning "directive writing between 3 and 6 bytes into a region of size 4" } */
} }
...@@ -86,7 +95,9 @@ void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a) ...@@ -86,7 +95,9 @@ void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a)
T (1, "%.0s", s); T (1, "%.0s", s);
T (1, "%.1s", s); /* { dg-warning "writing a terminating nul" } */ T (1, "%.1s", s); /* { dg-warning "writing a terminating nul" } */
T (1, "%ls", ws); /* { dg-warning "writing a terminating nul" } */ T (1, "%.0ls", ws);
T (1, "%.1ls", ws); /* { dg-warning "writing a terminating nul" } */
T (1, "%ls", ws); /* { dg-warning "writing a terminating nul" } */
/* Verify that the size of the array is used in lieu of its length. /* Verify that the size of the array is used in lieu of its length.
The minus sign disables GCC's sprintf to strcpy transformation. */ The minus sign disables GCC's sprintf to strcpy transformation. */
......
...@@ -175,9 +175,9 @@ void test_sprintf_chk_range_schar (signed char *a) ...@@ -175,9 +175,9 @@ void test_sprintf_chk_range_schar (signed char *a)
T ( 3, "%i", R ( 0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */ T ( 3, "%i", R ( 0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
/* The following call may write as few as 3 bytes and as many as 5. /* The following call may write as few as 3 bytes and as many as 5.
It's judgment call how best to diagnose it to make the potential It's a judgment call how best to diagnose it to make the potential
problem clear. */ problem clear. */
T ( 3, "%i%i", R (1, 10), R (9, 10)); /* { dg-warning ".%i. directive writing between 1 and 2 bytes into a region of size 1" } */ T ( 3, "%i%i", R (1, 10), R (9, 10)); /* { dg-warning "may write a terminating nul past the end|.%i. directive writing between 1 and 2 bytes into a region of size 1" } */
T ( 4, "%i%i", R (10, 11), R (12, 13)); /* { dg-warning "nul past the end" } */ T ( 4, "%i%i", R (10, 11), R (12, 13)); /* { dg-warning "nul past the end" } */
...@@ -187,7 +187,7 @@ void test_sprintf_chk_range_schar (signed char *a) ...@@ -187,7 +187,7 @@ void test_sprintf_chk_range_schar (signed char *a)
T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 10)); /* { dg-warning "may write a terminating nul past the end" } */ T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 10)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */ T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 10), R (0, 9), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */ T ( 6, "%i_%i_%i", R (0, 10), R (0, 9), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 10)); /* { dg-warning ".%i. directive writing between 1 and 2 bytes into a region of size 1" } */ T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 10)); /* { dg-warning "may write a terminating nul past the end|.%i. directive writing between 1 and 2 bytes into a region of size 1" } */
} }
void test_sprintf_chk_range_uchar (unsigned char *a, unsigned char *b) void test_sprintf_chk_range_uchar (unsigned char *a, unsigned char *b)
......
...@@ -3,31 +3,91 @@ ...@@ -3,31 +3,91 @@
extern int sprintf (char*, const char*, ...); extern int sprintf (char*, const char*, ...);
char dst [8]; char dst [3];
void test (void) void test (void)
{ {
sprintf (dst + 7, "%-s", "1"); /* Verify thet the caret points to the (invisible) nul character
/* { dg-warning "writing a terminating nul past the end of the destination" "" { target *-*-* } 10 } at the end of the format string (i.e., its closing quote).
{ dg-message "format output 2 bytes into a destination of size 1" "" { target *-*-* } 10 } The redundant argument is there to get around GCC bug 77799. */
{ dg-begin-multiline-output "" } sprintf (dst + 2, "1", 0);
sprintf (dst + 7, "%-s", "1"); /* { dg-warning "writing a terminating nul past the end of the destination" "nul warning" { target *-*-* } .-1 }
~~^~ { dg-message "format output 2 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-end-multiline-output "" } { dg-begin-multiline-output "-Wformat output: redundant argument" }
{ dg-begin-multiline-output "" } sprintf (dst + 2, "1", 0);
sprintf (dst + 7, "%-s", "1"); ^~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "-Wformat-length output" }
sprintf (dst + 2, "1", 0);
~^
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" }
sprintf (dst + 2, "1", 0);
^~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
/* Verify thet the caret points at the first format character written
past the end of the destination. */
sprintf (dst, "1234", 0);
/* { dg-warning "writing format character .4. at offset 3 past the end of the destination" "nul warning" { target *-*-* } .-1 }
{ dg-message "format output 5 bytes into a destination of size 3" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat output: redundant argument" }
sprintf (dst, "1234", 0);
^~~~~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "-Wformat-length output" }
sprintf (dst, "1234", 0);
^
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" }
sprintf (dst, "1234", 0);
^~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
/* Verify thet the caret points at the first format character written
past the end of the destination and the rest of the format string
is underlined. */
sprintf (dst, "12345", 0);
/* { dg-warning "writing format character .4. at offset 3 past the end of the destination" "nul warning" { target *-*-* } .-1 }
{ dg-message "format output 6 bytes into a destination of size 3" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat output: redundant argument" }
sprintf (dst, "12345", 0);
^~~~~~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "-Wformat-length output" }
sprintf (dst, "12345", 0);
^~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" }
sprintf (dst, "12345", 0);
^~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
/* Same as above but with a directive. The minus flag is used to
get around GCC bug 77671. */
sprintf (dst + 2, "%-s", "1");
/* { dg-warning "writing a terminating nul past the end of the destination" "warning" { target *-*-* } .-1 }
{ dg-message "format output 2 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat-length output" }
sprintf (dst + 2, "%-s", "1");
~~~^
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" }
sprintf (dst + 2, "%-s", "1");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */ { dg-end-multiline-output "" } */
sprintf (dst + 7, "%-s", "abcd"); sprintf (dst + 2, "%-s", "abcd");
/* { dg-warning ".%-s. directive writing 4 bytes into a region of size 1" "" { target *-*-* } 22 } /* { dg-warning ".%-s. directive writing 4 bytes into a region of size 1" "warning" { target *-*-* } .-1 }
{ dg-message "format output 5 bytes into a destination of size 1" "" { target *-*-* } 22 } { dg-message "format output 5 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "-Wformat-length output" }
sprintf (dst + 7, "%-s", "abcd"); sprintf (dst + 2, "%-s", "abcd");
^~~ ~~~~~~ ^~~ ~~~~~~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "note" }
sprintf (dst + 7, "%-s", "abcd"); sprintf (dst + 2, "%-s", "abcd");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */ { dg-end-multiline-output "" } */
} }
/* { dg-prune-output "too many arguments for format" } */
...@@ -88,5 +88,5 @@ void fllong (long j, char *p) ...@@ -88,5 +88,5 @@ void fllong (long j, char *p)
if (k > 999) if (k > 999)
return; return;
snprintf (p, 4, "%3llu", k); /* { dg-bogus "" "unsigned long long" { xfail *-*-* } } */ snprintf (p, 4, "%3llu", k); /* { dg-bogus "" "unsigned long long" { xfail lp64 } } */
} }
...@@ -359,23 +359,27 @@ test_x (unsigned char uc, unsigned short us, unsigned ui) ...@@ -359,23 +359,27 @@ test_x (unsigned char uc, unsigned short us, unsigned ui)
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_a_double (void) test_a_double (double d)
{ {
EQL ( 6, 7, "%a", 0.0); /* 0x0p+0 */ EQL ( 6, 7, "%.0a", 0.0); /* 0x0p+0 */
EQL ( 6, 7, "%a", 1.0); /* 0x8p-3 */ EQL ( 6, 7, "%.0a", 1.0); /* 0x8p-3 */
EQL ( 6, 7, "%a", 2.0); /* 0x8p-2 */ EQL ( 6, 7, "%.0a", 2.0); /* 0x8p-2 */
EQL ( 8, 9, "%.1a", 3.0); /* 0xc.0p-2 */ EQL ( 8, 9, "%.1a", 3.0); /* 0xc.0p-2 */
EQL ( 9, 10, "%.2a", 4.0); /* 0xa.00p-1 */ EQL ( 9, 10, "%.2a", 4.0); /* 0x8.00p-1 */
EQL (10, 11, "%.3a", 5.0); /* 0xa.000p-1 */
/* d is in [ 0, -DBL_MAX ] */
RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */
RNG ( 6, 12, 13, "%.1a", d); /* 0x0p+0 ... -0x2.0p+1023 */
RNG ( 6, 13, 14, "%.2a", d); /* 0x0p+0 ... -0x2.00p+1023 */
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_a_long_double (void) test_a_long_double (void)
{ {
EQL ( 6, 7, "%La", 0.0L); /* 0x0p+0 */ EQL ( 6, 7, "%.0La", 0.0L); /* 0x0p+0 */
EQL ( 6, 7, "%La", 1.0L); /* 0x8p-3 */ EQL ( 6, 7, "%.0La", 1.0L); /* 0x8p-3 */
EQL ( 6, 7, "%La", 2.0L); /* 0x8p-2 */ EQL ( 6, 7, "%.0La", 2.0L); /* 0x8p-2 */
EQL ( 8, 9, "%.1La", 3.0L); /* 0xc.0p-2 */ EQL ( 8, 9, "%.1La", 3.0L); /* 0xc.0p-2 */
EQL ( 9, 10, "%.2La", 4.0L); /* 0xa.00p-1 */ EQL ( 9, 10, "%.2La", 4.0L); /* 0xa.00p-1 */
} }
...@@ -525,7 +529,7 @@ int main (void) ...@@ -525,7 +529,7 @@ int main (void)
test_d_i (0xdeadbeef, 0xdeadbeefL); test_d_i (0xdeadbeef, 0xdeadbeefL);
test_x ('?', 0xdead, 0xdeadbeef); test_x ('?', 0xdead, 0xdeadbeef);
test_a_double (); test_a_double (0.0);
test_e_double (); test_e_double ();
test_f_double (); test_f_double ();
......
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