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
This commit is contained in:
Martin Sebor 2016-10-24 16:53:20 +00:00 committed by Martin Sebor
parent 94caf86019
commit 1eb4547b10
11 changed files with 558 additions and 199 deletions

View file

@ -1,3 +1,25 @@
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>
* config/i386/i386.c (ix86_in_large_data_p, ix86_expand_builtin): Use

View file

@ -503,9 +503,44 @@ get_pointer_alignment (tree exp)
return align;
}
/* Compute the length of a C string. TREE_STRING_LENGTH is not the right
way, because it could contain a zero byte in the middle.
TREE_STRING_LENGTH is the size of the character array, not the string.
/* Return the number of non-zero elements in the sequence
[ PTR, PTR + MAXELTS ) where each element's size is ELTSIZE bytes.
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
into the instruction stream and zero if it is going to be expanded.
@ -526,12 +561,6 @@ get_pointer_alignment (tree exp)
tree
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);
if (TREE_CODE (src) == COND_EXPR
&& (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
@ -548,25 +577,36 @@ c_strlen (tree src, int only_value)
&& (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
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)
return NULL_TREE;
max = TREE_STRING_LENGTH (src) - 1;
ptr = TREE_STRING_POINTER (src);
/* Determine the size of the string element. */
unsigned eltsize
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src))));
if (offset_node && TREE_CODE (offset_node) != INTEGER_CST)
/* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible
length of SRC. */
unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1;
/* 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
compute the offset to the following null if we don't know where to
start searching for it. */
int i;
for (i = 0; i < max; i++)
if (ptr[i] == 0)
if (string_length (ptr, eltsize, maxelts) < maxelts)
{
/* Return when an embedded null character is found. */
return NULL_TREE;
}
/* 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
@ -575,27 +615,31 @@ c_strlen (tree src, int only_value)
and return that. This would perhaps not be valid if we were dealing
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
a null character if we can represent it as a single HOST_WIDE_INT. */
if (offset_node == 0)
offset = 0;
else if (! tree_fits_shwi_p (offset_node))
offset = -1;
if (byteoff == 0)
eltoff = 0;
else if (! tree_fits_shwi_p (byteoff))
eltoff = -1;
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
runtime. */
if (offset < 0 || offset > max)
if (eltoff < 0 || eltoff > maxelts)
{
/* Suppress multiple warnings for propagated constant strings. */
if (only_value != 2
&& !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;
}
return NULL_TREE;
@ -605,9 +649,12 @@ c_strlen (tree src, int only_value)
constructed with build_string will have nulls appended, we win even
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. */
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

View file

@ -79,6 +79,11 @@ along with GCC; see the file COPYING3. If not see
#include "substring-locations.h"
#include "diagnostic.h"
/* The likely worst case value of MB_LEN_MAX for the target, large enough
for UTF-8. Ideally, this would be obtained by a target hook if it were
to be used for optimization but it's good enough as is for warnings. */
#define target_mb_len_max 6
namespace {
const pass_data pass_data_sprintf_length = {
@ -150,17 +155,30 @@ struct format_result
unsigned HOST_WIDE_INT number_chars_max;
/* True when the range given by NUMBER_CHARS_MIN and NUMBER_CHARS_MAX
is the output of all directives determined to be bounded to some
subrange of their types or possible lengths, false otherwise.
can be relied on for value range propagation, false otherwise.
This means that BOUNDED must not be set if the number of bytes
produced by any directive is unspecified or implementation-
defined (unless the implementation's behavior is known and
determined via a target hook).
Note that BOUNDED only implies that the length of a function's
output is known to be within some range, not that it's constant
and a candidate for folding. */
and a candidate for string folding. BOUNDED is a stronger
guarantee than KNOWNRANGE. */
bool bounded;
/* True when the range above is obtained from known values of
directive arguments or their bounds and not the result of
heuristics that depend on warning levels. It is used to
issue stricter diagnostics in cases where strings of unknown
lengths are bounded by the arrays they are determined to
refer to. KNOWNRANGE must not be used to set the range of
the return value of a call. */
bool knownrange;
/* True when the output of the formatted call is constant (and
thus a candidate for string constant folding). This is rare
and typically requires that the arguments of all directives
are also constant. Constant implies bounded. */
are also constant. CONSTANT implies BOUNDED. */
bool constant;
/* True if no individual directive resulted in more than 4095 bytes
@ -216,15 +234,31 @@ target_int_min ()
return int_min;
}
/* Return the value of INT_MAX for the target. */
/* Return the largest value for TYPE on the target. */
static unsigned HOST_WIDE_INT
target_max_value (tree type)
{
const unsigned HOST_WIDE_INT max_value
= HOST_WIDE_INT_M1U >> (HOST_BITS_PER_WIDE_INT
- TYPE_PRECISION (type) + 1);
return max_value;
}
/* Return the value of INT_MAX for the target. */
static inline unsigned HOST_WIDE_INT
target_int_max ()
{
const unsigned HOST_WIDE_INT int_max
= HOST_WIDE_INT_M1U >> (HOST_BITS_PER_WIDE_INT
- TYPE_PRECISION (integer_type_node) + 1);
return int_max;
return target_max_value (integer_type_node);
}
/* Return the value of SIZE_MAX for the target. */
static inline unsigned HOST_WIDE_INT
target_size_max ()
{
return target_max_value (size_type_node);
}
/* Return the constant initial value of DECL if available or DECL
@ -412,6 +446,12 @@ struct result_range
struct fmtresult
{
fmtresult ()
: argmin (), argmax (), knownrange (), bounded (), constant ()
{
range.min = range.max = HOST_WIDE_INT_MAX;
}
/* The range a directive's argument is in. */
tree argmin, argmax;
@ -419,11 +459,17 @@ struct fmtresult
results in on output for an argument in the range above. */
result_range range;
/* True when the range above is obtained from a known value of
a directive's argument or its bounds and not the result of
heuristics that depend on warning levels. */
bool knownrange;
/* True when the range is the result of an argument determined
to be bounded to a subrange of its type or value (such as by
value range propagation or the width of the formt directive),
false otherwise. */
bool bounded;
/* True when the output of a directive is constant. This is rare
and typically requires that the argument(s) of the directive
are also constant (such as determined by constant propagation,
@ -730,7 +776,7 @@ format_integer (const conversion_spec &, tree);
static fmtresult
format_pointer (const conversion_spec &spec, tree arg)
{
fmtresult res = fmtresult ();
fmtresult res;
/* Determine the target's integer format corresponding to "%p". */
const char *flags;
@ -869,14 +915,7 @@ format_integer (const conversion_spec &spec, tree arg)
break;
default:
{
fmtresult res = fmtresult ();
res.range.min = HOST_WIDE_INT_MAX;
res.range.max = HOST_WIDE_INT_MAX;
res.bounded = false;
res.constant = false;
return res;
}
return fmtresult ();
}
/* The type of the argument to the directive, either deduced from
@ -897,12 +936,13 @@ format_integer (const conversion_spec &spec, tree arg)
{
/* The minimum and maximum number of bytes produced by
the directive. */
fmtresult res = fmtresult ();
fmtresult res;
/* When a constant argument has been provided use its value
rather than type to determine the length of the output. */
res.bounded = true;
res.constant = true;
res.knownrange = true;
/* Base to format the number in. */
int base;
@ -975,12 +1015,10 @@ format_integer (const conversion_spec &spec, tree arg)
/* Don't bother with invalid arguments since they likely would
have already been diagnosed, and disable any further checking
of the format string by returning [-1, -1]. */
fmtresult res = fmtresult ();
res.range.min = res.range.max = HOST_WIDE_INT_M1U;
return res;
return fmtresult ();
}
fmtresult res = fmtresult ();
fmtresult res;
/* Using either the range the non-constant argument is in, or its
type (either "formal" or actual), create a range of values that
@ -1029,9 +1067,10 @@ format_integer (const conversion_spec &spec, tree arg)
argmax = res.argmax;
}
/* The argument is bounded by the range of values determined
by Value Range Propagation. */
/* The argument is bounded by the known range of values
determined by Value Range Propagation. */
res.bounded = true;
res.knownrange = true;
}
else if (range_type == VR_ANTI_RANGE)
{
@ -1047,6 +1086,12 @@ format_integer (const conversion_spec &spec, tree arg)
if (is_gimple_assign (def))
{
tree_code code = gimple_assign_rhs_code (def);
if (code == INTEGER_CST)
{
arg = gimple_assign_rhs1 (def);
return format_integer (spec, arg);
}
if (code == NOP_EXPR)
argtype = TREE_TYPE (gimple_assign_rhs1 (def));
}
@ -1111,7 +1156,7 @@ format_integer (const conversion_spec &spec, tree arg)
SPEC the largest value in the real floating TYPE. */
static int
format_floating_max (tree type, char spec)
format_floating_max (tree type, char spec, int prec = -1)
{
machine_mode mode = TYPE_MODE (type);
@ -1136,9 +1181,21 @@ format_floating_max (tree type, char spec)
mpfr_init2 (x, rfmt->p);
mpfr_from_real (x, &rv, GMP_RNDN);
int n;
if (-1 < prec)
{
const char fmt[] = { '%', '.', '*', 'R', spec, '\0' };
n = mpfr_snprintf (NULL, 0, fmt, prec, x);
}
else
{
const char fmt[] = { '%', 'R', spec, '\0' };
int n = mpfr_snprintf (NULL, 0, fmt, x);
return n;
n = mpfr_snprintf (NULL, 0, fmt, x);
}
/* Return a value one greater to account for the leading minus sign. */
return n + 1;
}
/* Return a range representing the minimum and maximum number of bytes
@ -1170,19 +1227,11 @@ format_floating (const conversion_spec &spec, int width, int prec)
break;
default:
{
fmtresult res = fmtresult ();
res.range.min = HOST_WIDE_INT_MAX;
res.range.max = HOST_WIDE_INT_MAX;
res.bounded = false;
res.constant = false;
return res;
}
return fmtresult ();
}
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res = fmtresult ();
res.constant = false;
fmtresult res;
/* Log10 of of the maximum number of exponent digits for the type. */
int logexpdigs = 2;
@ -1206,13 +1255,11 @@ format_floating (const conversion_spec &spec, int width, int prec)
{
/* The minimum output is "0x.p+0". */
res.range.min = 6 + (prec > 0 ? prec : 0);
res.range.max = format_floating_max (type, 'a', prec);
/* Compute the maximum just once. */
static const int a_max[] = {
format_floating_max (double_type_node, 'a'),
format_floating_max (long_double_type_node, 'a')
};
res.range.max = a_max [ldbl];
/* The output of "%a" is fully specified only when precision
is explicitly specified. */
res.bounded = -1 < prec;
break;
}
@ -1229,6 +1276,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
included), plus the difference between the minimum exponent
of 2 and the maximum exponent for the type. */
res.range.max = res.range.min + !sign + logexpdigs - 2;
/* "%e" is fully specified and the range of bytes is bounded. */
res.bounded = true;
break;
}
@ -1245,6 +1295,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
format_floating_max (long_double_type_node, 'f')
};
res.range.max = f_max [ldbl];
/* "%f" is fully specified and the range of bytes is bounded. */
res.bounded = true;
break;
}
case 'G':
@ -1259,18 +1312,14 @@ format_floating (const conversion_spec &spec, int width, int prec)
format_floating_max (long_double_type_node, 'g')
};
res.range.max = g_max [ldbl];
/* "%g" is fully specified and the range of bytes is bounded. */
res.bounded = true;
break;
}
default:
{
fmtresult res = fmtresult ();
res.range.min = HOST_WIDE_INT_MAX;
res.range.max = HOST_WIDE_INT_MAX;
res.bounded = false;
res.constant = false;
return res;
}
return fmtresult ();
}
if (width > 0)
@ -1281,9 +1330,6 @@ format_floating (const conversion_spec &spec, int width, int prec)
res.range.max = width;
}
/* The argument is only considered bounded when the range of output
bytes is exact. */
res.bounded = res.range.min == res.range.max;
return res;
}
@ -1298,7 +1344,7 @@ format_floating (const conversion_spec &spec, tree arg)
int prec = -1;
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res = fmtresult ();
fmtresult res;
res.constant = arg && TREE_CODE (arg) == REAL_CST;
if (spec.have_width)
@ -1390,7 +1436,16 @@ format_floating (const conversion_spec &spec, tree arg)
*minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval);
}
res.bounded = res.range.min < target_int_max ();
/* The output of all directives except "%a" is fully specified
and so the result is bounded unless it exceeds INT_MAX.
For "%a" the output is fully specified only when precision
is explicitly specified. */
res.bounded = ((TOUPPER (spec.specifier) != 'A'
|| (0 <= prec && (unsigned) prec < target_int_max ()))
&& res.range.min < target_int_max ());
/* The range of output is known even if the result isn't bounded. */
res.knownrange = true;
return res;
}
@ -1405,14 +1460,7 @@ static fmtresult
get_string_length (tree str)
{
if (!str)
{
fmtresult res;
res.range.min = HOST_WIDE_INT_MAX;
res.range.max = HOST_WIDE_INT_MAX;
res.bounded = false;
res.constant = false;
return res;
}
return fmtresult ();
if (tree slen = c_strlen (str, 1))
{
@ -1421,6 +1469,7 @@ get_string_length (tree str)
res.range.min = res.range.max = tree_to_shwi (slen);
res.bounded = true;
res.constant = true;
res.knownrange = true;
return res;
}
@ -1434,7 +1483,7 @@ get_string_length (tree str)
if (lenrange [0] || lenrange [1])
{
fmtresult res = fmtresult ();
fmtresult res;
res.range.min = (tree_fits_uhwi_p (lenrange[0])
? tree_to_uhwi (lenrange[0]) : 1 < warn_format_length);
@ -1445,11 +1494,13 @@ get_string_length (tree str)
by STR are known to be bounded (though not necessarily by their
actual length but perhaps by their maximum possible length). */
res.bounded = res.range.max < target_int_max ();
res.knownrange = res.bounded;
/* Set RES.CONSTANT to false even though that may be overly
conservative in rare cases like: 'x ? a : b' where a and
b have the same lengths and consist of the same characters. */
res.constant = false;
return res;
}
@ -1479,7 +1530,7 @@ format_string (const conversion_spec &spec, tree arg)
prec = (TREE_CODE (spec.star_precision) == INTEGER_CST
? tree_to_shwi (spec.star_precision) : -1);
fmtresult res = fmtresult ();
fmtresult res;
/* The maximum number of bytes for an unknown wide character argument
to a "%lc" directive adjusted for precision but not field width. */
@ -1515,13 +1566,16 @@ format_string (const conversion_spec &spec, tree arg)
locale, which is unfortunately, unknown. */
res.range.min = 1 == warn_format_length ? !nul : nul < 1;
res.range.max = max_bytes_for_unknown_wc;
res.bounded = true;
/* The range above is good enough to issue warnings but not
for value range propagation, so clear BOUNDED. */
res.bounded = false;
}
else
{
/* A plain '%c' directive. */
/* A plain '%c' directive. Its ouput is exactly 1. */
res.range.min = res.range.max = 1;
res.bounded = true;
res.knownrange = true;
res.constant = arg && TREE_CODE (arg) == INTEGER_CST;
}
}
@ -1533,24 +1587,35 @@ format_string (const conversion_spec &spec, tree arg)
{
gcc_checking_assert (slen.range.min == slen.range.max);
res.bounded = true;
/* A '%s' directive with a string argument with constant length. */
res.range = slen.range;
/* The output of "%s" and "%ls" directives with a constant
string is in a known range. For "%s" it is the length
of the string. For "%ls" it is in the range [length,
length * MB_LEN_MAX]. (The final range can be further
constrained by width and precision but it's always known.) */
res.knownrange = true;
if (spec.modifier == FMT_LEN_l)
{
if (warn_format_length > 2)
bounded = false;
if (warn_format_length > 1)
{
res.range.min *= 6;
/* Leave the minimum number of bytes the wide string
converts to equal to its length and set the maximum
to the worst case length which is the string length
multiplied by MB_LEN_MAX. */
/* It's possible to be smarter about computing the maximum
by scanning the wide string for any 8-bit characters and
if it contains none, using its length for the maximum.
Even though this would be simple to do it's unlikely to
be worth it when dealing with wide characters. */
res.range.max *= 6;
res.range.max *= target_mb_len_max;
}
/* For a wide character string, use precision as the maximum
even if precision is greater than the string length since
the number of bytes the string converts to may be greater
@ -1559,7 +1624,12 @@ format_string (const conversion_spec &spec, tree arg)
res.range.max = prec;
}
else
{
/* The output od a "%s" directive with a constant argument
is bounded, constant, and obviously in a known range. */
res.bounded = true;
res.constant = true;
}
if (0 <= prec && (unsigned)prec < res.range.min)
{
@ -1577,9 +1647,11 @@ format_string (const conversion_spec &spec, tree arg)
if (0 <= prec)
{
if ((unsigned)prec < slen.range.min
|| slen.range.min >= target_int_max ())
if (slen.range.min >= target_int_max ())
slen.range.min = max_bytes_for_unknown_str;
else if ((unsigned)prec < slen.range.min)
slen.range.min = prec;
if ((unsigned)prec < slen.range.max
|| slen.range.max >= target_int_max ())
slen.range.max = prec;
@ -1597,6 +1669,7 @@ format_string (const conversion_spec &spec, tree arg)
specified to limit the number of bytes or when the number
of bytes is known or contrained to some range. */
res.bounded = 0 <= prec || slen.bounded;
res.knownrange = slen.knownrange;
res.constant = false;
}
}
@ -1613,6 +1686,11 @@ format_string (const conversion_spec &spec, tree arg)
&& bounded)
res.bounded = true;
/* When precision is specified the range of characters on output
is known to be bounded by it. */
if (-1 < prec)
res.knownrange = true;
return res;
}
@ -1655,13 +1733,29 @@ format_directive (const pass_sprintf_length::call_info &info,
/* Compute the (approximate) length of the formatted output. */
fmtresult fmtres = spec.fmtfunc (spec, arg);
/* The overall result is bounded only if the output of every
directive is exact or bounded. */
res->bounded = res->bounded && fmtres.bounded;
res->constant = res->constant && fmtres.constant;
/* The overall result is bounded and constant only if the output
of every directive is bounded and constant, respectively. */
res->bounded &= fmtres.bounded;
res->constant &= fmtres.constant;
if (fmtres.range.max >= HOST_WIDE_INT_MAX)
/* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are
either known exactly or determined to be in a known range
or, for strings, limited by the upper bounds of the arrays
they refer to. */
res->knownrange &= fmtres.knownrange;
if (!fmtres.knownrange)
{
/* Only when the range is known, check it against the host value
of INT_MAX. Otherwise the range doesn't correspond to known
values of the argument. */
if (fmtres.range.max >= target_int_max ())
{
/* Normalize the MAX counter to avoid having to deal with it
later. The counter can be less than HOST_WIDE_INT_M1U
when compiling for an ILP32 target on an LP64 host. */
fmtres.range.max = HOST_WIDE_INT_M1U;
/* Disable exact and maximum length checking after a failure
to determine the maximum number of characters (for example
for wide characters or wide character strings) but continue
@ -1670,7 +1764,7 @@ format_directive (const pass_sprintf_length::call_info &info,
res->number_chars = HOST_WIDE_INT_M1U;
}
if (fmtres.range.min >= HOST_WIDE_INT_MAX)
if (fmtres.range.min >= target_int_max ())
{
/* Disable exact length checking after a failure to determine
even the minimum number of characters (it shouldn't happen
@ -1679,6 +1773,7 @@ format_directive (const pass_sprintf_length::call_info &info,
res->number_chars = HOST_WIDE_INT_M1U;
return;
}
}
/* Compute the number of available bytes in the destination. There
must always be at least one byte of space for the terminating
@ -1725,10 +1820,14 @@ format_directive (const pass_sprintf_length::call_info &info,
}
}
else if (navail < fmtres.range.max
&& (fmtres.bounded || 1 < warn_format_length))
&& (((spec.specifier == 's'
&& fmtres.range.max < HOST_WIDE_INT_MAX)
/* && (spec.precision || spec.star_precision) */)
|| 1 < warn_format_length))
{
/* The maximum directive output is longer than there is
room in the destination and the output is either bounded
room in the destination and the output length is either
explicitly constrained by the precision (for strings)
or the warning level is greater than 1. */
if (fmtres.range.max >= HOST_WIDE_INT_MAX)
{
@ -1910,10 +2009,13 @@ add_bytes (const pass_sprintf_length::call_info &info,
/* If issuing a diagnostic (only when one hasn't already been issued),
distinguish between a possible overflow ("may write") and a certain
overflow somewhere "past the end." (Ditto for truncation.) */
overflow somewhere "past the end." (Ditto for truncation.)
KNOWNRANGE is used to warn even at level 1 about possibly writing
past the end or truncation due to strings of unknown lengths that
are bounded by the arrays they are known to refer to. */
if (!res->warned
&& (avail_range.max < nbytes
|| ((res->bounded || 1 < warn_format_length)
|| ((res->knownrange || 1 < warn_format_length)
&& avail_range.min < nbytes)))
{
/* Set NAVAIL to the number of available bytes used to decide
@ -1921,7 +2023,7 @@ add_bytes (const pass_sprintf_length::call_info &info,
warning will depend on AVAIL_RANGE. */
unsigned HOST_WIDE_INT navail = avail_range.max;
if (nbytes <= navail && avail_range.min < HOST_WIDE_INT_MAX
&& (res->bounded || 1 < warn_format_length))
&& (res->knownrange || 1 < warn_format_length))
navail = avail_range.min;
/* Compute the offset of the first format character that is beyond
@ -1932,8 +2034,12 @@ add_bytes (const pass_sprintf_length::call_info &info,
size_t len = strlen (info.fmtstr + off);
/* Create a location that underscores the substring of the format
string that is or may be written past the end (or is or may be
truncated), pointing the caret at the first character of the
substring. */
substring_loc loc
(info.fmtloc, TREE_TYPE (info.format), off - !len, len ? off : 0,
(info.fmtloc, TREE_TYPE (info.format), off, len ? off : 0,
off + len - !!len);
/* Is the output of the last directive the result of the argument
@ -1944,7 +2050,7 @@ add_bytes (const pass_sprintf_length::call_info &info,
= (res->number_chars_min < res->number_chars_max
&& res->number_chars_min < info.objsize);
if (!end && (nbytes - navail) == 1)
if (!end && ((nbytes - navail) == 1 || boundrange))
{
/* There is room for the rest of the format string but none
for the terminating nul. */
@ -2082,10 +2188,11 @@ pass_sprintf_length::compute_format_length (const call_info &info,
/* Reset exact, minimum, and maximum character counters. */
res->number_chars = res->number_chars_min = res->number_chars_max = 0;
/* No directive has been seen yet so the output is bounded and constant
(with no conversion producing more than 4K bytes) until determined
otherwise. */
/* No directive has been seen yet so the length of output is bounded
by the known range [0, 0] and constant (with no conversion producing
more than 4K bytes) until determined otherwise. */
res->bounded = true;
res->knownrange = true;
res->constant = true;
res->under4k = true;
res->floating = false;
@ -2591,10 +2698,10 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator gsi)
if (TREE_CODE (size) == INTEGER_CST)
{
dstsize = tree_to_uhwi (size);
/* No object can be larger than HOST_WIDE_INT_MAX bytes
(half the address space). This imposes a limit that's
one byte less than that. */
if (dstsize >= HOST_WIDE_INT_MAX)
/* No object can be larger than SIZE_MAX bytes (half the address
space) on the target. This imposes a limit that's one byte
less than that. */
if (dstsize >= target_size_max () / 2)
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
"specified destination size %wu too large",
dstsize);
@ -2640,7 +2747,7 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator gsi)
info.objsize = dstsize < objsize ? dstsize : objsize;
if (info.bounded
&& dstsize != HOST_WIDE_INT_M1U && objsize < dstsize)
&& dstsize < target_size_max () / 2 && objsize < dstsize)
{
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
"specified size %wu exceeds the size %wu "

View file

@ -1,3 +1,14 @@
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>
PR testsuite/71491

View file

@ -1,5 +1,5 @@
/* 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
the return value is in a known range the range is not made
available to subsequent passes and doesn't affect branching and
@ -22,7 +22,8 @@ char buf8k [8192];
#define CAT(a, b) concat (a, b)
#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__) \
{ \
@ -37,7 +38,8 @@ char buf8k [8192];
to the formatted function is not treated as a constant or made available
to subsequent optimization passes. */
#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__) \
{ \
@ -52,6 +54,9 @@ extern int i;
extern long li;
extern char *str;
extern double d;
extern long double ld;
/* Verify that overflowing the destination object disables the return
value optimization. */
EQL (0, 0, "%c", ' ');
@ -78,7 +83,15 @@ enum { imax2 = (INT_MAX / 2) * 2 };
EQL (imax2, -1, "%*c%*c", INT_MAX / 2, 'x', INT_MAX / 2, 'y');
/* 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, 1, "%hhi", i)
RNG (0, 1, 1, "%hhi", i)
@ -190,11 +203,36 @@ RNG (0, 10, 10, "%i", i)
#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
literal and an unknown string isn't optimized. */
RNG (0, 1, 4, "%-s", i ? str : "123");
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
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
@ -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" 74 "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" 83 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */

View file

@ -37,6 +37,18 @@ typedef __WINT_TYPE__ wint_t;
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 s1[] = "1";
const char s2[] = "12";
@ -372,6 +384,13 @@ void test_sprintf_chk_s_const (void)
T (3, "%.0ls", L"1");
T (3, "%.1ls", 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
@ -382,7 +401,9 @@ void test_sprintf_chk_hh_const (void)
T (-1, "%hhd", 0);
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", cst1); /* { dg-warning "nul past the end" } */
T (1, "%hhd", -1); /* { dg-warning "into a region" } */
T (1, "%+hhd", 0); /* { 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)
T (2, "%+hhi", 9); /* { dg-warning "nul past the end" } */
T (2, "%-hhi", 9);
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" } */
@ -863,6 +885,35 @@ void test_sprintf_chk_z_const (void)
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)
{
T (-1, "%E", 0.0);
@ -893,20 +944,23 @@ void test_sprintf_chk_e_const (void)
T ( 6, "%.0e", 1.0);
/* The actual output of the following directives depends on the rounding
mode. Verify that the warning correctly reflects that. */
T (12, "%e", 9.999999e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%e", 9.9999994e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%e", 9.9999995e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%e", 9.9999996e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%e", 9.9999997e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%e", 9.9999998e+99); /* { dg-warning "directive writing between 12 and 13 bytes" } */
mode. Verify that the warning correctly reflects that. At level 1,
since the minimum number of bytes output by the directive fits the
space the directive itself isn't diagnosed but the terminating nul
is. The directive is diagnosed at level 2. */
T (12, "%e", 9.999999e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999994e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999995e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999996e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999997e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999998e+99); /* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999994e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%Le", 9.9999995e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */
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.9999998e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%Le", 9.9999999e+99L);/* { dg-warning "directive writing between 12 and 13 bytes" } */
T (12, "%Le", 9.9999994e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999995e+99L);/* { dg-warning "terminating nul" } */
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
@ -936,12 +990,14 @@ void test_sprintf_chk_hh_nonconst (int 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, "%hhu", 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", var0); /* { 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, "%hhx", a); /* { dg-warning "nul past the end" } */
@ -954,19 +1010,23 @@ void test_sprintf_chk_hh_nonconst (int a)
T (1, "%-hhi", a); /* { dg-warning "nul past the end" } */
T (2, "%hhd", a);
T (2, "%hhd", var0);
T (2, "%hhd", var10);
T (2, "%hhi", a);
T (2, "%hho", a);
T (2, "%hhu", a);
T (2, "%hhx", a);
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, "% hho", a); /* { dg-warning ". . flag used with .%o." } */
T (2, "% hhu", a); /* { dg-warning ". . flag used with .%u." } */
T (2, "% hhx", a); /* { dg-warning ". . flag used with .%x." } */
T (2, "#%hho", a); /* { dg-warning "nul past the end" } */
T (2, "#%hhx", a); /* { dg-warning "nul past the end" } */
T (2, "%#hho", 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, "%2hhi", a);
@ -1086,8 +1146,7 @@ void test_sprintf_chk_e_nonconst (double d)
T ( 1, "%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 (12, "%e", d); /* { dg-warning "past the end" } */
T (12, "%e", d); /* { dg-warning "past the end" } */
T (12, "%e", d); /* { dg-warning "nul past the end" } */
T (13, "%E", d); /* 1.000000E+00 */
T (13, "%e", d);
T (14, "%E", d);

View file

@ -47,10 +47,13 @@ void test_s_const (void)
T (1, "%*ls", 0, L"\0");
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 (2, "%.0ls", 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
L"1" is known and can be assumed to convert to at least one multibyte
@ -64,6 +67,12 @@ void test_s_const (void)
T (2, "%.0ls", L"1");
T (2, "%.1ls", 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,6 +95,8 @@ void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a)
T (1, "%.0s", s);
T (1, "%.1s", s); /* { 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.

View file

@ -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" } */
/* 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. */
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" } */
@ -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, 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, 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)

View file

@ -3,31 +3,91 @@
extern int sprintf (char*, const char*, ...);
char dst [8];
char dst [3];
void test (void)
{
sprintf (dst + 7, "%-s", "1");
/* { dg-warning "writing a terminating nul past the end of the destination" "" { target *-*-* } 10 }
{ dg-message "format output 2 bytes into a destination of size 1" "" { target *-*-* } 10 }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "1");
~~^~
/* Verify thet the caret points to the (invisible) nul character
at the end of the format string (i.e., its closing quote).
The redundant argument is there to get around GCC bug 77799. */
sprintf (dst + 2, "1", 0);
/* { 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-begin-multiline-output "-Wformat output: redundant argument" }
sprintf (dst + 2, "1", 0);
^~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "1");
{ 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 "" } */
sprintf (dst + 7, "%-s", "abcd");
/* { dg-warning ".%-s. directive writing 4 bytes into a region of size 1" "" { target *-*-* } 22 }
{ dg-message "format output 5 bytes into a destination of size 1" "" { target *-*-* } 22 }
{ dg-begin-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" "warning" { target *-*-* } .-1 }
{ dg-message "format output 5 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat-length output" }
sprintf (dst + 2, "%-s", "abcd");
^~~ ~~~~~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "abcd");
{ dg-begin-multiline-output "note" }
sprintf (dst + 2, "%-s", "abcd");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
}
/* { dg-prune-output "too many arguments for format" } */

View file

@ -88,5 +88,5 @@ void fllong (long j, char *p)
if (k > 999)
return;
snprintf (p, 4, "%3llu", k); /* { dg-bogus "" "unsigned long long" { xfail *-*-* } } */
snprintf (p, 4, "%3llu", k); /* { dg-bogus "" "unsigned long long" { xfail lp64 } } */
}

View file

@ -359,23 +359,27 @@ test_x (unsigned char uc, unsigned short us, unsigned ui)
}
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, "%a", 1.0); /* 0x8p-3 */
EQL ( 6, 7, "%a", 2.0); /* 0x8p-2 */
EQL ( 6, 7, "%.0a", 0.0); /* 0x0p+0 */
EQL ( 6, 7, "%.0a", 1.0); /* 0x8p-3 */
EQL ( 6, 7, "%.0a", 2.0); /* 0x8p-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))
test_a_long_double (void)
{
EQL ( 6, 7, "%La", 0.0L); /* 0x0p+0 */
EQL ( 6, 7, "%La", 1.0L); /* 0x8p-3 */
EQL ( 6, 7, "%La", 2.0L); /* 0x8p-2 */
EQL ( 6, 7, "%.0La", 0.0L); /* 0x0p+0 */
EQL ( 6, 7, "%.0La", 1.0L); /* 0x8p-3 */
EQL ( 6, 7, "%.0La", 2.0L); /* 0x8p-2 */
EQL ( 8, 9, "%.1La", 3.0L); /* 0xc.0p-2 */
EQL ( 9, 10, "%.2La", 4.0L); /* 0xa.00p-1 */
}
@ -525,7 +529,7 @@ int main (void)
test_d_i (0xdeadbeef, 0xdeadbeefL);
test_x ('?', 0xdead, 0xdeadbeef);
test_a_double ();
test_a_double (0.0);
test_e_double ();
test_f_double ();