+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
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.
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))))
&& (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))));
+
+ /* 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
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
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;
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
#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 = {
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
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_int_max ()
+target_max_value (tree type)
{
- const unsigned HOST_WIDE_INT int_max
+ const unsigned HOST_WIDE_INT max_value
= HOST_WIDE_INT_M1U >> (HOST_BITS_PER_WIDE_INT
- - TYPE_PRECISION (integer_type_node) + 1);
- return int_max;
+ - 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 ()
+{
+ 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
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;
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,
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;
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
{
/* 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;
/* 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
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)
{
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));
}
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);
mpfr_init2 (x, rfmt->p);
mpfr_from_real (x, &rv, GMP_RNDN);
- const char fmt[] = { '%', 'R', spec, '\0' };
- int n = mpfr_snprintf (NULL, 0, fmt, x);
- return n;
+ 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' };
+ 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
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;
{
/* 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;
}
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;
}
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':
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)
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;
}
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)
*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;
}
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))
{
res.range.min = res.range.max = tree_to_shwi (slen);
res.bounded = true;
res.constant = true;
+ res.knownrange = true;
return res;
}
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);
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;
}
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. */
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;
}
}
{
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
res.range.max = prec;
}
else
- res.constant = true;
+ {
+ /* 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)
{
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;
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;
}
}
&& 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;
}
/* 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)
- {
- /* 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
- tracking the minimum number of characters. */
- res->number_chars_max = HOST_WIDE_INT_M1U;
- res->number_chars = HOST_WIDE_INT_M1U;
- }
+ /* 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.range.min >= HOST_WIDE_INT_MAX)
+ if (!fmtres.knownrange)
{
- /* Disable exact length checking after a failure to determine
- even the minimum number of characters (it shouldn't happen
- except in an error) but keep tracking the minimum and maximum
- number of characters. */
- res->number_chars = HOST_WIDE_INT_M1U;
- return;
+ /* 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
+ tracking the minimum number of characters. */
+ res->number_chars_max = HOST_WIDE_INT_M1U;
+ res->number_chars = HOST_WIDE_INT_M1U;
+ }
+
+ 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
+ except in an error) but keep tracking the minimum and maximum
+ number of characters. */
+ res->number_chars = HOST_WIDE_INT_M1U;
+ return;
+ }
}
/* Compute the number of available bytes in the destination. There
}
}
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
- or the warning level is greater than 1. */
+ 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)
{
const char* fmtstr
/* 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
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
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
= (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. */
/* 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;
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);
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 "
+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
/* 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
#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__) \
{ \
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__) \
{ \
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", ' ');
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)
#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
*/
-/* { 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 } } } } } */
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";
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
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" } */
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" } */
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);
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" } */
-
- 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" } */
+ 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 "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
{
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" } */
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);
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);
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
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" } */
}
T (1, "%.0s", s);
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.
The minus sign disables GCC's sprintf to strcpy transformation. */
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" } */
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)
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");
- ~~^~
- { dg-end-multiline-output "" }
- { 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 "-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" } */
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 } } */
}
}
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 */
}
test_d_i (0xdeadbeef, 0xdeadbeefL);
test_x ('?', 0xdead, 0xdeadbeef);
- test_a_double ();
+ test_a_double (0.0);
test_e_double ();
test_f_double ();