Commit 5d93da1d by Martin Sebor Committed by Martin Sebor

PR middle-end/78703 -fprintf-return-value floating point handling incorrect in…

PR middle-end/78703 -fprintf-return-value floating point handling incorrect in locales with a mulltibyte decimal point

gcc/ChangeLog:
	PR middle-end/78703
	* gimple-ssa-sprintf.c (struct result_range): Add likely and
	unlikely counters.
	(struct format_result): Replace number_chars, number_chars_min,
	and number_chars_max with a single member of struct result_range.
	Remove bounded.
	(format_result::operator+=): Adjust.
	(struct fmtresult): Remove bounded.  Handle likely and unlikely
	counters.
	(fmtresult::adjust_for_width_or_precision): New function.
	(fmtresult:type_max_digits): New function.
	(bytes_remaining): Handle likely and unlikely counters.
	(min_bytes_remaining): Remove.
	(format_percent): Simplify.
	(format_integer, format_floating): Set likely and unlikely counters.
	(get_string_length, format_character, format_string): Same.
	(format_plain, should_warn_p): New function.
	(maybe_warn): Call should_warn_p.  Update diagnostic messages
	and handle those for all directives, including plain strings.
	(format_directive): Handle likely and unlikely counters.
	Remove unnecessary quoting from diagnostics.  Add an informational
	note.
	(add_bytes): Remove.
	(pass_sprintf_length::compute_format_length): Simplify.
	(try_substitute_return_value): Handle likely and unlikely counters.

gcc/testsuite/ChangeLog:

	PR middle-end/78703
	* gcc.dg/format/pr78569.c: Adjust.
	* gcc.dg/tree-ssa/builtin-snprintf-warn-2.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-2.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-5.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: Same.
	* gcc.dg/tree-ssa/builtin-sprintf.c: Same.

From-SVN: r244953
parent c8699672
2017-01-26 Martin Sebor <msebor@redhat.com>
PR middle-end/78703
* gimple-ssa-sprintf.c (struct result_range): Add likely and
unlikely counters.
(struct format_result): Replace number_chars, number_chars_min,
and number_chars_max with a single member of struct result_range.
Remove bounded.
(format_result::operator+=): Adjust.
(struct fmtresult): Remove bounded. Handle likely and unlikely
counters.
(fmtresult::adjust_for_width_or_precision): New function.
(fmtresult:type_max_digits): New function.
(bytes_remaining): Handle likely and unlikely counters.
(min_bytes_remaining): Remove.
(format_percent): Simplify.
(format_integer, format_floating): Set likely and unlikely counters.
(get_string_length, format_character, format_string): Same.
(format_plain, should_warn_p): New function.
(maybe_warn): Call should_warn_p. Update diagnostic messages
and handle those for all directives, including plain strings.
(format_directive): Handle likely and unlikely counters.
Remove unnecessary quoting from diagnostics. Add an informational
note.
(add_bytes): Remove.
(pass_sprintf_length::compute_format_length): Simplify.
(try_substitute_return_value): Handle likely and unlikely counters.
2017-01-26 Carl Love <cel@us.ibm.com> 2017-01-26 Carl Love <cel@us.ibm.com>
* config/rs6000/rs6000-c (altivec_overloaded_builtins): Remove * config/rs6000/rs6000-c (altivec_overloaded_builtins): Remove
......
...@@ -153,44 +153,53 @@ pass_sprintf_length::gate (function *) ...@@ -153,44 +153,53 @@ pass_sprintf_length::gate (function *)
&& (optimize > 0) == fold_return_value); && (optimize > 0) == fold_return_value);
} }
/* The minimum, maximum, likely, and unlikely maximum number of bytes
of output either a formatting function or an individual directive
can result in. */
struct result_range
{
/* The absolute minimum number of bytes. The result of a successful
conversion is guaranteed to be no less than this. (An erroneous
conversion can be indicated by MIN > HOST_WIDE_INT_MAX.) */
unsigned HOST_WIDE_INT min;
/* The likely maximum result that is used in diagnostics. In most
cases MAX is the same as the worst case UNLIKELY result. */
unsigned HOST_WIDE_INT max;
/* The likely result used to trigger diagnostics. For conversions
that result in a range of bytes [MIN, MAX], LIKELY is somewhere
in that range. */
unsigned HOST_WIDE_INT likely;
/* In rare cases (e.g., for nultibyte characters) UNLIKELY gives
the worst cases maximum result of a directive. In most cases
UNLIKELY == MAX. UNLIKELY is used to control the return value
optimization but not in diagnostics. */
unsigned HOST_WIDE_INT unlikely;
};
/* The result of a call to a formatted function. */ /* The result of a call to a formatted function. */
struct format_result struct format_result
{ {
/* Number of characters written by the formatted function, exact, /* Range of characters written by the formatted function.
minimum and maximum when an exact number cannot be determined. Setting the minimum to HOST_WIDE_INT_MAX disables all
Setting the minimum to HOST_WIDE_INT_MAX disables all length length tracking for the remainder of the format string. */
tracking for the remainder of the format string. result_range range;
Setting either of the other two members to HOST_WIDE_INT_MAX
disables the exact or maximum length tracking, respectively,
but continues to track the maximum. */
unsigned HOST_WIDE_INT number_chars;
unsigned HOST_WIDE_INT number_chars_min;
unsigned HOST_WIDE_INT number_chars_max;
/* True when the range given by NUMBER_CHARS_MIN and NUMBER_CHARS_MAX
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 string folding. BOUNDED is a stronger
guarantee than KNOWNRANGE. */
bool bounded;
/* True when the range above is obtained from known values of /* True when the range above is obtained from known values of
directive arguments or their bounds and not the result of directive arguments, or bounds on the amount of output such
heuristics that depend on warning levels. It is used to as width and precision, and not the result of heuristics that
issue stricter diagnostics in cases where strings of unknown depend on warning levels. It's used to issue stricter diagnostics
lengths are bounded by the arrays they are determined to in cases where strings of unknown lengths are bounded by the arrays
refer to. KNOWNRANGE must not be used to set the range of they are determined to refer to. KNOWNRANGE must not be used for
the return value of a call. */ the return value optimization. */
bool knownrange; bool knownrange;
/* True if no individual directive resulted in more than 4095 bytes /* True if no individual directive resulted in more than 4095 bytes
of output (the total NUMBER_CHARS might be greater). */ of output (the total NUMBER_CHARS_{MIN,MAX} might be greater).
Implementations are not required to handle directives that produce
more than 4K bytes (leading to undefined behavior) and so when one
is found it disables the return value optimization. */
bool under4k; bool under4k;
/* True when a floating point directive has been seen in the format /* True when a floating point directive has been seen in the format
...@@ -199,7 +208,7 @@ struct format_result ...@@ -199,7 +208,7 @@ struct format_result
/* True when an intermediate result has caused a warning. Used to /* True when an intermediate result has caused a warning. Used to
avoid issuing duplicate warnings while finishing the processing avoid issuing duplicate warnings while finishing the processing
of a call. */ of a call. WARNED also disables the return value optimization. */
bool warned; bool warned;
/* Preincrement the number of output characters by 1. */ /* Preincrement the number of output characters by 1. */
...@@ -225,14 +234,17 @@ format_result::operator+= (unsigned HOST_WIDE_INT n) ...@@ -225,14 +234,17 @@ format_result::operator+= (unsigned HOST_WIDE_INT n)
{ {
gcc_assert (n < HOST_WIDE_INT_MAX); gcc_assert (n < HOST_WIDE_INT_MAX);
if (number_chars < HOST_WIDE_INT_MAX) if (range.min < HOST_WIDE_INT_MAX)
number_chars += n; range.min += n;
if (range.max < HOST_WIDE_INT_MAX)
range.max += n;
if (number_chars_min < HOST_WIDE_INT_MAX) if (range.likely < HOST_WIDE_INT_MAX)
number_chars_min += n; range.likely += n;
if (number_chars_max < HOST_WIDE_INT_MAX) if (range.unlikely < HOST_WIDE_INT_MAX)
number_chars_max += n; range.unlikely += n;
return *this; return *this;
} }
...@@ -434,13 +446,6 @@ enum format_lengths ...@@ -434,13 +446,6 @@ enum format_lengths
}; };
/* A minimum and maximum number of bytes. */
struct result_range
{
unsigned HOST_WIDE_INT min, max;
};
/* Description of the result of conversion either of a single directive /* Description of the result of conversion either of a single directive
or the whole format string. */ or the whole format string. */
...@@ -451,25 +456,38 @@ struct fmtresult ...@@ -451,25 +456,38 @@ struct fmtresult
fmtresult (unsigned HOST_WIDE_INT min = HOST_WIDE_INT_MAX) fmtresult (unsigned HOST_WIDE_INT min = HOST_WIDE_INT_MAX)
: argmin (), argmax (), : argmin (), argmax (),
knownrange (min < HOST_WIDE_INT_MAX), knownrange (min < HOST_WIDE_INT_MAX),
bounded (),
nullp () nullp ()
{ {
range.min = min; range.min = min;
range.max = min; range.max = min;
range.likely = min;
range.unlikely = min;
} }
/* Construct a FMTRESULT object with all counters initialized /* Construct a FMTRESULT object with MIN, MAX, and LIKELY counters.
to MIN. KNOWNRANGE is set when MIN is valid. */ KNOWNRANGE is set when both MIN and MAX are valid. */
fmtresult (unsigned HOST_WIDE_INT min, unsigned HOST_WIDE_INT max) fmtresult (unsigned HOST_WIDE_INT min, unsigned HOST_WIDE_INT max,
unsigned HOST_WIDE_INT likely = HOST_WIDE_INT_MAX)
: argmin (), argmax (), : argmin (), argmax (),
knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX), knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
bounded (),
nullp () nullp ()
{ {
range.min = min; range.min = min;
range.max = max; range.max = max;
range.likely = max < likely ? min : likely;
range.unlikely = max;
} }
/* Adjust result upward to reflect the value the specified width
or precision is known to have. */
fmtresult& adjust_for_width_or_precision (HOST_WIDE_INT,
tree = NULL_TREE,
unsigned = 0, unsigned = 0);
/* Return the maximum number of decimal digits a value of TYPE
formats as on output. */
static unsigned type_max_digits (tree type, int base);
/* The range a directive's argument is in. */ /* The range a directive's argument is in. */
tree argmin, argmax; tree argmin, argmax;
...@@ -482,17 +500,116 @@ struct fmtresult ...@@ -482,17 +500,116 @@ struct fmtresult
heuristics that depend on warning levels. */ heuristics that depend on warning levels. */
bool knownrange; 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 argument is a null pointer. */ /* True when the argument is a null pointer. */
bool nullp; bool nullp;
}; };
/* Description of a conversion specification. */ /* Adjust result upward to reflect the value SCALAR_ADJUST the specified
width or precision is known to have. When non-null, TYPE denotes the
type of the directive whose result is being adjusted, BASE gives the
base of the directive (octal, decimal, or hex), and ADJ denotes
the additional adjustment to the LIKELY counter that may need to be
added when SCALAR_ADJUST represents a range. */
fmtresult&
fmtresult::adjust_for_width_or_precision (HOST_WIDE_INT scalar_adjust,
tree type /* = NULL_TREE */,
unsigned base /* = 0 */,
unsigned adj /* = 0 */)
{
bool minadjusted = false;
/* Translate SCALAR_ADJUST to a "fake" range until width and precision
ranges are handled. */
HOST_WIDE_INT adjust[2];
if (scalar_adjust == HOST_WIDE_INT_MIN)
{
adjust[0] = -1;
adjust[1] = target_int_max () + 1;
}
else
adjust[0] = adjust[1] = scalar_adjust;
/* Adjust the minimum and likely counters. */
if (0 <= adjust[0])
{
if (range.min < (unsigned HOST_WIDE_INT)adjust[0])
{
range.min = adjust[0];
minadjusted = true;
}
/* Adjust the likely counter. */
if (range.likely < range.min)
range.likely = range.min;
}
else if (adjust[0] == target_int_min ()
&& (unsigned HOST_WIDE_INT)adjust[1] == target_int_max ())
knownrange = false;
/* Adjust the maximum counter. */
if (0 < adjust[1])
{
if (range.max < (unsigned HOST_WIDE_INT)adjust[1])
{
range.max = adjust[1];
/* Set KNOWNRANGE if both the minimum and maximum have been
adjusted. Otherwise leave it at what it was before. */
knownrange = minadjusted;
}
}
if (warn_level > 1 && type)
{
/* For large non-constant width or precision whose range spans
the maximum number of digits produced by the directive for
any argument, set the likely number of bytes to be at most
the number digits plus other adjustment determined by the
caller (one for sign or two for the hexadecimal "0x"
prefix). */
unsigned dirdigs = type_max_digits (type, base);
if (adjust[0] < dirdigs && dirdigs < adjust[1]
&& range.likely < dirdigs)
range.likely = dirdigs + adj;
}
else if (range.likely < (range.min ? range.min : 1))
{
/* Conservatively, set LIKELY to at least MIN but no less than
1 unless MAX is zero. */
range.likely = (range.min
? range.min
: range.max && (range.max < HOST_WIDE_INT_MAX
|| warn_level > 1) ? 1 : 0);
}
/* Finally adjust the unlikely counter to be at least as large as
the maximum. */
if (range.unlikely < range.max)
range.unlikely = range.max;
return *this;
}
/* Return the maximum number of digits a value of TYPE formats in
BASE on output, not counting base prefix . */
unsigned
fmtresult::type_max_digits (tree type, int base)
{
unsigned prec = TYPE_PRECISION (type);
if (base == 8)
return (prec + 2) / 3;
if (base == 16)
return prec / 4;
/* Decimal approximation: yields 3, 5, 10, and 20 for precision
of 8, 16, 32, and 64 bits. */
return prec * 301 / 1000 + 1;
}
/* Description of a format directive. */
struct directive struct directive
{ {
...@@ -521,8 +638,8 @@ struct directive ...@@ -521,8 +638,8 @@ struct directive
take one or when none is available (such as for vararg functions). */ take one or when none is available (such as for vararg functions). */
tree arg; tree arg;
/* Format conversion function that given a conversion specification /* Format conversion function that given a directive and an argument
and an argument returns the formatting result. */ returns the formatting result. */
fmtresult (*fmtfunc) (const directive &, tree); fmtresult (*fmtfunc) (const directive &, tree);
/* Return True when a the format flag CHR has been used. */ /* Return True when a the format flag CHR has been used. */
...@@ -690,68 +807,29 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res) ...@@ -690,68 +807,29 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res)
if (HOST_WIDE_INT_MAX <= navail) if (HOST_WIDE_INT_MAX <= navail)
{ {
range.min = range.max = navail; range.min = range.max = range.likely = range.unlikely = navail;
return range; return range;
} }
if (res.number_chars < navail) /* The lower bound of the available range is the available size
{ minus the maximum output size, and the upper bound is the size
range.min = range.max = navail - res.number_chars; minus the minimum. */
} range.max = res.range.min < navail ? navail - res.range.min : 0;
else if (res.number_chars_min < navail)
{
range.max = navail - res.number_chars_min;
}
else
range.max = 0;
if (res.number_chars_max < navail)
range.min = navail - res.number_chars_max;
else
range.min = 0;
return range; /* Given the formatting result described by RES and NAVAIL, the number
} of available in the destination, return the minimum number of bytes
remaining in the destination. */
/* Given the formatting result described by RES and NAVAIL, the number range.likely = res.range.likely < navail ? navail - res.range.likely : 0;
of available in the destination, return the minimum number of bytes
remaining in the destination. */
static inline unsigned HOST_WIDE_INT if (res.range.max < HOST_WIDE_INT_MAX)
min_bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res) range.min = res.range.max < navail ? navail - res.range.max : 0;
{
if (HOST_WIDE_INT_MAX <= navail)
return navail;
if (warn_format_overflow > 1 || res.knownrange)
{
/* At level 2, or when all directives output an exact number
of bytes or when their arguments were bounded by known
ranges, use the greater of the two byte counters if it's
valid to compute the result. */
if (res.number_chars_max < HOST_WIDE_INT_MAX)
navail -= res.number_chars_max;
else if (res.number_chars < HOST_WIDE_INT_MAX)
navail -= res.number_chars;
else if (res.number_chars_min < HOST_WIDE_INT_MAX)
navail -= res.number_chars_min;
}
else else
{ range.min = range.likely;
/* At level 1 use the smaller of the byte counters to compute
the result. */
if (res.number_chars < HOST_WIDE_INT_MAX)
navail -= res.number_chars;
else if (res.number_chars_min < HOST_WIDE_INT_MAX)
navail -= res.number_chars_min;
else if (res.number_chars_max < HOST_WIDE_INT_MAX)
navail -= res.number_chars_max;
}
if (navail > HOST_WIDE_INT_MAX) range.unlikely = (res.range.unlikely < navail
navail = 0; ? navail - res.range.unlikely : 0);
return navail; return range;
} }
/* Description of a call to a formatted function. */ /* Description of a call to a formatted function. */
...@@ -810,7 +888,6 @@ static fmtresult ...@@ -810,7 +888,6 @@ static fmtresult
format_none (const directive &, tree) format_none (const directive &, tree)
{ {
fmtresult res (0); fmtresult res (0);
res.bounded = true;
return res; return res;
} }
...@@ -820,7 +897,6 @@ static fmtresult ...@@ -820,7 +897,6 @@ static fmtresult
format_percent (const directive &, tree) format_percent (const directive &, tree)
{ {
fmtresult res (1); fmtresult res (1);
res.bounded = true;
return res; return res;
} }
...@@ -929,9 +1005,8 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax) ...@@ -929,9 +1005,8 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
} }
/* Return a range representing the minimum and maximum number of bytes /* Return a range representing the minimum and maximum number of bytes
that the conversion specification DIR will write on output for the that the directive DIR will write on output for the integer argument
integer argument ARG when non-null. ARG may be null (for vararg ARG when non-null. ARG may be null (for vararg functions). */
functions). */
static fmtresult static fmtresult
format_integer (const directive &dir, tree arg) format_integer (const directive &dir, tree arg)
...@@ -939,13 +1014,13 @@ format_integer (const directive &dir, tree arg) ...@@ -939,13 +1014,13 @@ format_integer (const directive &dir, tree arg)
tree intmax_type_node; tree intmax_type_node;
tree uintmax_type_node; tree uintmax_type_node;
/* Set WIDTH and PRECISION based on the specification. */
HOST_WIDE_INT width = dir.width;
HOST_WIDE_INT prec = dir.prec;
/* Base to format the number in. */ /* Base to format the number in. */
int base; int base;
/* True when a conversion is preceded by a prefix indicating the base
of the argument (octal or hexadecimal). */
bool maybebase = dir.get_flag ('#');
/* True when a signed conversion is preceded by a sign or space. */ /* True when a signed conversion is preceded by a sign or space. */
bool maybesign = false; bool maybesign = false;
...@@ -1044,9 +1119,10 @@ format_integer (const directive &dir, tree arg) ...@@ -1044,9 +1119,10 @@ format_integer (const directive &dir, tree arg)
/* When a constant argument has been provided use its value /* When a constant argument has been provided use its value
rather than type to determine the length of the output. */ rather than type to determine the length of the output. */
HOST_WIDE_INT len; fmtresult res;
if ((prec == HOST_WIDE_INT_MIN || prec == 0) && integer_zerop (arg)) if ((dir.prec == HOST_WIDE_INT_MIN || dir.prec == 0)
&& integer_zerop (arg))
{ {
/* As a special case, a precision of zero with a zero argument /* As a special case, a precision of zero with a zero argument
results in zero bytes except in base 8 when the '#' flag is results in zero bytes except in base 8 when the '#' flag is
...@@ -1055,44 +1131,45 @@ format_integer (const directive &dir, tree arg) ...@@ -1055,44 +1131,45 @@ format_integer (const directive &dir, tree arg)
when it results in just one byte (with width having the normal when it results in just one byte (with width having the normal
effect). This must extend to the case of a specified precision effect). This must extend to the case of a specified precision
with an unknown value because it can be zero. */ with an unknown value because it can be zero. */
len = ((base == 8 && dir.get_flag ('#')) || maybesign); res.range.min = ((base == 8 && dir.get_flag ('#')) || maybesign);
if (res.range.min == 0 && dir.prec == HOST_WIDE_INT_MIN)
{
res.range.max = 1;
res.range.likely = 1;
}
else
{
res.range.max = res.range.min;
res.range.likely = res.range.min;
}
} }
else else
{ {
/* Convert the argument to the type of the directive. */ /* Convert the argument to the type of the directive. */
arg = fold_convert (dirtype, arg); arg = fold_convert (dirtype, arg);
/* True when a conversion is preceded by a prefix indicating the base res.range.min = tree_digits (arg, base, dir.prec,
of the argument (octal or hexadecimal). */ maybesign, maybebase);
bool maybebase = dir.get_flag ('#');
len = tree_digits (arg, base, prec, maybesign, maybebase);
if (len < 1)
len = HOST_WIDE_INT_MAX;
}
if (len < width)
len = width;
/* The minimum and maximum number of bytes produced by the directive. */ /* Set the maximum to INT_MAX when precision is specified
fmtresult res; but unknown (because it can be as large as that) otherwise
to the minimum and have it adjusted below. */
res.range.min = len; if (dir.prec == HOST_WIDE_INT_MIN)
res.range.max = target_int_max ();
else
res.range.max = res.range.min;
/* The upper bound of the number of bytes is unlimited when either res.range.likely = res.range.min;
width or precision is specified but its value is unknown, and
the same as the lower bound otherwise. */
if (width == HOST_WIDE_INT_MIN || prec == HOST_WIDE_INT_MIN)
{
res.range.max = HOST_WIDE_INT_MAX;
}
else
{
res.range.max = len;
res.bounded = true;
res.knownrange = true;
res.bounded = true;
} }
res.range.unlikely = res.range.max;
/* Bump up the counters if WIDTH is greater than LEN. */
res.adjust_for_width_or_precision (dir.width, dirtype, base,
(sign | maybebase) + (base == 16));
/* Bump up the counters again if PRECision is greater still. */
res.adjust_for_width_or_precision (dir.prec, dirtype, base,
(sign | maybebase) + (base == 16));
return res; return res;
} }
else if (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE else if (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE
...@@ -1107,10 +1184,6 @@ format_integer (const directive &dir, tree arg) ...@@ -1107,10 +1184,6 @@ format_integer (const directive &dir, tree arg)
fmtresult res; fmtresult res;
/* The result is bounded unless width or precision has been specified
whose value is unknown. */
res.bounded = width != HOST_WIDE_INT_MIN && prec != HOST_WIDE_INT_MIN;
/* Using either the range the non-constant argument is in, or its /* Using either the range the non-constant argument is in, or its
type (either "formal" or actual), create a range of values that type (either "formal" or actual), create a range of values that
constrain the length of output given the warning level. */ constrain the length of output given the warning level. */
...@@ -1178,10 +1251,11 @@ format_integer (const directive &dir, tree arg) ...@@ -1178,10 +1251,11 @@ format_integer (const directive &dir, tree arg)
or one whose value range cannot be determined, create a T_MIN or one whose value range cannot be determined, create a T_MIN
constant if the argument's type is signed and T_MAX otherwise, constant if the argument's type is signed and T_MAX otherwise,
and use those to compute the range of bytes that the directive and use those to compute the range of bytes that the directive
can output. When precision is specified but unknown, use zero can output. When precision may be zero, use zero as the minimum
as the minimum since it results in no bytes on output (unless since it results in no bytes on output (unless width is specified
width is specified to be greater than 0). */ to be greater than 0). */
argmin = build_int_cst (argtype, prec && prec != HOST_WIDE_INT_MIN); bool zero = dir.prec == 0 || dir.prec == HOST_WIDE_INT_MIN;
argmin = build_int_cst (argtype, !zero);
int typeprec = TYPE_PRECISION (dirtype); int typeprec = TYPE_PRECISION (dirtype);
int argprec = TYPE_PRECISION (argtype); int argprec = TYPE_PRECISION (argtype);
...@@ -1250,13 +1324,6 @@ format_integer (const directive &dir, tree arg) ...@@ -1250,13 +1324,6 @@ format_integer (const directive &dir, tree arg)
res.range.max = format_integer (dir, argmin).range.max; res.range.max = format_integer (dir, argmin).range.max;
} }
/* The result is bounded either when the argument is determined to be
(e.g., when it's within some range) or when the minimum and maximum
are the same. That can happen here for example when the specified
width is as wide as the greater of MIN and MAX, as would be the case
with sprintf (d, "%08x", x) with a 32-bit integer x. */
res.bounded |= res.range.min == res.range.max;
if (res.range.max < res.range.min) if (res.range.max < res.range.min)
{ {
unsigned HOST_WIDE_INT tmp = res.range.max; unsigned HOST_WIDE_INT tmp = res.range.max;
...@@ -1264,6 +1331,13 @@ format_integer (const directive &dir, tree arg) ...@@ -1264,6 +1331,13 @@ format_integer (const directive &dir, tree arg)
res.range.min = tmp; res.range.min = tmp;
} }
res.range.likely = res.knownrange ? res.range.max : res.range.min;
res.range.unlikely = res.range.max;
res.adjust_for_width_or_precision (dir.width, dirtype, base,
(sign | maybebase) + (base == 16));
res.adjust_for_width_or_precision (dir.prec, dirtype, base,
(sign | maybebase) + (base == 16));
return res; return res;
} }
...@@ -1371,9 +1445,9 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec) ...@@ -1371,9 +1445,9 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
} }
/* Return a range representing the minimum and maximum number of bytes /* Return a range representing the minimum and maximum number of bytes
that the conversion specification DIR will output for any argument that the format directive DIR will output for any argument given
given the WIDTH and PRECISION (extracted from DIR). This function the WIDTH and PRECISION (extracted from DIR). This function is
is used when the directive argument or its value isn't known. */ used when the directive argument or its value isn't known. */
static fmtresult static fmtresult
format_floating (const directive &dir) format_floating (const directive &dir)
...@@ -1402,35 +1476,45 @@ format_floating (const directive &dir) ...@@ -1402,35 +1476,45 @@ format_floating (const directive &dir)
/* The minimum and maximum number of bytes produced by the directive. */ /* The minimum and maximum number of bytes produced by the directive. */
fmtresult res; fmtresult res;
/* The result is always bounded (though the range may be all of int). */ /* The minimum output as determined by flags. It's always at least 1.
res.bounded = true; When plus or space are set the output is preceded by either a sign
or a space. */
/* The minimum output as determined by flags. It's always at least 1. */
int flagmin = (1 /* for the first digit */ int flagmin = (1 /* for the first digit */
+ (dir.get_flag ('+') | dir.get_flag (' ')) + (dir.get_flag ('+') | dir.get_flag (' ')));
+ (dir.prec == 0 && dir.get_flag ('#')));
if (dir.width == HOST_WIDE_INT_MIN || dir.prec == HOST_WIDE_INT_MIN) /* When the pound flag is set the decimal point is included in output
{ regardless of precision. Whether or not a decimal point is included
/* When either width or precision is specified but unknown otherwise depends on the specification and precision. */
the upper bound is the maximum. Otherwise it will be bool radix = dir.get_flag ('#');
computed for each directive below. */
res.range.max = HOST_WIDE_INT_MAX;
}
else
res.range.max = HOST_WIDE_INT_M1U;
switch (dir.specifier) switch (dir.specifier)
{ {
case 'A': case 'A':
case 'a': case 'a':
{ {
res.range.min = flagmin + 5 + (dir.prec > 0 ? dir.prec + 1 : 0); HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
if (res.range.max == HOST_WIDE_INT_M1U) if (dir.prec <= 0)
{ minprec = 0;
/* Compute the upper bound for -TYPE_MAX. */ else if (dir.prec > 0)
res.range.max = format_floating_max (type, 'a', dir.prec); minprec = dir.prec + !radix /* decimal point */;
}
res.range.min = (2 /* 0x */
+ flagmin
+ radix
+ minprec
+ 3 /* p+0 */);
HOST_WIDE_INT maxprec
= dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
res.range.max = format_floating_max (type, 'a', maxprec);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
if (dir.prec != 0)
res.range.unlikely = res.range.max + target_mb_len_max () - 1;
else
res.range.unlikely = res.range.max;
break; break;
} }
...@@ -1440,18 +1524,32 @@ format_floating (const directive &dir) ...@@ -1440,18 +1524,32 @@ format_floating (const directive &dir)
{ {
/* The minimum output is "[-+]1.234567e+00" regardless /* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */ of the value of the actual argument. */
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
if (dir.prec == HOST_WIDE_INT_MIN || dir.prec == 0)
minprec = 0;
else if (dir.prec > 0)
minprec = dir.prec + !radix /* decimal point */;
res.range.min = (flagmin res.range.min = (flagmin
+ (dir.prec == HOST_WIDE_INT_MIN + radix
? 0 : dir.prec < 0 ? 7 : dir.prec ? dir.prec + 1 : 0) + minprec
+ 2 /* e+ */ + 2); + 2 /* e+ */ + 2);
if (res.range.max == HOST_WIDE_INT_M1U) /* MPFR uses a precision of 16 by default for some reason.
{ Set it to the C default of 6. */
/* MPFR uses a precision of 16 by default for some reason. HOST_WIDE_INT maxprec
Set it to the C default of 6. */ = (dir.prec == HOST_WIDE_INT_MIN ? target_int_max ()
res.range.max = format_floating_max (type, 'e', : dir.prec < 0 ? 6 : dir.prec);
-1 == dir.prec ? 6 : dir.prec); res.range.max = format_floating_max (type, 'e', maxprec);
}
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
if (dir.prec != 0)
res.range.unlikely = res.range.max + target_mb_len_max () -1;
else
res.range.unlikely = res.range.max;
break; break;
} }
...@@ -1463,16 +1561,27 @@ format_floating (const directive &dir) ...@@ -1463,16 +1561,27 @@ format_floating (const directive &dir)
is zero, the lower bound is 1 byte (e.g., "1"). Otherwise, is zero, the lower bound is 1 byte (e.g., "1"). Otherwise,
when precision is greater than zero, then the lower bound when precision is greater than zero, then the lower bound
is 2 plus precision (plus flags). */ is 2 plus precision (plus flags). */
res.range.min = (flagmin HOST_WIDE_INT minprec = 0;
+ (dir.prec != HOST_WIDE_INT_MIN) /* decimal point */ if (dir.prec == HOST_WIDE_INT_MIN)
+ (dir.prec == HOST_WIDE_INT_MIN minprec = 0;
? 0 : dir.prec < 0 ? 6 : dir.prec ? dir.prec : -1)); else if (dir.prec < 0)
minprec = 6 + !radix /* decimal point */;
if (res.range.max == HOST_WIDE_INT_M1U) else if (dir.prec)
{ minprec = dir.prec + !radix /* decimal point */;
/* Compute the upper bound for -TYPE_MAX. */
res.range.max = format_floating_max (type, 'f', dir.prec); res.range.min = flagmin + radix + minprec;
}
/* Compute the upper bound for -TYPE_MAX. */
HOST_WIDE_INT maxprec
= dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
res.range.max = format_floating_max (type, 'f', maxprec);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
if (dir.prec != 0)
res.range.unlikely = res.range.max + target_mb_len_max () - 1;
break; break;
} }
...@@ -1484,12 +1593,15 @@ format_floating (const directive &dir) ...@@ -1484,12 +1593,15 @@ format_floating (const directive &dir)
the lower bound on the range of bytes (not counting flags the lower bound on the range of bytes (not counting flags
or width) is 1. */ or width) is 1. */
res.range.min = flagmin; res.range.min = flagmin;
if (res.range.max == HOST_WIDE_INT_M1U)
{ HOST_WIDE_INT maxprec
/* Compute the upper bound for -TYPE_MAX which should be = dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
the lesser of %e and %f. */ res.range.max = format_floating_max (type, 'g', maxprec);
res.range.max = format_floating_max (type, 'g', dir.prec); res.range.likely = res.range.max;
}
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
res.range.unlikely = res.range.max + target_mb_len_max () - 1;
break; break;
} }
...@@ -1497,14 +1609,8 @@ format_floating (const directive &dir) ...@@ -1497,14 +1609,8 @@ format_floating (const directive &dir)
return fmtresult (); return fmtresult ();
} }
if (dir.width > 0) /* Bump up the byte counters if WIDTH is greater. */
{ res.adjust_for_width_or_precision (dir.width);
/* If width has been specified use it to adjust the range. */
if (res.range.min < (unsigned)dir.width)
res.range.min = dir.width;
if (res.range.max < (unsigned)dir.width)
res.range.max = dir.width;
}
return res; return res;
} }
...@@ -1519,13 +1625,29 @@ format_floating (const directive &dir, tree arg) ...@@ -1519,13 +1625,29 @@ format_floating (const directive &dir, tree arg)
if (!arg || TREE_CODE (arg) != REAL_CST) if (!arg || TREE_CODE (arg) != REAL_CST)
return format_floating (dir); return format_floating (dir);
HOST_WIDE_INT prec = dir.prec; HOST_WIDE_INT prec[] = { dir.prec, dir.prec };
if (prec < 0 && TOUPPER (dir.specifier) != 'A') if (TOUPPER (dir.specifier) == 'A')
{ {
/* Specify the precision explicitly since mpfr_sprintf defaults /* For %a, leave the minimum precision unspecified to let
to zero. */ MFPR trim trailing zeros (as it and many other systems
prec = 6; including Glibc happen to do) and set the maximum
precision to reflect what it would be with trailing zeros
present (as Solaris and derived systems do). */
if (prec[0] < 0)
prec[0] = -1;
if (prec[1] < 0)
{
unsigned fmtprec
= (dir.modifier == FMT_LEN_L
? REAL_MODE_FORMAT (XFmode)->p
: REAL_MODE_FORMAT (DFmode)->p);
/* The precision of the IEEE 754 double format is 53.
The precision of all other GCC binary double formats
is 56 or less. */
prec[1] = fmtprec <= 56 ? 13 : 15;
}
} }
/* The minimum and maximum number of bytes produced by the directive. */ /* The minimum and maximum number of bytes produced by the directive. */
...@@ -1569,14 +1691,8 @@ format_floating (const directive &dir, tree arg) ...@@ -1569,14 +1691,8 @@ format_floating (const directive &dir, tree arg)
/* Format it and store the result in the corresponding member /* Format it and store the result in the corresponding member
of the result struct. */ of the result struct. */
unsigned HOST_WIDE_INT len *minmax[i] = get_mpfr_format_length (mpfrval, fmtstr, prec[i],
= get_mpfr_format_length (mpfrval, fmtstr, prec, dir.specifier, rndspec);
dir.specifier, rndspec);
if (0 < dir.width && len < (unsigned)dir.width)
len = dir.width;
*minmax[i] = len;
} }
} }
...@@ -1589,23 +1705,25 @@ format_floating (const directive &dir, tree arg) ...@@ -1589,23 +1705,25 @@ format_floating (const directive &dir, tree arg)
res.range.max = tmp; res.range.max = tmp;
} }
/* The range of output is known even if the result isn't bounded. */ res.knownrange = true;
if (dir.width == HOST_WIDE_INT_MIN)
/* For the same floating point constant use the longer output
as the likely maximum since with round to nearest either is
equally likely. */
res.range.likely = res.range.max;
res.range.unlikely = res.range.max;
if (res.range.max > 2 && (prec[0] != 0 || prec[1] != 0))
{ {
res.knownrange = false; /* Unless the precision is zero output longer than 2 bytes may
res.range.max = HOST_WIDE_INT_MAX; include the decimal point which must be a single character
up to MB_LEN_MAX in length. This is overly conservative
since in some conversions some constants result in no decimal
point (e.g., in %g). */
res.range.unlikely += target_mb_len_max () - 1;
} }
else
res.knownrange = true;
/* The output of all directives except "%a" is fully specified res.adjust_for_width_or_precision (dir.width);
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 = (res.knownrange
&& (TOUPPER (dir.specifier) != 'A'
|| (0 <= dir.prec && (unsigned) dir.prec < target_int_max ()))
&& res.range.min < target_int_max ());
return res; return res;
} }
...@@ -1623,10 +1741,7 @@ get_string_length (tree str) ...@@ -1623,10 +1741,7 @@ get_string_length (tree str)
if (tree slen = c_strlen (str, 1)) if (tree slen = c_strlen (str, 1))
{ {
/* Simply return the length of the string. */ /* Simply return the length of the string. */
fmtresult res; fmtresult res (tree_to_shwi (slen));
res.range.min = res.range.max = tree_to_shwi (slen);
res.bounded = true;
res.knownrange = true;
return res; return res;
} }
...@@ -1640,18 +1755,45 @@ get_string_length (tree str) ...@@ -1640,18 +1755,45 @@ get_string_length (tree str)
if (lenrange [0] || lenrange [1]) if (lenrange [0] || lenrange [1])
{ {
fmtresult res; HOST_WIDE_INT min
= (tree_fits_uhwi_p (lenrange[0])
? tree_to_uhwi (lenrange[0])
: 0);
res.range.min = (tree_fits_uhwi_p (lenrange[0]) HOST_WIDE_INT max
? tree_to_uhwi (lenrange[0]) : warn_format_overflow > 1); = (tree_fits_uhwi_p (lenrange[1])
res.range.max = (tree_fits_uhwi_p (lenrange[1]) ? tree_to_uhwi (lenrange[1])
? tree_to_uhwi (lenrange[1]) : HOST_WIDE_INT_M1U); : HOST_WIDE_INT_M1U);
/* Set RES.BOUNDED to true if and only if all strings referenced /* get_range_strlen() returns the target value of SIZE_MAX for
strings of unknown length. Bump it up to HOST_WIDE_INT_M1U
which may be bigger. */
if ((unsigned HOST_WIDE_INT)min == target_size_max ())
min = HOST_WIDE_INT_M1U;
if ((unsigned HOST_WIDE_INT)max == target_size_max ())
max = HOST_WIDE_INT_M1U;
fmtresult res (min, max);
/* Set RES.KNOWNRANGE to true if and only if all strings referenced
by STR are known to be bounded (though not necessarily by their by STR are known to be bounded (though not necessarily by their
actual length but perhaps by their maximum possible length). */ actual length but perhaps by their maximum possible length). */
res.bounded = res.range.max < target_int_max (); if (res.range.max < target_int_max ())
res.knownrange = res.bounded; {
res.knownrange = true;
/* When the the length of the longest string is known and not
excessive use it as the likely length of the string(s). */
res.range.likely = res.range.max;
}
else
{
/* When the upper bound is unknown (as assumed to be excessive)
set the likely length to the greater of 1 and the length of
the shortest string. */
res.range.likely = res.range.min ? res.range.min : warn_level > 1;
}
res.range.unlikely = res.range.max;
return res; return res;
} }
...@@ -1669,58 +1811,54 @@ format_character (const directive &dir, tree arg) ...@@ -1669,58 +1811,54 @@ format_character (const directive &dir, tree arg)
{ {
fmtresult res; fmtresult res;
/* The maximum number of bytes for an unknown wide character argument res.knownrange = true;
to a "%lc" directive adjusted for precision but not field width.
6 is the longest UTF-8 sequence for a single wide character. */ if (dir.modifier == FMT_LEN_l)
const unsigned HOST_WIDE_INT max_bytes_for_unknown_wc {
= (0 <= dir.prec ? dir.prec : warn_level > 1 ? 6 : 1); unsigned HOST_WIDE_INT val;
if (dir.modifier == FMT_LEN_l) if (arg && TREE_CODE (arg) == INTEGER_CST && tree_fits_shwi_p (arg))
{ val = tree_to_shwi (arg);
/* Positive if the argument is a wide NUL character. */ else
int nul = (arg && TREE_CODE (arg) == INTEGER_CST val = HOST_WIDE_INT_MAX;
? integer_zerop (arg) : -1);
/* A wide character can result in as few as zero bytes. */
/* A '%lc' directive is the same as '%ls' for a two element res.range.min = 0;
wide string character with the second element of NUL, so
when the character is unknown the minimum number of bytes if (val == 0)
is the smaller of either 0 (at level 1) or 1 (at level 2) {
and WIDTH, and the maximum is MB_CUR_MAX in the selected /* The NUL wide character results in no bytes. */
locale, which is unfortunately, unknown. */ res.range.max = 0;
res.range.min = warn_level == 1 ? !nul : nul < 1; res.range.likely = 0;
res.range.max = max_bytes_for_unknown_wc; res.range.unlikely = 0;
/* The range above is good enough to issue warnings but not }
for value range propagation, so clear BOUNDED. */ else if (0 < val && val < 128)
res.bounded = false; {
/* A wide character in the ASCII range most likely results
in a single byte, and only unlikely in up to MB_LEN_MAX. */
res.range.max = 1;
res.range.likely = 1;
res.range.unlikely = target_mb_len_max ();
}
else
{
/* A wide character outside the ASCII range likely results
in up to two bytes, and only unlikely in up to MB_LEN_MAX. */
res.range.max = target_mb_len_max ();
res.range.likely = 2;
res.range.unlikely = res.range.max;
}
} }
else else
{ {
/* A plain '%c' directive. Its ouput is exactly 1. */ /* A plain '%c' directive. Its ouput is exactly 1. */
res.range.min = res.range.max = 1; res.range.min = res.range.max = 1;
res.bounded = true; res.range.likely = res.range.unlikely = res.range.min;
res.knownrange = true; res.knownrange = true;
} }
/* Adjust the lengths for field width. */ /* Bump up the byte counters if WIDTH is greater. */
if (0 < dir.width) return res.adjust_for_width_or_precision (dir.width);
{
if (res.range.min < (unsigned HOST_WIDE_INT)dir.width)
res.range.min = dir.width;
if (res.range.max < (unsigned HOST_WIDE_INT)dir.width)
res.range.max = dir.width;
/* Adjust BOUNDED if width happens to make them equal. */
if (res.range.min == res.range.max && res.range.min < target_int_max ())
res.bounded = true;
}
/* When precision is specified the range of characters on output
is known to be bounded by it. */
if (-1 < dir.width && -1 < dir.prec)
res.knownrange = true;
return res;
} }
/* Return the minimum and maximum number of characters formatted /* Return the minimum and maximum number of characters formatted
...@@ -1733,83 +1871,62 @@ format_string (const directive &dir, tree arg) ...@@ -1733,83 +1871,62 @@ format_string (const directive &dir, tree arg)
{ {
fmtresult res; fmtresult res;
/* The maximum number of bytes for an unknown string argument to either
a "%s" or "%ls" directive adjusted for precision but not field width. */
const unsigned HOST_WIDE_INT max_bytes_for_unknown_str
= (0 <= dir.prec ? dir.prec : warn_format_overflow > 1);
/* The result is bounded unless overriddden for a non-constant string
of an unknown length. */
bool bounded = true;
/* Compute the range the argument's length can be in. */ /* Compute the range the argument's length can be in. */
fmtresult slen = get_string_length (arg); fmtresult slen = get_string_length (arg);
if (slen.range.min == slen.range.max if (slen.range.min == slen.range.max
&& slen.range.min < HOST_WIDE_INT_MAX) && slen.range.min < HOST_WIDE_INT_MAX)
{ {
gcc_checking_assert (slen.range.min == slen.range.max); /* The argument is either a string constant or it refers
to one of a number of strings of the same length. */
/* A '%s' directive with a string argument with constant length. */ /* A '%s' directive with a string argument with constant length. */
res.range = slen.range; res.range = slen.range;
/* The output of "%s" and "%ls" directives with a constant
string is in a known range unless width of an unknown value
is specified. 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 = HOST_WIDE_INT_MIN != dir.width;
if (dir.modifier == FMT_LEN_l) if (dir.modifier == FMT_LEN_l)
{ {
bounded = false; /* In the worst case the length of output of a wide string S
is bounded by MB_LEN_MAX * wcslen (S). */
if (warn_level > 1) res.range.max *= target_mb_len_max ();
{ res.range.unlikely = res.range.max;
/* Leave the minimum number of bytes the wide string /* It's likely that the the total length is not more that
converts to equal to its length and set the maximum 2 * wcslen (S).*/
to the worst case length which is the string length res.range.likely = res.range.min * 2;
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 *= target_mb_len_max();
}
/* For a wide character string, use precision as the maximum /* For a wide character string, use precision as the maximum
even if precision is greater than the string length since even if precision is greater than the string length since
the number of bytes the string converts to may be greater the number of bytes the string converts to may be greater
(due to MB_CUR_MAX). */ (due to MB_CUR_MAX). */
if (0 <= dir.prec) if (0 <= dir.prec
res.range.max = dir.prec; && (unsigned HOST_WIDE_INT)dir.prec < res.range.max)
} {
else if (-1 <= dir.width) res.range.max = dir.prec;
{ res.range.likely = dir.prec;
/* The output of a "%s" directive with a constant argument res.range.unlikely = dir.prec;
and constant or no width is bounded. It is constant if }
precision is either not specified or it is specified and
its value is known. */
res.bounded = true;
}
else if (dir.width == HOST_WIDE_INT_MIN)
{
/* Specified but unknown width makes the output unbounded. */
res.range.max = HOST_WIDE_INT_MAX;
}
if (0 <= dir.prec && (unsigned HOST_WIDE_INT)dir.prec < res.range.min) /* Even a non-empty wide character string need not convert into
{ any bytes. */
res.range.min = dir.prec; res.range.min = 0;
res.range.max = dir.prec;
} }
else if (dir.prec == HOST_WIDE_INT_MIN) else
{ {
/* When precision is specified but not known the lower res.knownrange = true;
bound is assumed to be as low as zero. */
res.range.min = 0; if (dir.prec == HOST_WIDE_INT_MIN)
res.range.min = 0;
else if ((unsigned HOST_WIDE_INT)dir.prec < res.range.min)
{
res.range.min = dir.prec;
res.range.max = dir.prec;
res.range.likely = dir.prec;
res.range.unlikely = dir.prec;
}
else if ((unsigned HOST_WIDE_INT)dir.prec < res.range.max)
{
res.range.max = dir.prec;
res.range.likely = dir.prec;
res.range.unlikely = dir.prec;
}
} }
} }
else if (arg && integer_zerop (arg)) else if (arg && integer_zerop (arg))
...@@ -1833,49 +1950,110 @@ format_string (const directive &dir, tree arg) ...@@ -1833,49 +1950,110 @@ format_string (const directive &dir, tree arg)
if (slen.range.min >= target_int_max ()) if (slen.range.min >= target_int_max ())
slen.range.min = 0; slen.range.min = 0;
else if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.min) else if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.min)
slen.range.min = dir.prec; {
slen.range.min = dir.prec;
slen.range.likely = slen.range.min;
}
if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.max if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.max
|| slen.range.max >= target_int_max ()) || slen.range.max >= target_int_max ())
slen.range.max = dir.prec; {
slen.range.max = dir.prec;
slen.range.likely = slen.range.max;
}
} }
else if (slen.range.min >= target_int_max ()) else if (slen.range.min >= target_int_max ())
{ {
slen.range.min = max_bytes_for_unknown_str; slen.range.min = 0;
slen.range.max = max_bytes_for_unknown_str; slen.range.max = HOST_WIDE_INT_MAX;
bounded = false; /* At level one strings of unknown length are assumed to be
empty, while at level 1 they are assumed to be one byte
long. */
slen.range.likely = warn_level > 1;
} }
res.range = slen.range; slen.range.unlikely = slen.range.max;
/* The output is considered bounded when a precision has been res.range = slen.range;
specified to limit the number of bytes or when the number
of bytes is known or contrained to some range. */
res.bounded = 0 <= dir.prec || slen.bounded;
res.knownrange = slen.knownrange; res.knownrange = slen.knownrange;
} }
/* Adjust the lengths for field width. */ /* Bump up the byte counters if WIDTH is greater. */
if (0 < dir.width) return res.adjust_for_width_or_precision (dir.width);
}
/* Format plain string (part of the format string itself). */
static fmtresult
format_plain (const directive &dir, tree)
{
fmtresult res (dir.len);
return res;
}
/* Return true if the RESULT of a directive in a call describe by INFO
should be diagnosed given the AVAILable space in the destination. */
static bool
should_warn_p (const pass_sprintf_length::call_info &info,
const result_range &avail, const result_range &result)
{
if (result.max <= avail.min)
{
/* The least amount of space remaining in the destination is big
enough for the longest output. */
return false;
}
if (info.bounded)
{ {
if (res.range.min < (unsigned HOST_WIDE_INT)dir.width) if (1 == warn_format_trunc && result.min <= avail.max
res.range.min = dir.width; && info.retval_used ())
{
/* The likely amount of space remaining in the destination is big
enough for the least output and the return value is used. */
return false;
}
if (res.range.max < (unsigned HOST_WIDE_INT)dir.width) if (1 == warn_format_trunc && result.likely <= avail.likely
res.range.max = dir.width; && !info.retval_used ())
{
/* The likely amount of space remaining in the destination is big
enough for the likely output and the return value is unused. */
return false;
}
/* Adjust BOUNDED if width happens to make them equal. */ if (warn_format_trunc == 2
if (res.range.min == res.range.max && res.range.min < target_int_max () && result.likely <= avail.min
&& bounded) && (result.max <= avail.min
res.bounded = true; || result.max > HOST_WIDE_INT_MAX))
{
/* The minimum amount of space remaining in the destination is big
enough for the longest output. */
return false;
}
} }
else
{
if (1 == warn_level && result.likely <= avail.likely)
{
/* The likely amount of space remaining in the destination is big
enough for the likely output. */
return false;
}
/* When precision is specified the range of characters on output if (warn_level == 2
is known to be bounded by it. */ && result.likely <= avail.min
if (HOST_WIDE_INT_MIN != dir.width && -1 < dir.prec) && (result.max <= avail.min
res.knownrange = true; || result.max > HOST_WIDE_INT_MAX))
{
/* The minimum amount of space remaining in the destination is big
enough for the longest output. */
return false;
}
}
return res; return true;
} }
/* At format string location describe by DIRLOC in a call described /* At format string location describe by DIRLOC in a call described
...@@ -1889,132 +2067,294 @@ format_string (const directive &dir, tree arg) ...@@ -1889,132 +2067,294 @@ format_string (const directive &dir, tree arg)
static bool static bool
maybe_warn (substring_loc &dirloc, source_range *pargrange, maybe_warn (substring_loc &dirloc, source_range *pargrange,
const pass_sprintf_length::call_info &info, const pass_sprintf_length::call_info &info,
unsigned HOST_WIDE_INT navail, const result_range &res, const result_range &avail_range, const result_range &res,
const directive &dir) const directive &dir)
{ {
bool warned = false; if (!should_warn_p (info, avail_range, res))
return false;
if (res.min < res.max) /* A warning will definitely be issued below. */
/* The maximum byte count to reference in the warning. Larger counts
imply that the upper bound is unknown (and could be anywhere between
RES.MIN + 1 and SIZE_MAX / 2) are printed as "N or more bytes" rather
than "between N and X" where X is some huge number. */
unsigned HOST_WIDE_INT maxbytes = target_dir_max ();
/* True when there is enough room in the destination for the least
amount of a directive's output but not enough for its likely or
maximum output. */
bool maybe = (res.min <= avail_range.max
&& (avail_range.min < res.likely
|| (res.max < HOST_WIDE_INT_MAX
&& avail_range.min < res.max)));
if (avail_range.min == avail_range.max)
{ {
/* The result is a range (i.e., it's inexact). */ /* The size of the destination region is exact. */
if (!warned) unsigned HOST_WIDE_INT navail = avail_range.max;
if (*dir.beg != '%')
{ {
if (navail < res.min) /* For plain character directives (i.e., the format string itself)
{ but not others, point the caret at the first character that's
/* The minimum directive output is longer than there is past the end of the destination. */
room in the destination. */ dirloc.set_caret_index (dirloc.get_caret_idx () + navail);
if (res.min == res.max)
{
const char* fmtstr
= (info.bounded
? G_("%<%.*s%> directive output truncated writing "
"%wu bytes into a region of size %wu")
: G_("%<%.*s%> directive writing %wu bytes "
"into a region of size %wu"));
warned = fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
fmtstr,
(int)dir.len, dir.beg, res.min,
navail);
}
else if (res.max < HOST_WIDE_INT_MAX)
{
const char* fmtstr
= (info.bounded
? G_("%<%.*s%> directive output truncated writing "
"between %wu and %wu bytes into a region of "
"size %wu")
: G_("%<%.*s%> directive writing between %wu and "
"%wu bytes into a region of size %wu"));
warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
(int)dir.len, dir.beg,
res.min, res.max, navail);
}
else
{
const char* fmtstr
= (info.bounded
? G_("%<%.*s%> directive output truncated writing "
"%wu or more bytes into a region of size %wu")
: G_("%<%.*s%> directive writing %wu or more bytes "
"into a region of size %wu"));
warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
(int)dir.len, dir.beg,
res.min, navail);
}
}
else if (navail < res.max
&& (dir.specifier != 's'
|| res.max < HOST_WIDE_INT_MAX)
&& ((info.bounded
&& (!info.retval_used ()
|| warn_level > 1))
|| (!info.bounded
&& (dir.specifier == 's'
|| warn_level > 1))))
{
/* The maximum directive output is longer than there is
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 (res.max >= HOST_WIDE_INT_MAX)
{
const char* fmtstr
= (info.bounded
? G_("%<%.*s%> directive output may be truncated "
"writing %wu or more bytes into a region "
"of size %wu")
: G_("%<%.*s%> directive writing %wu or more bytes "
"into a region of size %wu"));
warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
(int)dir.len, dir.beg,
res.min, navail);
}
else
{
const char* fmtstr
= (info.bounded
? G_("%<%.*s%> directive output may be truncated "
"writing between %wu and %wu bytes into a region "
"of size %wu")
: G_("%<%.*s%> directive writing between %wu and %wu "
"bytes into a region of size %wu"));
warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
(int)dir.len, dir.beg,
res.min, res.max,
navail);
}
}
} }
}
else if (*dir.beg == '\0')
{
if (!warned && res.min > 0 && navail < res.min)
{ {
const char* fmtstr /* This is the terminating nul. */
gcc_assert (res.min == 1 && res.min == res.max);
const char *fmtstr
= (info.bounded = (info.bounded
? (1 < res.min ? (maybe
? G_("%<%.*s%> directive output truncated while writing " ? G_("%qE output may be truncated before the last format "
"%wu bytes into a region of size %wu") "character")
: G_("%<%.*s%> directive output truncated while writing " : G_("%qE output truncated before the last format character"))
"%wu byte into a region of size %wu")) : (maybe
: (1 < res.min ? G_("%qE may write a terminating nul past the end "
? G_("%<%.*s%> directive writing %wu bytes " "of the destination")
"into a region of size %wu") : G_("%qE writing a terminating nul past the end "
"of the destination")));
return fmtwarn (dirloc, NULL, NULL, info.warnopt (), fmtstr,
info.func);
}
if (res.min == res.max)
{
const char* fmtstr
= (res.min == 1
? (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"%wu byte into a region of size %wu")
: G_("%<%.*s%> directive output truncated writing "
"%wu byte into a region of size %wu"))
: G_("%<%.*s%> directive writing %wu byte " : G_("%<%.*s%> directive writing %wu byte "
"into a region of size %wu"))
: (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"%wu bytes into a region of size %wu")
: G_("%<%.*s%> directive output truncated writing "
"%wu bytes into a region of size %wu"))
: G_("%<%.*s%> directive writing %wu bytes "
"into a region of size %wu"))); "into a region of size %wu")));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.min,
navail);
}
warned = fmtwarn (dirloc, pargrange, NULL, if (0 == res.min && res.max < maxbytes)
info.warnopt (), fmtstr, {
(int)dir.len, dir.beg, res.min, const char* fmtstr
navail); = (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"up to %wu bytes into a region of size %wu")
: G_("%<%.*s%> directive output truncated writing "
"up to %wu bytes into a region of size %wu"))
: G_("%<%.*s%> directive writing up to %wu bytes "
"into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.max, navail);
} }
if (0 == res.min && maxbytes <= res.max)
{
/* This is a special case to avoid issuing the potentially
confusing warning:
writing 0 or more bytes into a region of size 0. */
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"likely %wu or more bytes into a region of size %wu")
: G_("%<%.*s%> directive output truncated writing "
"likely %wu or more bytes into a region of size %wu"))
: G_("%<%.*s%> directive writing likely %wu or more bytes "
"into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.likely, navail);
}
if (res.max < maxbytes)
{
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"between %wu and %wu bytes into a region of size %wu")
: G_("%<%.*s%> directive output truncated writing "
"between %wu and %wu bytes into a region of size %wu"))
: G_("%<%.*s%> directive writing between %wu and "
"%wu bytes into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min, res.max,
navail);
}
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"%wu or more bytes into a region of size %wu")
: G_("%<%.*s%> directive output truncated writing "
"%wu or more bytes into a region of size %wu"))
: G_("%<%.*s%> directive writing %wu or more bytes "
"into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min, navail);
} }
return warned; /* The size of the destination region is a range. */
if (*dir.beg != '%')
{
unsigned HOST_WIDE_INT navail = avail_range.max;
/* For plain character directives (i.e., the format string itself)
but not others, point the caret at the first character that's
past the end of the destination. */
dirloc.set_caret_index (dirloc.get_caret_idx () + navail);
}
if (*dir.beg == '\0')
{
gcc_assert (res.min == 1 && res.min == res.max);
const char *fmtstr
= (info.bounded
? (maybe
? G_("%qE output may be truncated before the last format "
"character")
: G_("%qE output truncated before the last format character"))
: (maybe
? G_("%qE may write a terminating nul past the end "
"of the destination")
: G_("%qE writing a terminating nul past the end "
"of the destination")));
return fmtwarn (dirloc, NULL, NULL, info.warnopt (), fmtstr,
info.func);
}
if (res.min == res.max)
{
const char* fmtstr
= (res.min == 1
? (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"%wu byte into a region of size between %wu and %wu")
: G_("%<%.*s%> directive output truncated writing "
"%wu byte into a region of size between %wu and %wu"))
: G_("%<%.*s%> directive writing %wu byte "
"into a region of size between %wu and %wu"))
: (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"%wu bytes into a region of size between %wu and %wu")
: G_("%<%.*s%> directive output truncated writing "
"%wu bytes into a region of size between %wu and %wu"))
: G_("%<%.*s%> directive writing %wu bytes "
"into a region of size between %wu and %wu")));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.min,
avail_range.min, avail_range.max);
}
if (0 == res.min && res.max < maxbytes)
{
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"up to %wu bytes into a region of size between "
"%wu and %wu")
: G_("%<%.*s%> directive output truncated writing "
"up to %wu bytes into a region of size between "
"%wu and %wu"))
: G_("%<%.*s%> directive writing up to %wu bytes "
"into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.max,
avail_range.min, avail_range.max);
}
if (0 == res.min && maxbytes <= res.max)
{
/* This is a special case to avoid issuing the potentially confusing
warning:
writing 0 or more bytes into a region of size between 0 and N. */
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"likely %wu or more bytes into a region of size between "
"%wu and %wu")
: G_("%<%.*s%> directive output truncated writing likely "
"%wu or more bytes into a region of size between "
"%wu and %wu"))
: G_("%<%.*s%> directive writing likely %wu or more bytes "
"into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.likely,
avail_range.min, avail_range.max);
}
if (res.max < maxbytes)
{
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"between %wu and %wu bytes into a region of size "
"between %wu and %wu")
: G_("%<%.*s%> directive output truncated writing "
"between %wu and %wu bytes into a region of size "
"between %wu and %wu"))
: G_("%<%.*s%> directive writing between %wu and "
"%wu bytes into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min, res.max,
avail_range.min, avail_range.max);
}
const char* fmtstr
= (info.bounded
? (maybe
? G_("%<%.*s%> directive output may be truncated writing "
"%wu or more bytes into a region of size between "
"%wu and %wu")
: G_("%<%.*s%> directive output truncated writing "
"%wu or more bytes into a region of size between "
"%wu and %wu"))
: G_("%<%.*s%> directive writing %wu or more bytes "
"into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min,
avail_range.min, avail_range.max);
} }
/* Compute the length of the output resulting from the conversion /* Compute the length of the output resulting from the conversion
...@@ -2053,16 +2393,12 @@ format_directive (const pass_sprintf_length::call_info &info, ...@@ -2053,16 +2393,12 @@ format_directive (const pass_sprintf_length::call_info &info,
/* Bail when there is no function to compute the output length, /* Bail when there is no function to compute the output length,
or when minimum length checking has been disabled. */ or when minimum length checking has been disabled. */
if (!dir.fmtfunc || res->number_chars_min >= HOST_WIDE_INT_MAX) if (!dir.fmtfunc || res->range.min >= HOST_WIDE_INT_MAX)
return false; return false;
/* Compute the (approximate) length of the formatted output. */ /* Compute the (approximate) length of the formatted output. */
fmtresult fmtres = dir.fmtfunc (dir, arg); fmtresult fmtres = dir.fmtfunc (dir, arg);
/* The overall result is bounded only if the output of every directive
is bounded. */
res->bounded &= fmtres.bounded;
/* Record whether the output of all directives is known to be /* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are bounded by some maximum, implying that their arguments are
either known exactly or determined to be in a known range either known exactly or determined to be in a known range
...@@ -2089,8 +2425,7 @@ format_directive (const pass_sprintf_length::call_info &info, ...@@ -2089,8 +2425,7 @@ format_directive (const pass_sprintf_length::call_info &info,
to determine the maximum number of characters (for example to determine the maximum number of characters (for example
for wide characters or wide character strings) but continue for wide characters or wide character strings) but continue
tracking the minimum number of characters. */ tracking the minimum number of characters. */
res->number_chars_max = HOST_WIDE_INT_M1U; res->range.max = HOST_WIDE_INT_M1U;
res->number_chars = HOST_WIDE_INT_M1U;
} }
if (fmtres.range.min > target_dir_max ()) if (fmtres.range.min > target_dir_max ())
...@@ -2099,56 +2434,67 @@ format_directive (const pass_sprintf_length::call_info &info, ...@@ -2099,56 +2434,67 @@ format_directive (const pass_sprintf_length::call_info &info,
even the minimum number of characters (it shouldn't happen even the minimum number of characters (it shouldn't happen
except in an error) but keep tracking the minimum and maximum except in an error) but keep tracking the minimum and maximum
number of characters. */ number of characters. */
res->number_chars = HOST_WIDE_INT_M1U;
return true; return true;
} }
} }
int dirlen = dir.len;
if (fmtres.nullp) if (fmtres.nullp)
{ {
fmtwarn (dirloc, pargrange, NULL, info.warnopt (), fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
"%<%.*s%> directive argument is null", "%<%.*s%> directive argument is null",
(int)cvtlen, cvtbeg); dirlen, dir.beg);
/* Don't bother processing the rest of the format string. */ /* Don't bother processing the rest of the format string. */
res->warned = true; res->warned = true;
res->number_chars = HOST_WIDE_INT_M1U; res->range.min = HOST_WIDE_INT_M1U;
res->number_chars_min = res->number_chars_max = res->number_chars; res->range.max = HOST_WIDE_INT_M1U;
return false; return false;
} }
/* Compute the number of available bytes in the destination. There /* Compute the number of available bytes in the destination. There
must always be at least one byte of space for the terminating must always be at least one byte of space for the terminating
NUL that's appended after the format string has been processed. */ NUL that's appended after the format string has been processed. */
unsigned HOST_WIDE_INT navail = min_bytes_remaining (info.objsize, *res); result_range avail_range = bytes_remaining (info.objsize, *res);
bool warned = res->warned; bool warned = res->warned;
if (!warned) if (!warned)
warned = maybe_warn (dirloc, pargrange, info, navail, warned = maybe_warn (dirloc, pargrange, info, avail_range,
fmtres.range, dir); fmtres.range, dir);
if (fmtres.range.min < fmtres.range.max) /* Bump up the total maximum if it isn't too big. */
{ if (res->range.max < HOST_WIDE_INT_MAX
/* Disable exact length checking but adjust the minimum and maximum. */ && fmtres.range.max < HOST_WIDE_INT_MAX)
res->number_chars = HOST_WIDE_INT_M1U; res->range.max += fmtres.range.max;
if (res->number_chars_max < HOST_WIDE_INT_MAX
&& fmtres.range.max < HOST_WIDE_INT_MAX)
res->number_chars_max += fmtres.range.max;
res->number_chars_min += fmtres.range.min; /* Raise the total unlikely maximum by the larger of the maximum
} and the unlikely maximum. It doesn't matter if the unlikely
maximum overflows. */
if (fmtres.range.max < fmtres.range.unlikely)
res->range.unlikely += fmtres.range.unlikely;
else else
*res += fmtres.range.min; res->range.unlikely += fmtres.range.max;
res->range.min += fmtres.range.min;
res->range.likely += fmtres.range.likely;
/* Has the minimum directive output length exceeded the maximum /* Has the minimum directive output length exceeded the maximum
of 4095 bytes required to be supported? */ of 4095 bytes required to be supported? */
bool minunder4k = fmtres.range.min < 4096; bool minunder4k = fmtres.range.min < 4096;
if (!minunder4k || fmtres.range.max > 4095) bool maxunder4k = fmtres.range.max < 4096;
/* Clear UNDER4K in the overall result if the maximum has exceeded
the 4k (this is necessary to avoid the return valuye optimization
that may not be safe in the maximum case). */
if (!maxunder4k)
res->under4k = false; res->under4k = false;
if (!warned && warn_level > 1 if (!warned
&& (!minunder4k || fmtres.range.max > 4095)) /* Only warn at level 2. */
&& 1 < warn_level
&& (!minunder4k
|| (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX)))
{ {
/* The directive output may be longer than the maximum required /* The directive output may be longer than the maximum required
to be handled by an implementation according to 7.21.6.1, p15 to be handled by an implementation according to 7.21.6.1, p15
...@@ -2162,30 +2508,35 @@ format_directive (const pass_sprintf_length::call_info &info, ...@@ -2162,30 +2508,35 @@ format_directive (const pass_sprintf_length::call_info &info,
info.warnopt (), info.warnopt (),
"%<%.*s%> directive output of %wu bytes exceeds " "%<%.*s%> directive output of %wu bytes exceeds "
"minimum required size of 4095", "minimum required size of 4095",
(int)cvtlen, cvtbeg, fmtres.range.min); dirlen, dir.beg, fmtres.range.min);
else else
{ {
const char *fmtstr const char *fmtstr
= (minunder4k = (minunder4k
? G_("%<%.*s%> directive output between %qu and %wu " ? G_("%<%.*s%> directive output between %wu and %wu "
"bytes may exceed minimum required size of 4095") "bytes may exceed minimum required size of 4095")
: G_("%<%.*s%> directive output between %qu and %wu " : G_("%<%.*s%> directive output between %wu and %wu "
"bytes exceeds minimum required size of 4095")); "bytes exceeds minimum required size of 4095"));
warned = fmtwarn (dirloc, pargrange, NULL, warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr, info.warnopt (), fmtstr,
(int)cvtlen, cvtbeg, dirlen, dir.beg,
fmtres.range.min, fmtres.range.max); fmtres.range.min, fmtres.range.max);
} }
} }
/* Has the minimum directive output length exceeded INT_MAX? */ /* Has the minimum directive output length exceeded INT_MAX? */
bool exceedmin = res->number_chars_min > target_int_max (); /* Has the likely and maximum directive output exceeded INT_MAX? */
bool likelyximax = *dir.beg && res->range.likely > target_int_max ();
bool maxximax = *dir.beg && res->range.max > target_int_max ();
if (!warned if (!warned
&& (exceedmin /* Warn for the likely output size at level 1. */
|| (warn_level > 1 && (likelyximax
&& res->number_chars_max > target_int_max ()))) /* But only warn for the maximum at level 2. */
|| (1 < warn_level
&& maxximax
&& fmtres.range.max < HOST_WIDE_INT_MAX)))
{ {
/* The directive output causes the total length of output /* The directive output causes the total length of output
to exceed INT_MAX bytes. */ to exceed INT_MAX bytes. */
...@@ -2194,22 +2545,32 @@ format_directive (const pass_sprintf_length::call_info &info, ...@@ -2194,22 +2545,32 @@ format_directive (const pass_sprintf_length::call_info &info,
warned = fmtwarn (dirloc, pargrange, NULL, info.warnopt (), warned = fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
"%<%.*s%> directive output of %wu bytes causes " "%<%.*s%> directive output of %wu bytes causes "
"result to exceed %<INT_MAX%>", "result to exceed %<INT_MAX%>",
(int)cvtlen, cvtbeg, fmtres.range.min); dirlen, dir.beg, fmtres.range.min);
else else
{ {
const char *fmtstr const char *fmtstr
= (exceedmin = (fmtres.range.min > target_int_max ()
? G_ ("%<%.*s%> directive output between %wu and %wu " ? G_ ("%<%.*s%> directive output between %wu and %wu "
"bytes causes result to exceed %<INT_MAX%>") "bytes causes result to exceed %<INT_MAX%>")
: G_ ("%<%.*s%> directive output between %wu and %wu " : G_ ("%<%.*s%> directive output between %wu and %wu "
"bytes may cause result to exceed %<INT_MAX%>")); "bytes may cause result to exceed %<INT_MAX%>"));
warned = fmtwarn (dirloc, pargrange, NULL, warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr, info.warnopt (), fmtstr,
(int)cvtlen, cvtbeg, dirlen, dir.beg,
fmtres.range.min, fmtres.range.max); fmtres.range.min, fmtres.range.max);
} }
} }
if (warned && fmtres.range.min < fmtres.range.likely
&& fmtres.range.likely < fmtres.range.max)
{
inform (info.fmtloc,
(1 == fmtres.range.likely
? G_("assuming directive output of %wu byte")
: G_("assuming directive output of %wu bytes")),
fmtres.range.likely);
}
if (warned && fmtres.argmin) if (warned && fmtres.argmin)
{ {
if (fmtres.argmin == fmtres.argmax) if (fmtres.argmin == fmtres.argmax)
...@@ -2225,138 +2586,7 @@ format_directive (const pass_sprintf_length::call_info &info, ...@@ -2225,138 +2586,7 @@ format_directive (const pass_sprintf_length::call_info &info,
res->warned |= warned; res->warned |= warned;
if (dump_file && *dir.beg) if (!dir.beg[0] && res->warned && info.objsize < HOST_WIDE_INT_MAX)
{
fprintf (dump_file, " Result: %lli, %lli "
"(%lli, %lli, %lli)\n",
(long long)fmtres.range.min,
(long long)fmtres.range.max,
(long long)res->number_chars,
(long long)res->number_chars_min,
(long long)res->number_chars_max);
}
return true;
}
/* Account for the number of bytes between BEG and END (or between
BEG + strlen (BEG) when END is null) in the format string in a call
to a formatted output function described by INFO. Reflect the count
in RES and issue warnings as appropriate. */
static void
add_bytes (const pass_sprintf_length::call_info &info,
const char *beg, const char *end, format_result *res)
{
if (res->number_chars_min >= HOST_WIDE_INT_MAX)
return;
/* The number of bytes to output is the number of bytes between
the end of the last directive and the beginning of the next
one if it exists, otherwise the number of characters remaining
in the format string plus 1 for the terminating NUL. */
size_t nbytes = end ? end - beg : strlen (beg) + 1;
/* Return if there are no bytes to add at this time but there are
directives remaining in the format string. */
if (!nbytes)
return;
/* Compute the range of available bytes in the destination. There
must always be at least one byte left for the terminating NUL
that's appended after the format string has been processed. */
result_range avail_range = bytes_remaining (info.objsize, *res);
/* 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.)
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->knownrange || warn_level > 1)
&& avail_range.min < nbytes)))
{
/* Set NAVAIL to the number of available bytes used to decide
whether or not to issue a warning below. The exact kind of
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->knownrange || warn_level > 1))
navail = avail_range.min;
/* Compute the offset of the first format character that is beyond
the end of the destination region and the length of the rest of
the format string from that point on. */
unsigned HOST_WIDE_INT off
= (unsigned HOST_WIDE_INT)(beg - info.fmtstr) + navail;
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 ? off : 0,
off + len - !!len);
/* Is the output of the last directive the result of the argument
being within a range whose lower bound would fit in the buffer
but the upper bound would not? If so, use the word "may" to
indicate that the overflow/truncation may (but need not) happen. */
bool boundrange
= (res->number_chars_min < res->number_chars_max
&& res->number_chars_min + nbytes <= info.objsize);
if (!end && ((nbytes - navail) == 1 || boundrange))
{
/* There is room for the rest of the format string but none
for the terminating nul. */
const char *text
= (info.bounded // Snprintf and the like.
? (boundrange
? G_("output may be truncated before the last format character"
: "output truncated before the last format character"))
: (boundrange
? G_("may write a terminating nul past the end "
"of the destination")
: G_("writing a terminating nul past the end "
"of the destination")));
if (!info.bounded
|| !boundrange
|| !info.retval_used ()
|| warn_level > 1)
res->warned = fmtwarn (loc, NULL, NULL, info.warnopt (), text);
}
else
{
/* There isn't enough room for 1 or more characters that remain
to copy from the format string. */
const char *text
= (info.bounded // Snprintf and the like.
? (boundrange
? G_("output may be truncated at or before format character "
"%qc at offset %wu")
: G_("output truncated at format character %qc at offset %wu"))
: (res->number_chars >= HOST_WIDE_INT_MAX
? G_("may write format character %#qc at offset %wu past "
"the end of the destination")
: G_("writing format character %#qc at offset %wu past "
"the end of the destination")));
if (!info.bounded
|| !boundrange
|| !info.retval_used ()
|| warn_level > 1)
res->warned = fmtwarn (loc, NULL, NULL, info.warnopt (),
text, info.fmtstr[off], off);
}
}
if (res->warned && !end && info.objsize < HOST_WIDE_INT_MAX)
{ {
/* If a warning has been issued for buffer overflow or truncation /* If a warning has been issued for buffer overflow or truncation
(but not otherwise) help the user figure out how big a buffer (but not otherwise) help the user figure out how big a buffer
...@@ -2364,78 +2594,46 @@ add_bytes (const pass_sprintf_length::call_info &info, ...@@ -2364,78 +2594,46 @@ add_bytes (const pass_sprintf_length::call_info &info,
location_t callloc = gimple_location (info.callstmt); location_t callloc = gimple_location (info.callstmt);
unsigned HOST_WIDE_INT min = res->number_chars_min; unsigned HOST_WIDE_INT min = res->range.min;
unsigned HOST_WIDE_INT max = res->number_chars_max; unsigned HOST_WIDE_INT max = res->range.max;
unsigned HOST_WIDE_INT exact
= (res->number_chars < HOST_WIDE_INT_MAX
? res->number_chars : res->number_chars_min);
if (min < max && max < HOST_WIDE_INT_MAX) if (min == max)
inform (callloc,
(min == 1
? G_("%qE output %wu byte into a destination of size %wu")
: G_("%qE output %wu bytes into a destination of size %wu")),
info.func, min, info.objsize);
else if (max < HOST_WIDE_INT_MAX)
inform (callloc, inform (callloc,
"format output between %wu and %wu bytes into " "%qE output between %wu and %wu bytes into "
"a destination of size %wu", "a destination of size %wu",
min + nbytes, max + nbytes, info.objsize); info.func, min, max, info.objsize);
else if (min < res->range.likely && res->range.likely < max)
inform (callloc,
"%qE output %wu or more bytes (assuming %wu) into "
"a destination of size %wu",
info.func, min, res->range.likely, info.objsize);
else else
inform (callloc, inform (callloc,
(nbytes + exact == 1 "%qE output %wu or more bytes into a destination of size %wu",
? G_("format output %wu byte into a destination of size %wu") info.func, min, info.objsize);
: G_("format output %wu bytes into a destination of size %wu")),
nbytes + exact, info.objsize);
} }
/* Add the number of bytes and then check for INT_MAX overflow. */ if (dump_file && *dir.beg)
*res += nbytes;
/* Has the minimum output length minus the terminating nul exceeded
INT_MAX? */
bool exceedmin = (res->number_chars_min - !end) > target_int_max ();
if (!res->warned
&& (exceedmin
|| (warn_level > 1
&& (res->number_chars_max - !end) > target_int_max ())))
{ {
/* The function's output exceeds INT_MAX bytes. */ fprintf (dump_file, " Result: %lli, %lli, %lli, %lli "
"(%lli, %lli, %lli, %lli)\n",
/* Set NAVAIL to the number of available bytes used to decide (long long)fmtres.range.min,
whether or not to issue a warning below. The exact kind of (long long)fmtres.range.likely,
warning will depend on AVAIL_RANGE. */ (long long)fmtres.range.max,
unsigned HOST_WIDE_INT navail = avail_range.max; (long long)fmtres.range.unlikely,
if (nbytes <= navail && avail_range.min < HOST_WIDE_INT_MAX (long long)res->range.min,
&& (res->bounded || warn_level > 1)) (long long)res->range.likely,
navail = avail_range.min; (long long)res->range.max,
(long long)res->range.unlikely);
/* Compute the offset of the first format character that is beyond
the end of the destination region and the length of the rest of
the format string from that point on. */
unsigned HOST_WIDE_INT off = (unsigned HOST_WIDE_INT)(beg - info.fmtstr);
if (navail < HOST_WIDE_INT_MAX)
off += navail;
size_t len = strlen (info.fmtstr + off);
substring_loc loc
(info.fmtloc, TREE_TYPE (info.format), off - !len, len ? off : 0,
off + len - !!len);
if (res->number_chars_min == res->number_chars_max)
res->warned = fmtwarn (loc, NULL, NULL, info.warnopt (),
"output of %wu bytes causes "
"result to exceed %<INT_MAX%>",
res->number_chars_min - !end);
else
{
const char *text
= (exceedmin
? G_ ("output between %wu and %wu bytes causes "
"result to exceed %<INT_MAX%>")
: G_ ("output between %wu and %wu bytes may cause "
"result to exceed %<INT_MAX%>"));
res->warned = fmtwarn (loc, NULL, NULL, info.warnopt (), text,
res->number_chars_min - !end,
res->number_chars_max - !end);
}
} }
return true;
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
...@@ -2459,7 +2657,7 @@ parse_directive (pass_sprintf_length::call_info &info, ...@@ -2459,7 +2657,7 @@ parse_directive (pass_sprintf_length::call_info &info,
(which isn't really a directive but it simplifies things to (which isn't really a directive but it simplifies things to
handle it as if it were). */ handle it as if it were). */
dir.len = len; dir.len = len;
dir.fmtfunc = NULL; dir.fmtfunc = format_plain;
if (dump_file) if (dump_file)
{ {
...@@ -2686,7 +2884,7 @@ parse_directive (pass_sprintf_length::call_info &info, ...@@ -2686,7 +2884,7 @@ parse_directive (pass_sprintf_length::call_info &info,
undefined prevent the result from being folded. */ undefined prevent the result from being folded. */
case '\0': case '\0':
--pf; --pf;
res->bounded = false; res->range.min = res->range.max = HOST_WIDE_INT_M1U;
/* FALLTHRU */ /* FALLTHRU */
case '%': case '%':
dir.fmtfunc = format_percent; dir.fmtfunc = format_percent;
...@@ -2821,13 +3019,12 @@ pass_sprintf_length::compute_format_length (call_info &info, ...@@ -2821,13 +3019,12 @@ pass_sprintf_length::compute_format_length (call_info &info,
(unsigned long long)info.objsize, info.fmtstr); (unsigned long long)info.objsize, info.fmtstr);
} }
/* Reset exact, minimum, and maximum character counters. */ /* Reset the minimum and maximum bytes counters. */
res->number_chars = res->number_chars_min = res->number_chars_max = 0; res->range.min = res->range.max = 0;
/* No directive has been seen yet so the length of output is bounded /* No directive has been seen yet so the length of output is bounded
by the known range [0, 0] (with no conversion producing more than by the known range [0, 0] (with no conversion producing more than
4K bytes) until determined otherwise. */ 4K bytes) until determined otherwise. */
res->bounded = true;
res->knownrange = true; res->knownrange = true;
res->under4k = true; res->under4k = true;
res->floating = false; res->floating = false;
...@@ -2846,19 +3043,9 @@ pass_sprintf_length::compute_format_length (call_info &info, ...@@ -2846,19 +3043,9 @@ pass_sprintf_length::compute_format_length (call_info &info,
size_t n = parse_directive (info, dir, res, pf, &argno); size_t n = parse_directive (info, dir, res, pf, &argno);
if (dir.fmtfunc) /* Return failure if the format function fails. */
{ if (!format_directive (info, res, dir))
/* Return failure if the format function fails. */ return false;
if (!format_directive (info, res, dir))
return false;
}
else
{
/* Add the number of bytes between the end of the last directive
and either the next if one exists, or the end of the format
string. */
add_bytes (info, pf, n ? pf + n : NULL, res);
}
/* Return success the directive is zero bytes long and it's /* Return success the directive is zero bytes long and it's
the last think in the format string (i.e., it's the terminating the last think in the format string (i.e., it's the terminating
...@@ -2870,7 +3057,7 @@ pass_sprintf_length::compute_format_length (call_info &info, ...@@ -2870,7 +3057,7 @@ pass_sprintf_length::compute_format_length (call_info &info,
pf += n; pf += n;
} }
/* Complete format string was processed (with or without warnings). */ /* The complete format string was processed (with or without warnings). */
return true; return true;
} }
...@@ -2888,7 +3075,6 @@ get_destination_size (tree dest) ...@@ -2888,7 +3075,6 @@ get_destination_size (tree dest)
a member array as opposed to the whole enclosing object), otherwise a member array as opposed to the whole enclosing object), otherwise
use type-zero object size to determine the size of the enclosing use type-zero object size to determine the size of the enclosing
object (the function fails without optimization in this type). */ object (the function fails without optimization in this type). */
int ost = optimize > 0; int ost = optimize > 0;
unsigned HOST_WIDE_INT size; unsigned HOST_WIDE_INT size;
if (compute_builtin_object_size (dest, ost, &size)) if (compute_builtin_object_size (dest, ost, &size))
...@@ -2910,19 +3096,19 @@ try_substitute_return_value (gimple_stmt_iterator *gsi, ...@@ -2910,19 +3096,19 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
const pass_sprintf_length::call_info &info, const pass_sprintf_length::call_info &info,
const format_result &res) const format_result &res)
{ {
if (!res.bounded)
return false;
tree lhs = gimple_get_lhs (info.callstmt); tree lhs = gimple_get_lhs (info.callstmt);
/* Set to true when the entire call has been removed. */ /* Set to true when the entire call has been removed. */
bool removed = false; bool removed = false;
/* The minumum return value. */ /* The minimum return value. */
unsigned HOST_WIDE_INT minretval = res.number_chars_min; unsigned HOST_WIDE_INT minretval = res.range.min;
/* The maximum return value. */ /* The maximum return value is in most cases bounded by RES.RANGE.MAX
unsigned HOST_WIDE_INT maxretval = res.number_chars_max; but in cases involving multibyte characters could be as large as
RES.RANGE.UNLIKELY. */
unsigned HOST_WIDE_INT maxretval
= res.range.unlikely < res.range.max ? res.range.max : res.range.unlikely;
/* Adjust the number of bytes which includes the terminating nul /* Adjust the number of bytes which includes the terminating nul
to reflect the return value of the function which does not. to reflect the return value of the function which does not.
......
2017-01-26 Martin Sebor <msebor@redhat.com>
PR middle-end/78703
* gcc.dg/format/pr78569.c: Adjust.
* gcc.dg/tree-ssa/builtin-snprintf-warn-2.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-2.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-5.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf.c: Same.
2017-01-26 Jakub Jelinek <jakub@redhat.com> 2017-01-26 Jakub Jelinek <jakub@redhat.com>
PR c++/68727 PR c++/68727
......
...@@ -20,5 +20,5 @@ void test (void) ...@@ -20,5 +20,5 @@ void test (void)
"channel uplink (see section 7.6.1)."); "channel uplink (see section 7.6.1).");
/* { dg-warning "output truncated" "" { target *-*-* } 7 } */ /* { dg-warning "output truncated" "" { target *-*-* } 7 } */
/* { dg-message "format output" "" { target *-*-* } 6 } */ /* { dg-message "output" "" { target *-*-* } 6 } */
} }
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
typedef struct typedef struct
{ {
char a0[0]; char a0[0];
/* Separate a0 from a1 to prevent the former from being substituted
for the latter and causing false positives. */
int: 8;
char a1[1]; char a1[1];
char a2[2]; char a2[2];
char a3[3]; char a3[3];
...@@ -39,7 +42,9 @@ void test_int_retval_unused (void) ...@@ -39,7 +42,9 @@ void test_int_retval_unused (void)
void test_string_retval_unused (const Arrays *ar) void test_string_retval_unused (const Arrays *ar)
{ {
T (1, "%-s", ar->a0); /* At level 2 strings of unknown length are assumed to be 1 character
long, so the following is diagnosed. */
T (1, "%-s", ar->a0); /* { dg-warning "output may be truncated" } */
T (1, "%-s", ar->a1); T (1, "%-s", ar->a1);
T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */ T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */
} }
...@@ -64,7 +69,7 @@ void test_int_retval_used (void) ...@@ -64,7 +69,7 @@ void test_int_retval_used (void)
void test_string_retval_used (const Arrays *ar) void test_string_retval_used (const Arrays *ar)
{ {
T (1, "%-s", ar->a0); T (1, "%-s", ar->a0); /* { dg-warning "output may be truncated" } */
T (1, "%-s", ar->a1); T (1, "%-s", ar->a1);
T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */ T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */
} }
...@@ -59,15 +59,15 @@ void must_not_eliminate (void); ...@@ -59,15 +59,15 @@ void must_not_eliminate (void);
typedef __SIZE_TYPE__ size_t; typedef __SIZE_TYPE__ size_t;
extern int i; volatile int i;
extern unsigned u; volatile unsigned u;
extern long li; volatile long li;
extern unsigned long lu; volatile unsigned long lu;
extern size_t sz; volatile size_t sz;
extern char *str; volatile char *str;
extern double d; volatile double d;
extern long double ld; volatile long double ld;
/* Verify that overflowing the destination object disables the return /* Verify that overflowing the destination object disables the return
value optimization. */ value optimization. */
...@@ -280,7 +280,8 @@ RNG (0, 6, 8, "%s%ls", "1", L"2"); ...@@ -280,7 +280,8 @@ RNG (0, 6, 8, "%s%ls", "1", L"2");
*/ */
/* Only conditional calls to abort should be made (with any probability): /* Only conditional calls to must_not_eliminate must be made (with
any probability):
{ dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 124 "optimized" { target { ilp32 || lp64 } } } } { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 124 "optimized" { target { ilp32 || lp64 } } } }
{ dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } }
No unconditional calls to abort should be made: No unconditional calls to abort should be made:
......
...@@ -136,7 +136,6 @@ void test_arg_string (const char *s) ...@@ -136,7 +136,6 @@ void test_arg_string (const char *s)
void test_arg_multiarg (int i, double d) void test_arg_multiarg (int i, double d)
{ {
EQL (16, "%i %f %s", 123, 3.14, "abc");
EQL (16, "%12i %s", i, "abc"); EQL (16, "%12i %s", i, "abc");
EQL (16, "%*i %s", 12, i, "abc"); EQL (16, "%*i %s", 12, i, "abc");
} }
......
...@@ -11,21 +11,19 @@ ...@@ -11,21 +11,19 @@
#define INT_MAX __INT_MAX__ #define INT_MAX __INT_MAX__
typedef __builtin_va_list va_list;
char buffer [256]; char buffer [256];
extern char *ptr; extern char *ptr;
/* Evaluate to an array of SIZE characters when non-negative and LINE /* Evaluate to an array of SIZE characters when non-negative, or to
is not set or set to the line the macro is on, or to a pointer to a pointer to an unknown object otherwise. */
an unknown object otherwise. */ #define buffer(size) \
#define buffer(size) \ ((0 <= size) ? buffer + sizeof buffer - (size) : ptr)
(0 <= size && (!LINE || __LINE__ == LINE) \
? buffer + sizeof buffer - size : ptr)
/* Evaluate to SIZE when non-negative and LINE is not set or set to /* Evaluate to SIZE when non-negative, or to SIZE_MAX otherise. */
the line the macro is on, or to SIZE_MAX otherise. */ #define objsize(size) ((0 <= size) ? (size) : __SIZE_MAX__)
#define objsize(size) \
(0 <= size && (!LINE || __LINE__ == LINE) \
? size : __SIZE_MAX__)
typedef __SIZE_TYPE__ size_t; typedef __SIZE_TYPE__ size_t;
...@@ -61,12 +59,26 @@ const char s8[] = "12345678"; ...@@ -61,12 +59,26 @@ const char s8[] = "12345678";
void sink (void*, ...); void sink (void*, ...);
int dummy_sprintf (char*, const char*, ...);
int dummy_snprintf (char*, size_t, const char*, ...);
int dummy_vsprintf (char*, const char*, va_list);
int dummy_vsnprintf (char*, size_t, const char*, va_list);
int dummy___sprintf_chk (char*, int, size_t, const char*, ...);
int dummy___snprintf_chk (char*, size_t, int, size_t, const char*, ...);
int dummy___vsprintf_chk (char*, int, size_t, const char*, va_list);
int dummy___vsnprintf_chk (char*, size_t, int, size_t, const char*, va_list);
/* Helper to expand function to either __builtin_f or dummy_f to
make debugging GCC easy. */
#define FUNC(f) \
((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f)
/* Macro to verify that calls to __builtin_sprintf (i.e., with no size /* Macro to verify that calls to __builtin_sprintf (i.e., with no size
argument) issue diagnostics by correctly determining the size of argument) issue diagnostics by correctly determining the size of
the destination buffer. */ the destination buffer. */
#define T(size, fmt, ...) \ #define T(size, fmt, ...) \
__builtin_sprintf (buffer (size), fmt, __VA_ARGS__), \ (FUNC (sprintf) (buffer (size), fmt, __VA_ARGS__), \
sink (buffer, ptr); sink (buffer, ptr))
/* Exercise the "%c" and "%lc" directive with constant arguments. */ /* Exercise the "%c" and "%lc" directive with constant arguments. */
...@@ -109,19 +121,21 @@ void test_sprintf_flexarray (void *p, int i) ...@@ -109,19 +121,21 @@ void test_sprintf_flexarray (void *p, int i)
struct S struct S
{ {
int n; int n;
char a []; char a[];
} *s = p; } *s = p;
__builtin_sprintf (s->a, "%c", 'x'); FUNC (sprintf)(s->a, "%c", 'x');
__builtin_sprintf (s->a, "%s", ""); FUNC (sprintf)(s->a, "%-s", "");
__builtin_sprintf (s->a, "%s", "abc"); FUNC (sprintf)(s->a, "%-s", "abc");
__builtin_sprintf (s->a, "abc%sghi", "def"); FUNC (sprintf)(s->a, "abc%sghi", "def");
__builtin_sprintf (s->a, "%i", 1234); FUNC (sprintf)(s->a, "%i", 1234);
__builtin_sprintf (buffer (1), "%s", s->a); FUNC (sprintf)(buffer (1), "%-s", s->a);
__builtin_sprintf (buffer (1), "%s", s [i].a); FUNC (sprintf)(buffer (1), "%-s", s [i].a);
FUNC (sprintf)(buffer (2), "%-s", s->a);
FUNC (sprintf)(buffer (2), "%-s", s [i].a);
} }
/* Same as above but for zero-length arrays. */ /* Same as above but for zero-length arrays. */
...@@ -134,22 +148,22 @@ void test_sprintf_zero_length_array (void *p, int i) ...@@ -134,22 +148,22 @@ void test_sprintf_zero_length_array (void *p, int i)
char a [0]; char a [0];
} *s = p; } *s = p;
__builtin_sprintf (s->a, "%c", 'x'); FUNC (sprintf)(s->a, "%c", 'x');
__builtin_sprintf (s->a, "%s", ""); FUNC (sprintf)(s->a, "%s", "");
__builtin_sprintf (s->a, "%s", "abc"); FUNC (sprintf)(s->a, "%s", "abc");
__builtin_sprintf (s->a, "abc%sghi", "def"); FUNC (sprintf)(s->a, "abc%sghi", "def");
__builtin_sprintf (s->a, "%i", 1234); FUNC (sprintf)(s->a, "%i", 1234);
__builtin_sprintf (buffer (1), "%s", s->a); FUNC (sprintf)(buffer (1), "%s", s->a);
__builtin_sprintf (buffer (1), "%s", s [i].a); FUNC (sprintf)(buffer (1), "%s", s [i].a);
} }
#undef T #undef T
#define T(size, fmt, ...) \ #define T(size, fmt, ...) \
__builtin___sprintf_chk (buffer (size), 0, objsize (size), fmt, \ (FUNC (__sprintf_chk) (buffer (size), 0, objsize (size), fmt, __VA_ARGS__), \
__VA_ARGS__), sink (buffer, ptr) sink (buffer, ptr))
/* Exercise the "%c" and "%lc" directive with constant arguments. */ /* Exercise the "%c" and "%lc" directive with constant arguments. */
...@@ -286,6 +300,7 @@ void test_sprintf_chk_s_const (void) ...@@ -286,6 +300,7 @@ void test_sprintf_chk_s_const (void)
T (2, "_%s", ""); T (2, "_%s", "");
T (2, "%%%s", ""); T (2, "%%%s", "");
T (2, "%%%%%s", ""); /* { dg-warning "nul past the end" } */
T (2, "%s%%", ""); T (2, "%s%%", "");
T (2, "_%s", "1"); /* { dg-warning "nul past the end" } */ T (2, "_%s", "1"); /* { dg-warning "nul past the end" } */
T (2, "%%%s", "1"); /* { dg-warning "nul past the end" } */ T (2, "%%%s", "1"); /* { dg-warning "nul past the end" } */
...@@ -317,7 +332,10 @@ void test_sprintf_chk_s_const (void) ...@@ -317,7 +332,10 @@ void test_sprintf_chk_s_const (void)
T (1, "%*ls", 0, L"\0"); T (1, "%*ls", 0, L"\0");
T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */ T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */
T (1, "%ls", L"1"); /* { dg-warning "nul past the end" } */ /* A wide character string need not convert into any bytes (although
individual ASCII characters are assumed to convert into 1 bt %lc
so this could be made smarter. */
T (1, "%ls", L"1"); /* { dg-warning "directive writing up to 6 bytes into a region of size 1" } */
T (1, "%.0ls", L"1"); T (1, "%.0ls", L"1");
T (2, "%.0ls", L"1"); T (2, "%.0ls", L"1");
T (2, "%.1ls", L"1"); T (2, "%.1ls", L"1");
...@@ -333,9 +351,9 @@ void test_sprintf_chk_s_const (void) ...@@ -333,9 +351,9 @@ void test_sprintf_chk_s_const (void)
T (3, "%.0ls", L"1"); T (3, "%.0ls", L"1");
T (3, "%.1ls", L"1"); T (3, "%.1ls", L"1");
T (3, "%.2ls", L"1"); T (3, "%.2ls", L"1");
T (3, "%ls", L"12"); T (3, "%ls", L"12"); /* { dg-warning "directive writing up to 12 bytes" } */
T (3, "%ls", L"123"); /* { dg-warning "nul past the end" } */ T (3, "%ls", L"123"); /* { dg-warning "directive writing up to 18 bytes" } */
T (3, "%.0ls", L"123"); T (3, "%.0ls", L"123");
T (3, "%.1ls", L"123"); T (3, "%.1ls", L"123");
T (3, "%.2ls", L"123"); T (3, "%.2ls", L"123");
...@@ -579,7 +597,7 @@ void test_sprintf_chk_integer_const (void) ...@@ -579,7 +597,7 @@ void test_sprintf_chk_integer_const (void)
T ( 1, "%i", 0); /* { dg-warning "nul past the end" } */ T ( 1, "%i", 0); /* { dg-warning "nul past the end" } */
T ( 1, "%i", 1); /* { dg-warning "nul past the end" } */ T ( 1, "%i", 1); /* { dg-warning "nul past the end" } */
T ( 1, "%i", -1); /* { dg-warning "into a region" } */ T ( 1, "%i", -1); /* { dg-warning "into a region" } */
T ( 1, "%i_", 1); /* { dg-warning "character ._. at offset 2 past the end" } */ T ( 1, "%i_", 1); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 1, "_%i", 1); /* { dg-warning "into a region" } */ T ( 1, "_%i", 1); /* { dg-warning "into a region" } */
T ( 1, "_%i_", 1); /* { dg-warning "into a region" } */ T ( 1, "_%i_", 1); /* { dg-warning "into a region" } */
T ( 1, "%o", 0); /* { dg-warning "nul past the end" } */ T ( 1, "%o", 0); /* { dg-warning "nul past the end" } */
...@@ -596,7 +614,7 @@ void test_sprintf_chk_integer_const (void) ...@@ -596,7 +614,7 @@ void test_sprintf_chk_integer_const (void)
T ( 2, "%i", 10); /* { dg-warning "nul past the end" } */ T ( 2, "%i", 10); /* { dg-warning "nul past the end" } */
T ( 2, "%i_", 0); /* { dg-warning "nul past the end" } */ T ( 2, "%i_", 0); /* { dg-warning "nul past the end" } */
T ( 2, "_%i", 0); /* { dg-warning "nul past the end" } */ T ( 2, "_%i", 0); /* { dg-warning "nul past the end" } */
T ( 2, "_%i_", 0); /* { dg-warning "character ._. at offset 3 past the end" } */ T ( 2, "_%i_", 0); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 2, "%o", 1); T ( 2, "%o", 1);
T ( 2, "%o", 7); T ( 2, "%o", 7);
T ( 2, "%o", 010); /* { dg-warning "nul past the end" } */ T ( 2, "%o", 010); /* { dg-warning "nul past the end" } */
...@@ -678,7 +696,7 @@ void test_sprintf_chk_j_const (void) ...@@ -678,7 +696,7 @@ void test_sprintf_chk_j_const (void)
T ( 1, "%ji", I ( 0)); /* { dg-warning "nul past the end" } */ T ( 1, "%ji", I ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%ji", I ( 1)); /* { dg-warning "nul past the end" } */ T ( 1, "%ji", I ( 1)); /* { dg-warning "nul past the end" } */
T ( 1, "%ji", I ( -1)); /* { dg-warning "into a region" } */ T ( 1, "%ji", I ( -1)); /* { dg-warning "into a region" } */
T ( 1, "%ji_", I ( 1)); /* { dg-warning "character ._. at offset 3 past the end" } */ T ( 1, "%ji_", I ( 1)); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 1, "_%ji", I ( 1)); /* { dg-warning "into a region" } */ T ( 1, "_%ji", I ( 1)); /* { dg-warning "into a region" } */
T ( 1, "_%ji_",I ( 1)); /* { dg-warning "into a region" } */ T ( 1, "_%ji_",I ( 1)); /* { dg-warning "into a region" } */
T ( 1, "%jo", I ( 0)); /* { dg-warning "nul past the end" } */ T ( 1, "%jo", I ( 0)); /* { dg-warning "nul past the end" } */
...@@ -695,7 +713,7 @@ void test_sprintf_chk_j_const (void) ...@@ -695,7 +713,7 @@ void test_sprintf_chk_j_const (void)
T ( 2, "%ji", I ( 10)); /* { dg-warning "nul past the end" } */ T ( 2, "%ji", I ( 10)); /* { dg-warning "nul past the end" } */
T ( 2, "%ji_", I ( 0)); /* { dg-warning "nul past the end" } */ T ( 2, "%ji_", I ( 0)); /* { dg-warning "nul past the end" } */
T ( 2, "_%ji", I ( 0)); /* { dg-warning "nul past the end" } */ T ( 2, "_%ji", I ( 0)); /* { dg-warning "nul past the end" } */
T ( 2, "_%ji_",I ( 0)); /* { dg-warning "character ._. at offset 4 past the end" } */ T ( 2, "_%ji_",I ( 0)); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 2, "%jo", I ( 1)); T ( 2, "%jo", I ( 1));
T ( 2, "%jo", I ( 7)); T ( 2, "%jo", I ( 7));
T ( 2, "%jo", I ( 010)); /* { dg-warning "nul past the end" } */ T ( 2, "%jo", I ( 010)); /* { dg-warning "nul past the end" } */
...@@ -754,7 +772,7 @@ void test_sprintf_chk_l_const (void) ...@@ -754,7 +772,7 @@ void test_sprintf_chk_l_const (void)
T ( 1, "%li", 0L); /* { dg-warning "nul past the end" } */ T ( 1, "%li", 0L); /* { dg-warning "nul past the end" } */
T ( 1, "%li", 1L); /* { dg-warning "nul past the end" } */ T ( 1, "%li", 1L); /* { dg-warning "nul past the end" } */
T ( 1, "%li", -1L); /* { dg-warning "into a region" } */ T ( 1, "%li", -1L); /* { dg-warning "into a region" } */
T ( 1, "%li_", 1L); /* { dg-warning "character ._. at offset 3 past the end" } */ T ( 1, "%li_", 1L); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 1, "_%li", 1L); /* { dg-warning "into a region" } */ T ( 1, "_%li", 1L); /* { dg-warning "into a region" } */
T ( 1, "_%li_", 1L); /* { dg-warning "into a region" } */ T ( 1, "_%li_", 1L); /* { dg-warning "into a region" } */
T ( 1, "%lo", 0L); /* { dg-warning "nul past the end" } */ T ( 1, "%lo", 0L); /* { dg-warning "nul past the end" } */
...@@ -771,7 +789,7 @@ void test_sprintf_chk_l_const (void) ...@@ -771,7 +789,7 @@ void test_sprintf_chk_l_const (void)
T ( 2, "%li", 10L); /* { dg-warning "nul past the end" } */ T ( 2, "%li", 10L); /* { dg-warning "nul past the end" } */
T ( 2, "%li_", 0L); /* { dg-warning "nul past the end" } */ T ( 2, "%li_", 0L); /* { dg-warning "nul past the end" } */
T ( 2, "_%li", 0L); /* { dg-warning "nul past the end" } */ T ( 2, "_%li", 0L); /* { dg-warning "nul past the end" } */
T ( 2, "_%li_", 0L); /* { dg-warning "character ._. at offset 4 past the end" } */ T ( 2, "_%li_", 0L); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 2, "%lo", 1L); T ( 2, "%lo", 1L);
T ( 2, "%lo", 7L); T ( 2, "%lo", 7L);
T ( 2, "%lo", 010L); /* { dg-warning "nul past the end" } */ T ( 2, "%lo", 010L); /* { dg-warning "nul past the end" } */
...@@ -830,7 +848,7 @@ void test_sprintf_chk_ll_const (void) ...@@ -830,7 +848,7 @@ void test_sprintf_chk_ll_const (void)
T ( 1, "%lli", 0LL); /* { dg-warning "nul past the end" } */ T ( 1, "%lli", 0LL); /* { dg-warning "nul past the end" } */
T ( 1, "%lli", 1LL); /* { dg-warning "nul past the end" } */ T ( 1, "%lli", 1LL); /* { dg-warning "nul past the end" } */
T ( 1, "%lli", -1LL); /* { dg-warning "into a region" } */ T ( 1, "%lli", -1LL); /* { dg-warning "into a region" } */
T ( 1, "%lli_", 1LL); /* { dg-warning "character ._. at offset 4 past the end" } */ T ( 1, "%lli_", 1LL); /* { dg-warning " 1 byte into a region of size 0 " } */
T ( 1, "_%lli", 1LL); /* { dg-warning "into a region" } */ T ( 1, "_%lli", 1LL); /* { dg-warning "into a region" } */
T ( 1, "_%lli_", 1LL); /* { dg-warning "into a region" } */ T ( 1, "_%lli_", 1LL); /* { dg-warning "into a region" } */
T ( 1, "%llo", 0LL); /* { dg-warning "nul past the end" } */ T ( 1, "%llo", 0LL); /* { dg-warning "nul past the end" } */
...@@ -847,7 +865,7 @@ void test_sprintf_chk_ll_const (void) ...@@ -847,7 +865,7 @@ void test_sprintf_chk_ll_const (void)
T ( 2, "%lli", 10LL); /* { dg-warning "nul past the end" } */ T ( 2, "%lli", 10LL); /* { dg-warning "nul past the end" } */
T ( 2, "%lli_", 0LL); /* { dg-warning "nul past the end" } */ T ( 2, "%lli_", 0LL); /* { dg-warning "nul past the end" } */
T ( 2, "_%lli", 0LL); /* { dg-warning "nul past the end" } */ T ( 2, "_%lli", 0LL); /* { dg-warning "nul past the end" } */
T ( 2, "_%lli_", 0LL); /* { dg-warning "character ._. at offset 5 past the end" } */ T ( 2, "_%lli_", 0LL); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 2, "%llo", 1LL); T ( 2, "%llo", 1LL);
T ( 2, "%llo", 7LL); T ( 2, "%llo", 7LL);
T ( 2, "%llo", 010LL); /* { dg-warning "nul past the end" } */ T ( 2, "%llo", 010LL); /* { dg-warning "nul past the end" } */
...@@ -909,7 +927,7 @@ void test_sprintf_chk_L_const (void) ...@@ -909,7 +927,7 @@ void test_sprintf_chk_L_const (void)
T ( 1, "%Li", 0LL); /* { dg-warning "nul past the end" } */ T ( 1, "%Li", 0LL); /* { dg-warning "nul past the end" } */
T ( 1, "%Li", 1LL); /* { dg-warning "nul past the end" } */ T ( 1, "%Li", 1LL); /* { dg-warning "nul past the end" } */
T ( 1, "%Li", -1LL); /* { dg-warning "into a region" } */ T ( 1, "%Li", -1LL); /* { dg-warning "into a region" } */
T ( 1, "%Li_", 1LL); /* { dg-warning "character ._. at offset 3 past the end" } */ T ( 1, "%Li_", 1LL); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 1, "_%Li", 1LL); /* { dg-warning "into a region" } */ T ( 1, "_%Li", 1LL); /* { dg-warning "into a region" } */
T ( 1, "_%Li_", 1LL); /* { dg-warning "into a region" } */ T ( 1, "_%Li_", 1LL); /* { dg-warning "into a region" } */
} }
...@@ -920,7 +938,7 @@ void test_sprintf_chk_z_const (void) ...@@ -920,7 +938,7 @@ void test_sprintf_chk_z_const (void)
T ( 1, "%zi", (size_t)0); /* { dg-warning "nul past the end" } */ T ( 1, "%zi", (size_t)0); /* { dg-warning "nul past the end" } */
T ( 1, "%zi", (size_t)1); /* { dg-warning "nul past the end" } */ T ( 1, "%zi", (size_t)1); /* { dg-warning "nul past the end" } */
T ( 1, "%zi", (size_t)-1L);/* { dg-warning "into a region" } */ T ( 1, "%zi", (size_t)-1L);/* { dg-warning "into a region" } */
T ( 1, "%zi_", (size_t)1); /* { dg-warning "character ._. at offset 3 past the end" } */ T ( 1, "%zi_", (size_t)1); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 1, "_%zi", (size_t)1); /* { dg-warning "into a region" } */ T ( 1, "_%zi", (size_t)1); /* { dg-warning "into a region" } */
T ( 1, "_%zi_", (size_t)1); /* { dg-warning "into a region" } */ T ( 1, "_%zi_", (size_t)1); /* { dg-warning "into a region" } */
...@@ -942,16 +960,20 @@ void test_sprintf_chk_a_const (void) ...@@ -942,16 +960,20 @@ void test_sprintf_chk_a_const (void)
/* The least number of bytes on output is 6 for "0x0p+0". When precision /* 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 is missing the number of digits after the decimal point isn't fully
specified by C (it seems like a defect). */ specified by C (a defect). Two sets of implementations are known to
T (0, "%a", 0.0); /* { dg-warning "into a region" } */ exist: those that trim trailing zeros (e.g., Glibc) and those that
T (0, "%la", 0.0); /* { dg-warning "into a region" } */ pad output with trailing zeros so that all floating point numbers
T (1, "%a", 0.0); /* { dg-warning "into a region" } */ result in the same number of bytes on output (e.g., Solaris). */
T (2, "%a", 0.0); /* { dg-warning "into a region" } */ T ( 0, "%a", 0.0); /* { dg-warning "writing between 6 and 20 bytes" } */
T (3, "%a", 0.0); /* { dg-warning "into a region" } */ T ( 0, "%la", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T (4, "%a", 0.0); /* { dg-warning "into a region" } */ T ( 1, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T (5, "%a", 0.0); /* { dg-warning "into a region" } */ T ( 2, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T (6, "%a", 0.0); /* { dg-warning "writing a terminating nul" } */ T ( 3, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T (7, "%a", 0.0); T ( 4, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T ( 5, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T ( 6, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T (19, "%a", 0.0); /* { dg-warning "between 6 and 20 bytes" } */
T (20, "%a", 0.0); /* { dg-warning "may write a terminating nul" } */
T (0, "%.a", 0.0); /* { dg-warning "into a region" } */ T (0, "%.a", 0.0); /* { dg-warning "into a region" } */
T (0, "%.0a", 0.0); /* { dg-warning "into a region" } */ T (0, "%.0a", 0.0); /* { dg-warning "into a region" } */
...@@ -1008,24 +1030,21 @@ void test_sprintf_chk_e_const (void) ...@@ -1008,24 +1030,21 @@ void test_sprintf_chk_e_const (void)
T ( 6, "%.e", 1.0); T ( 6, "%.e", 1.0);
T ( 6, "%.0e", 1.0); T ( 6, "%.0e", 1.0);
/* The actual output of the following directives depends on the rounding /* The output of the following directives depends on the rounding
mode. Verify that the warning correctly reflects that. At level 1, mode. */
since the minimum number of bytes output by the directive fits the T (12, "%e", 9.999999e+99); /* { dg-warning "between 12 and 13" } */
space the directive itself isn't diagnosed but the terminating nul T (12, "%e", 9.9999994e+99); /* { dg-warning "between 12 and 13" } */
is. The directive is diagnosed at level 2. */ T (12, "%e", 9.9999995e+99); /* { dg-warning "between 12 and 13" } */
T (12, "%e", 9.999999e+99); /* { dg-warning "terminating nul" } */ T (12, "%e", 9.9999996e+99); /* { dg-warning "between 12 and 13" } */
T (12, "%e", 9.9999994e+99); /* { dg-warning "terminating nul" } */ T (12, "%e", 9.9999997e+99); /* { dg-warning "between 12 and 13" } */
T (12, "%e", 9.9999995e+99); /* { dg-warning "terminating nul" } */ T (12, "%e", 9.9999998e+99); /* { dg-warning "between 12 and 13" } */
T (12, "%e", 9.9999996e+99); /* { dg-warning "terminating nul" } */
T (12, "%e", 9.9999997e+99); /* { dg-warning "terminating nul" } */ T (12, "%Le", 9.9999994e+99L);/* { dg-warning "between 12 and 13" } */
T (12, "%e", 9.9999998e+99); /* { dg-warning "terminating nul" } */ T (12, "%Le", 9.9999995e+99L);/* { dg-warning "between 12 and 13" } */
T (12, "%Le", 9.9999996e+99L);/* { dg-warning "between 12 and 13" } */
T (12, "%Le", 9.9999994e+99L);/* { dg-warning "terminating nul" } */ T (12, "%Le", 9.9999997e+99L);/* { dg-warning "between 12 and 13" } */
T (12, "%Le", 9.9999995e+99L);/* { dg-warning "terminating nul" } */ T (12, "%Le", 9.9999998e+99L);/* { dg-warning "between 12 and 13" } */
T (12, "%Le", 9.9999996e+99L);/* { dg-warning "terminating nul" } */ T (12, "%Le", 9.9999999e+99L);/* { dg-warning "between 12 and 13" } */
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-overflow level 1 unknown numbers are assumed to have /* At -Wformat-overflow level 1 unknown numbers are assumed to have
...@@ -1035,21 +1054,32 @@ void test_sprintf_chk_e_const (void) ...@@ -1035,21 +1054,32 @@ void test_sprintf_chk_e_const (void)
void test_sprintf_chk_s_nonconst (int w, int p, const char *s) void test_sprintf_chk_s_nonconst (int w, int p, const char *s)
{ {
T (-1, "%s", s); T (-1, "%s", s);
T ( 0, "%s", s); /* { dg-warning "nul past the end" } */ T ( 0, "%s", s); /* { dg-warning "writing a terminating nul" } */
T ( 1, "%s", s); T ( 1, "%s", s);
T (-1, "%.0s", s);
T ( 1, "%.0s", s); T ( 1, "%.0s", s);
T ( 1, "%.1s", s); /* { dg-warning "nul past the end" } */ T (-1, "%.1s", s);
T ( 1, "%.1s", s); /* { dg-warning "may write a terminating nul past the end" } */
T (-1, "%.2s", s);
T ( 1, "%.2s", s); /* { dg-warning "directive writing up to 2 bytes" } */
T ( 2, "%.2s", s); /* { dg-warning "may write a terminating nul" } */
T ( 3, "%.2s", s);
/* The string argument is constant but the width and/or precision /* The string argument is constant but the width and/or precision
is not. */ is not. */
T ( 1, "%*s", w, ""); T (-1, "%*s", w, "");
T ( 1, "%*s", w, "1"); /* { dg-warning "nul past the end" } */ T ( 1, "%*s", w, ""); /* { dg-warning "may write a terminating nul past the end" } */
T (-1, "%*s", w, "1");
T ( 1, "%*s", w, "1"); /* { dg-warning "writing a terminating nul past the end" } */
T (-1, "%.*s", p, "");
T ( 1, "%.*s", p, ""); T ( 1, "%.*s", p, "");
T (-1, "%.*s", p, "1");
T ( 1, "%.*s", p, "1"); /* { dg-warning "may write a terminating nul" } */ T ( 1, "%.*s", p, "1"); /* { dg-warning "may write a terminating nul" } */
T ( 1, "%.*s", w, "123"); /* { dg-warning "writing between 0 and 3 bytes into a region of size 1" } */ T ( 1, "%.*s", w, "123"); /* { dg-warning "writing up to 3 bytes into a region of size 1" } */
T ( 1, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 1" } */ /* Either of the messages below is acceptable. */
T ( 2, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 2" } */ T ( 1, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 1|writing between 3 and 2147483648 bytes" } */
T ( 2, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 2|writing between 3 and 2147483648 bytes" } */
T ( 3, "%*s", w, "123"); /* { dg-warning "writing a terminating nul past the end" } */ T ( 3, "%*s", w, "123"); /* { dg-warning "writing a terminating nul past the end" } */
T ( 4, "%*s", w, "123"); T ( 4, "%*s", w, "123");
...@@ -1059,6 +1089,10 @@ void test_sprintf_chk_s_nonconst (int w, int p, const char *s) ...@@ -1059,6 +1089,10 @@ void test_sprintf_chk_s_nonconst (int w, int p, const char *s)
the end (we don't print "past the end" when we're not the end (we don't print "past the end" when we're not
sure which we can't be with an unknown string. */ sure which we can't be with an unknown string. */
T (1, "%1s", s); /* { dg-warning "writing a terminating nul past the end" } */ T (1, "%1s", s); /* { dg-warning "writing a terminating nul past the end" } */
/* Multiple directives. */
T (1, "%s%s", s, s);
T (1, "%s%s%s", s, s, s);
} }
/* Exercise the hh length modifier with all integer specifiers and /* Exercise the hh length modifier with all integer specifiers and
...@@ -1167,15 +1201,15 @@ void test_sprintf_chk_hh_nonconst (int w, int p, int a) ...@@ -1167,15 +1201,15 @@ void test_sprintf_chk_hh_nonconst (int w, int p, int a)
/* The argument is known but precision isn't. When the argument /* The argument is known but precision isn't. When the argument
is zero only the first call can be diagnosed since a zero is zero only the first call can be diagnosed since a zero
precision would result in no bytes on output. */ precision would result in no bytes on output. */
T (0, "%.*hhi", p, 0); /* { dg-warning "nul past the end" } */ T (0, "%.*hhi", p, 0); /* { dg-warning " writing up to \[0-9\]+ bytes" } */
T (1, "%.*hhi", p, 0); T (1, "%.*hhi", p, 0); /* { dg-warning "may write a terminating nul" }*/
T (2, "%.*hhi", p, 0); T (2, "%.*hhi", p, 0);
T (2, "%.*hhi", p, 12); /* { dg-warning "nul past the end" } */ T (2, "%.*hhi", p, 12); /* { dg-warning "nul past the end" } */
T (2, "%.*hhi", p, 123); /* { dg-warning "into a region" } */ T (2, "%.*hhi", p, 123); /* { dg-warning "into a region" } */
/* The argument is known but neither width nor precision is. */ /* The argument is known but neither width nor precision is. */
T (0, "%*.*hhi", w, p, 0); /* { dg-warning "nul past the end" } */ T (0, "%*.*hhi", w, p, 0); /* { dg-warning "writing up to \[0-9\]+ bytes" } */
T (1, "%*.*hhi", w, p, 0); T (1, "%*.*hhi", w, p, 0); /* { dg-warning "may write a terminating nul" } */
T (2, "%*.*hhi", w, p, 0); T (2, "%*.*hhi", w, p, 0);
T (2, "%*.*hhi", w, p, 12); /* { dg-warning "nul past the end" } */ T (2, "%*.*hhi", w, p, 12); /* { dg-warning "nul past the end" } */
T (2, "%*.*hhi", w, p, 123); /* { dg-warning "into a region" } */ T (2, "%*.*hhi", w, p, 123); /* { dg-warning "into a region" } */
...@@ -1298,12 +1332,13 @@ void test_sprintf_chk_int_nonconst (int w, int p, int a) ...@@ -1298,12 +1332,13 @@ void test_sprintf_chk_int_nonconst (int w, int p, int a)
T (3, "%2u", a); T (3, "%2u", a);
T (3, "%2x", a); T (3, "%2x", a);
T (1, "%.*d", p, a); T (1, "%.*d", p, a); /* { dg-warning "nul past the end" } */
T (2, "%.*d", p, a);
T (4, "%i %i", a, a); T (4, "%i %i", a, a);
/* The following will definitely be "writing a terminating nul past the end" /* The following will definitely be "writing a terminating nul past the end"
(i.e., not "may write".) */ (i.e., not "may write".) */
T (4, "%i %i ", a, a); /* { dg-warning "writing a terminating nul past the end" } */ T (4, "%i %i ", a, a); /* { dg-warning "nul past the end" } */
T (4, "%i %i %i", a, a, a); /* { dg-warning "into a region" }*/ T (4, "%i %i %i", a, a, a); /* { dg-warning "into a region" }*/
} }
...@@ -1347,7 +1382,7 @@ void test_sprintf_chk_e_nonconst (int w, int p, double d) ...@@ -1347,7 +1382,7 @@ void test_sprintf_chk_e_nonconst (int w, int p, double d)
T ( 8, "%.1e", d); T ( 8, "%.1e", d);
T ( 0, "%*e", 0, d); /* { dg-warning "writing between 12 and 14 bytes into a region of size 0" } */ T ( 0, "%*e", 0, d); /* { dg-warning "writing between 12 and 14 bytes into a region of size 0" } */
T ( 0, "%*e", w, d); /* { dg-warning "writing 12 or more bytes into a region of size 0" } */ T ( 0, "%*e", w, d); /* { dg-warning "writing 12 or more bytes into a region of size 0|writing between 12 and \[0-9\]+ bytes into a region of size 0" } */
} }
void test_sprintf_chk_f_nonconst (double d) void test_sprintf_chk_f_nonconst (double d)
...@@ -1381,9 +1416,10 @@ void test_sprintf_chk_f_nonconst (double d) ...@@ -1381,9 +1416,10 @@ void test_sprintf_chk_f_nonconst (double d)
__builtin_sprintf_chk with non-constant arguments. */ __builtin_sprintf_chk with non-constant arguments. */
#undef T #undef T
#define T(size, fmt) \ #define T(size, fmt) \
__builtin___vsprintf_chk (buffer (size), 0, objsize (size), fmt, va) (FUNC (__vsprintf_chk) (buffer (size), 0, objsize (size), fmt, va), \
sink (buffer))
void test_vsprintf_chk_c (__builtin_va_list va) void test_vsprintf_chk_c (va_list va)
{ {
T (-1, "%c"); T (-1, "%c");
...@@ -1399,26 +1435,25 @@ void test_vsprintf_chk_c (__builtin_va_list va) ...@@ -1399,26 +1435,25 @@ void test_vsprintf_chk_c (__builtin_va_list va)
T (3, "%c%c"); T (3, "%c%c");
/* Wide characters. */ /* Wide characters. */
T (0, "%lc"); /* { dg-warning "nul past the end" } */ T (0, "%lc"); /* { dg-warning "up to 6 bytes " } */
T (1, "%lc"); T (1, "%lc"); /* { dg-warning "up to 6 bytes " } */
T (2, "%lc"); T (2, "%lc"); /* { dg-warning "may write a terminating nul" } */
/* The following could result in as few as a single byte and in as many /* The following could result in as few as a single byte and in as many
as MB_CUR_MAX, but since the MB_CUR_MAX value is a runtime property as MB_CUR_MAX, but since the MB_CUR_MAX value is a runtime property
the write cannot be reliably diagnosed. */ the write cannot be reliably diagnosed. */
T (2, "%lc"); T (2, "%1lc"); /* { dg-warning "may write a terminating nul" } */
T (2, "%1lc");
/* Writing some unknown number of bytes into a field two characters wide. */ /* Writing some unknown number of bytes into a field two characters wide. */
T (2, "%2lc"); /* { dg-warning "nul past the end" } */ T (2, "%2lc"); /* { dg-warning "nul past the end" } */
T (2, "%lc%lc"); T (2, "%lc%lc"); /* { dg-warning "writing up to 6 bytes into a region of size between 0 and 2" } */
T (3, "%lc%c"); T (3, "%lc%c"); /* { dg-warning "may write a terminating nul" } */
/* Here in the best case each argument will format as single character, /* Here in the best case each argument will format as single character,
causing the terminating NUL to be written past the end. */ causing the terminating NUL to be written past the end. */
T (3, "%lc%c%c"); T (3, "%lc%c%c"); /* { dg-warning "writing 1 byte into a region of size between 0 and 2" } */
} }
void test_vsprintf_chk_int (__builtin_va_list va) void test_vsprintf_chk_int (va_list va)
{ {
T (-1, "%d"); T (-1, "%d");
...@@ -1462,8 +1497,9 @@ void test_vsprintf_chk_int (__builtin_va_list va) ...@@ -1462,8 +1497,9 @@ void test_vsprintf_chk_int (__builtin_va_list va)
} }
#undef T #undef T
#define T(size, fmt, ...) \ #define T(size, fmt, ...) \
__builtin_snprintf (buffer (size), objsize (size), fmt, __VA_ARGS__) (FUNC (snprintf) (buffer (size), objsize (size), fmt, __VA_ARGS__), \
sink (buffer))
void test_snprintf_c_const (char *d) void test_snprintf_c_const (char *d)
{ {
...@@ -1498,24 +1534,27 @@ void test_snprintf_c_const (char *d) ...@@ -1498,24 +1534,27 @@ void test_snprintf_c_const (char *d)
T (2, "%2lc", (wint_t)L'1'); /* { dg-warning "output truncated before the last format character" } */ T (2, "%2lc", (wint_t)L'1'); /* { dg-warning "output truncated before the last format character" } */
T (3, "%lc%c", (wint_t)'1', '2'); T (3, "%lc%c", (wint_t)'1', '2');
/* Here in the best case each argument will format as single character, /* Here %lc may result in anywhere between 0 and MB_CUR_MAX characters
causing the output to be truncated just before the terminating NUL so the minimum number of bytes on output is 2 (plus the terminating
(i.e., cutting off the '3'). */ nul), but the likely number is 3 (plus the nul). */
T (3, "%lc%c%c", (wint_t)'1', '2', '3'); /* { dg-warning "output truncated" } */ T (3, "%lc%c%c", (wint_t)'\x80', '2', '3'); /* { dg-warning ".%c. directive output may be truncated writing 1 byte into a region of size between 0 and 2" } */
T (3, "%lc%lc%c", (wint_t)'1', (wint_t)'2', '3'); /* { dg-warning "output truncated" } */ /* It's reasonably safe that L'1' converts into the single byte '1'. */
T (3, "%lc%c%c", (wint_t)'1', '2', '3'); /* { dg-warning "output may be truncated" } */
T (3, "%lc%lc%c", (wint_t)'1', (wint_t)'2', '3'); /* { dg-warning "output may be truncated" } */
} }
#undef T #undef T
#define T(size, fmt, ...) \ #define T(size, fmt, ...) \
__builtin___snprintf_chk (buffer (size), objsize (size), \ (FUNC (__snprintf_chk) (buffer (size), objsize (size), \
0, objsize (size), fmt, __VA_ARGS__) 0, objsize (size), fmt, __VA_ARGS__), \
sink (buffer))
void test_snprintf_chk_c_const (void) void test_snprintf_chk_c_const (void)
{ {
/* Verify that specifying a size of the destination buffer that's /* Verify that specifying a size of the destination buffer that's
bigger than its actual size (normally determined and passed to bigger than its actual size (normally determined and passed to
the function by __builtin_object_size) is diagnosed. */ the function by __builtin_object_size) is diagnosed. */
__builtin___snprintf_chk (buffer, 3, 0, 2, " "); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" } */ FUNC (__snprintf_chk)(buffer, 3, 0, 2, " "); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" } */
T (-1, "%c", 0); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ T (-1, "%c", 0); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
...@@ -1547,11 +1586,11 @@ void test_snprintf_chk_c_const (void) ...@@ -1547,11 +1586,11 @@ void test_snprintf_chk_c_const (void)
T (2, "%2lc", (wint_t)'1'); /* { dg-warning "output truncated before the last format character" } */ T (2, "%2lc", (wint_t)'1'); /* { dg-warning "output truncated before the last format character" } */
T (3, "%lc%c", (wint_t)'1', '2'); T (3, "%lc%c", (wint_t)'1', '2');
/* Here in the best case each argument will format as single character, /* Here %lc may result in anywhere between 0 and MB_CUR_MAX characters
causing the output to be truncated just before the terminating NUL so the minimum number of bytes on output is 2 (plus the terminating
(i.e., cutting off the '3'). */ nul), but the likely number is 3 (plus the nul). */
T (3, "%lc%c%c", (wint_t)'1', '2', '3'); /* { dg-warning "output truncated" } */ T (3, "%lc%c%c", (wint_t)'1', '2', '3'); /* { dg-warning "output may be truncated" } */
T (3, "%lc%lc%c", (wint_t)'1', (wint_t)'2', '3'); /* { dg-warning "output truncated" } */ T (3, "%lc%lc%c", (wint_t)'1', (wint_t)'2', '3'); /* { dg-warning "output may be truncated" } */
} }
/* Macro to verify that calls to __builtin_vsprintf (i.e., with no size /* Macro to verify that calls to __builtin_vsprintf (i.e., with no size
...@@ -1559,27 +1598,30 @@ void test_snprintf_chk_c_const (void) ...@@ -1559,27 +1598,30 @@ void test_snprintf_chk_c_const (void)
the destination buffer. */ the destination buffer. */
#undef T #undef T
#define T(size, fmt) \ #define T(size, fmt) \
__builtin_vsprintf (buffer (size), fmt, va) (FUNC (vsprintf) (buffer (size), fmt, va), \
sink (buffer))
void test_vsprintf_s (__builtin_va_list va) void test_vsprintf_s (va_list va)
{ {
T (-1, "%s"); T (-1, "%s");
T (0, "%s"); /* { dg-warning "writing a terminating nul past the end" } */ T (0, "%s"); /* { dg-warning "writing a terminating nul" } */
T (1, "%s"); T (1, "%s");
T (1, "%1s"); /* { dg-warning "writing a terminating nul past the end" } */ T (1, "%1s"); /* { dg-warning "writing a terminating nul past the end" } */
T (2, "%s%s"); T (2, "%s%s");
T (2, "%s%s_"); T (2, "%s%s1");
T (2, "%s%s12"); /* { dg-warning "writing a terminating nul" } */
T (2, "%s%s123"); /* { dg-warning "writing 3 bytes into a region of size 2" } */
T (2, "%s_%s"); T (2, "%s_%s");
T (2, "_%s%s"); T (2, "_%s%s");
T (2, "_%s_%s"); /* { dg-warning "writing a terminating nul past the end" } */ T (2, "_%s_%s"); /* { dg-warning "writing a terminating nul" } */
} }
/* Exercise all integer specifiers with no modifier and a non-constant /* Exercise all integer specifiers with no modifier and a non-constant
argument. */ argument. */
void test_vsprintf_int (__builtin_va_list va) void test_vsprintf_int (va_list va)
{ {
T (-1, "%d"); T (-1, "%d");
...@@ -1623,10 +1665,11 @@ void test_vsprintf_int (__builtin_va_list va) ...@@ -1623,10 +1665,11 @@ void test_vsprintf_int (__builtin_va_list va)
} }
#undef T #undef T
#define T(size, fmt) \ #define T(size, fmt) \
__builtin_vsnprintf (buffer (size), objsize (size), fmt, va) (FUNC (vsnprintf) (buffer (size), objsize (size), fmt, va), \
sink (buffer))
void test_vsnprintf_s (__builtin_va_list va) void test_vsnprintf_s (va_list va)
{ {
T (-1, "%s"); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ T (-1, "%s"); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
...@@ -1638,22 +1681,23 @@ void test_vsnprintf_s (__builtin_va_list va) ...@@ -1638,22 +1681,23 @@ void test_vsnprintf_s (__builtin_va_list va)
T (2, "%s%s_"); T (2, "%s%s_");
T (2, "%s_%s"); T (2, "%s_%s");
T (2, "_%s%s"); T (2, "_%s%s");
T (2, "_%s_%s"); /* { dg-warning "output truncated before the last format character" } */ T (2, "_%s_%s"); /* { dg-warning "output truncated" } */
} }
#undef T #undef T
#define T(size, fmt) \ #define T(size, fmt) \
__builtin___vsnprintf_chk (buffer (size), objsize (size), \ (FUNC (__vsnprintf_chk) (buffer (size), objsize (size), \
0, objsize (size), fmt, va) 0, objsize (size), fmt, va), \
sink (buffer))
void test_vsnprintf_chk_s (__builtin_va_list va) void test_vsnprintf_chk_s (va_list va)
{ {
/* Verify that specifying a size of the destination buffer that's /* Verify that specifying a size of the destination buffer that's
bigger than its actual size (normally determined and passed to bigger than its actual size (normally determined and passed to
the function by __builtin_object_size) is diagnosed. */ the function by __builtin_object_size) is diagnosed. */
__builtin___vsnprintf_chk (buffer, 123, 0, 122, "%-s", va); /* { dg-warning "specified bound 123 exceeds the size 122 of the destination" } */ FUNC (__vsnprintf_chk)(buffer, 123, 0, 122, "%-s", va); /* { dg-warning "specified bound 123 exceeds the size 122 of the destination" } */
__builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ FUNC (__vsnprintf_chk)(buffer, __SIZE_MAX__, 0, 2, "%-s", va); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
T (0, "%s"); T (0, "%s");
T (1, "%s"); T (1, "%s");
...@@ -1663,5 +1707,5 @@ void test_vsnprintf_chk_s (__builtin_va_list va) ...@@ -1663,5 +1707,5 @@ void test_vsnprintf_chk_s (__builtin_va_list va)
T (2, "%s%s_"); T (2, "%s%s_");
T (2, "%s_%s"); T (2, "%s_%s");
T (2, "_%s%s"); T (2, "_%s%s");
T (2, "_%s_%s"); /* { dg-warning "output truncated before the last format character" } */ T (2, "_%s_%s"); /* { dg-warning "output truncated" } */
} }
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-std=c99 -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */ /* { dg-options "-Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */
/* When debugging, define LINE to the line number of the test case to exercise /* When debugging, define LINE to the line number of the test case to exercise
and avoid exercising any of the others. The buffer and objsize macros and avoid exercising any of the others. The buffer and objsize macros
...@@ -47,12 +47,16 @@ void test_s_const (void) ...@@ -47,12 +47,16 @@ void test_s_const (void)
T (1, "%*ls", 0, L"\0"); T (1, "%*ls", 0, L"\0");
T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */ T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */
T (1, "%ls", L"1"); /* { dg-warning "directive writing between 1 and 6 bytes into a region of size 1" } */ /* A wide character converts into between zero and MB_LEN_MAX bytes
(although individual ASCII characters are assumed to convert into
1 bt %lc so this could be made smarter. */
T (1, "%ls", L"1"); /* { dg-warning "directive writing up to 6 bytes into a region of size 1" } */
T (1, "%.0ls", L"1"); T (1, "%.0ls", L"1");
T (2, "%.0ls", L"1"); T (2, "%.0ls", L"1");
T (2, "%.1ls", L"1"); T (2, "%.1ls", L"1");
T (2, "%.2ls", L"1"); /* { dg-warning "nul past the end" } */ T (2, "%.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, "%.3ls", L"1"); /* { dg-warning "directive writing up to 3 bytes into a region of size 2" } */
T (2, "%.7ls", L"1"); /* { dg-warning "directive writing up to 6 bytes into a region of size 2" } */
T (2, "%.2ls", L"12"); /* { dg-warning "nul past the end" } */ T (2, "%.2ls", L"12"); /* { dg-warning "nul past the end" } */
/* The "%.2ls" directive below will write at a minimum 1 byte (because /* The "%.2ls" directive below will write at a minimum 1 byte (because
...@@ -71,8 +75,8 @@ void test_s_const (void) ...@@ -71,8 +75,8 @@ void test_s_const (void)
T (3, "%.3ls", L"12"); /* { dg-warning "nul past the end" } */ T (3, "%.3ls", L"12"); /* { dg-warning "nul past the end" } */
T (4, "%.3ls", L"123"); T (4, "%.3ls", L"123");
T (4, "%.4ls", L"123"); /* { dg-warning "nul past the end" } */ 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, "%.5ls", L"123"); /* { dg-warning "directive writing up to 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 (4, "%.6ls", L"123"); /* { dg-warning "directive writing up to 6 bytes into a region of size 4" } */
} }
...@@ -87,37 +91,48 @@ struct Arrays { ...@@ -87,37 +91,48 @@ struct Arrays {
/* Exercise buffer overflow detection with non-const string arguments. */ /* Exercise buffer overflow detection with non-const string arguments. */
void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a) void test_s_nonconst (int w, int p, const char *s, const wchar_t *ws,
struct Arrays *a)
{ {
T (0, "%s", s); /* { dg-warning "into a region" "sprintf transformed into strcpy" { xfail *-*-* } } */ T (0, "%s", s); /* { dg-warning "into a region" "sprintf transformed into strcpy" { xfail *-*-* } } */
T (1, "%s", s); /* { dg-warning "nul past the end" "sprintf transformed into strcpy" { xfail *-*-* } } */ T (1, "%s", s); /* { dg-warning "nul past the end" "sprintf transformed into strcpy" { xfail *-*-* } } */
T (1, "%1s", s); /* { dg-warning "nul past the end" } */ T (1, "%1s", s); /* { dg-warning "writing a terminating nul" } */
T (1, "%.0s", s); T (1, "%.0s", s);
T (1, "%.1s", s); /* { dg-warning "may write a terminating nul" } */ T (1, "%.1s", s); /* { dg-warning "may write a terminating nul" } */
T (1, "%*s", 0, s); /* { dg-warning "may write a terminating nul" } */
T (1, "%*s", 1, s); /* { dg-warning "writing a terminating nul" } */
T (1, "%*s", 2, s); /* { dg-warning "directive writing 2 or more bytes" } */
T (1, "%*s", 3, s); /* { dg-warning "directive writing 3 or more bytes" } */
T (1, "%.*s", 1, s); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*s", 2, s); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%.*s", 3, s); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%.0ls", ws); T (1, "%.0ls", ws);
T (1, "%.1ls", ws); /* { dg-warning "may write a terminating nul" } */ T (1, "%.1ls", ws); /* { dg-warning "may write a terminating nul" } */
T (1, "%ls", ws); /* { dg-warning "writing a terminating nul" } */ T (1, "%ls", ws); /* { dg-warning "may write a terminating nul" } */
/* Verify that the size of the array is used in lieu of its length. /* Verify that the size of the array is used in lieu of its length.
The minus sign disables GCC's sprintf to strcpy transformation. */ The minus sign disables GCC's sprintf to strcpy transformation.
T (1, "%-s", a->a1); /* { dg-warning "nul past the end" } */ In the case below, the length of s->a1 can be at most zero, so
the call should not be diagnosed. */
T (1, "%-s", a->a1);
/* In the following test, since the length of the strings isn't known, /* In the following test, since the length of the strings isn't known,
their type (the array) is used to bound the maximum length to 1, their type (the array) is used to bound the maximum length to 1,
which means the "%-s" directive would not overflow the buffer, which means the "%-s" directive would not overflow the buffer,
but it would leave no room for the terminating nul. */ but it would leave no room for the terminating nul. */
T (1, "%-s", a->a2); /* { dg-warning "writing a terminating nul" } */ T (1, "%-s", a->a2); /* { dg-warning "may write a terminating nul" } */
/* Unlike in the test above, since the length of the string is bounded /* Unlike in the test above, since the length of the string is bounded
by the array type to at most 2, the "^-s" directive is diagnosed firts, by the array type to at most 2, the "^-s" directive is diagnosed firts,
preventing the diagnostic about the terminatinb nul. */ preventing the diagnostic about the terminatinb nul. */
T (1, "%-s", a->a3); /* { dg-warning "directive writing between 1 and 2 bytes" } */ T (1, "%-s", a->a3); /* { dg-warning "directive writing up to 2 bytes" } */
/* The length of a zero length array and flexible array member is /* The length of a zero length array and flexible array member is
unknown and at leve 2 assumed to be at least 1. */ unknown and at leve 2 assumed to be at least 1. */
T (1, "%-s", a->a0); /* { dg-warning "nul past the end" } */ T (1, "%-s", a->a0); /* { dg-warning "may write a terminating nul" } */
T (1, "%-s", a->ax); /* { dg-warning "nul past the end" } */ T (1, "%-s", a->ax); /* { dg-warning "may write a terminating nul" } */
T (2, "%-s", a->a0); T (2, "%-s", a->a0);
T (2, "%-s", a->ax); T (2, "%-s", a->ax);
...@@ -145,20 +160,20 @@ void test_hh_nonconst (int w, int p, int x, unsigned y) ...@@ -145,20 +160,20 @@ void test_hh_nonconst (int w, int p, int x, unsigned y)
/* Zero precision means that zero argument formats as no bytes unless /* Zero precision means that zero argument formats as no bytes unless
length or flags make it otherwise. */ length or flags make it otherwise. */
T (1, "%.*hhi", 0, x); /* { dg-warning "between 0 and 4 bytes" } */ T (1, "%.*hhi", 0, x); /* { dg-warning "writing up to 4 bytes" } */
T (2, "%.*hhi", 0, x); /* { dg-warning "between 0 and 4 bytes" } */ T (2, "%.*hhi", 0, x); /* { dg-warning "writing up to 4 bytes" } */
T (3, "%.*hhi", 0, x); /* { dg-warning "between 0 and 4 bytes" } */ T (3, "%.*hhi", 0, x); /* { dg-warning "writing up to 4 bytes" } */
T (4, "%.*hhi", 0, x); /* { dg-warning "may write a terminating nul past the end of the destination" } */ T (4, "%.*hhi", 0, x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T (1, "%.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */ T (1, "%.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
T (2, "%.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */ T (2, "%.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
T (3, "%.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */ T (3, "%.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
T (4, "%.*hhi", 0, y); /* { dg-warning "may write a terminating nul past the end of the destination" } */ T (4, "%.*hhi", 0, y); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T (1, "%#.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */ T (1, "%#.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */ /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */ T (1, "%+.*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */
T (1, "%-.*hhi", 0, y); /* { dg-warning "between 0 and 4 bytes" } */ T (1, "%-.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
T (1, "% .*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */ T (1, "% .*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */
T (1, "%#.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */ T (1, "%#.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */
...@@ -167,18 +182,18 @@ void test_hh_nonconst (int w, int p, int x, unsigned y) ...@@ -167,18 +182,18 @@ void test_hh_nonconst (int w, int p, int x, unsigned y)
T (1, "%-.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */ T (1, "%-.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */
T (1, "% .*hhi", 1, y); /* { dg-warning "between 2 and 4 bytes" } */ T (1, "% .*hhi", 1, y); /* { dg-warning "between 2 and 4 bytes" } */
T (1, "%#.*hhi", p, y); /* { dg-warning "writing 0 or more bytes" } */ T (1, "%#.*hhi", p, y); /* { dg-warning "writing up to \[0-9\]+ bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */ /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhi", p, y); /* { dg-warning "writing 1 or more bytes" } */ T (1, "%+.*hhi", p, y); /* { dg-warning "writing 1 or more bytes|writing between 1 and \[0-9\]+ bytes" } */
T (1, "%-.*hhi", p, y); /* { dg-warning "writing 0 or more bytes" } */ T (1, "%-.*hhi", p, y); /* { dg-warning "writing up to \[0-9\]+ bytes" } */
T (1, "% .*hhi", p, y); /* { dg-warning "writing 1 or more bytes" } */ T (1, "% .*hhi", p, y); /* { dg-warning "writing between 1 and \[0-9\]+ bytes|writing 1 or more bytes" } */
T (1, "%#.*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */ T (1, "%#.*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */ /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%+.*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */ T (1, "%+.*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
/* { dg-warning ".\\+. flag used" "-Wformat" { target *-*-* } .-1 } */ /* { dg-warning ".\\+. flag used" "-Wformat" { target *-*-* } .-1 } */
T (1, "%-.*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */ T (1, "%-.*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
T (1, "% .*hhu", 0, y); /* { dg-warning "between 0 and 3 bytes" } */ T (1, "% .*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
/* { dg-warning ". . flag used" "-Wformat" { target *-*-* } .-1 } */ /* { dg-warning ". . flag used" "-Wformat" { target *-*-* } .-1 } */
} }
......
...@@ -60,6 +60,9 @@ void test_sprintf_chk_string (const char *s, const char *t) ...@@ -60,6 +60,9 @@ void test_sprintf_chk_string (const char *s, const char *t)
T (1, "%s", x ? "1" : ""); /* { dg-warning "nul past the end" } */ T (1, "%s", x ? "1" : ""); /* { dg-warning "nul past the end" } */
T (1, "%s", x ? s : "1"); /* { dg-warning "nul past the end" } */ T (1, "%s", x ? s : "1"); /* { dg-warning "nul past the end" } */
T (1, "%s", x ? "1" : s); /* { dg-warning "nul past the end" } */ T (1, "%s", x ? "1" : s); /* { dg-warning "nul past the end" } */
/* When neither string is known no warning should be issued at level 1
since their lenghts are assumed to be zero. */
T (1, "%s", x ? s : t); T (1, "%s", x ? s : t);
T (2, "%s", x ? "" : "1"); T (2, "%s", x ? "" : "1");
...@@ -85,7 +88,7 @@ void test_sprintf_chk_integer_value (void) ...@@ -85,7 +88,7 @@ void test_sprintf_chk_integer_value (void)
T ( 1, "%i", i ( 0)); /* { dg-warning "nul past the end" } */ T ( 1, "%i", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%i", i ( 1)); /* { dg-warning "nul past the end" } */ T ( 1, "%i", i ( 1)); /* { dg-warning "nul past the end" } */
T ( 1, "%i", i ( -1)); /* { dg-warning "into a region" } */ T ( 1, "%i", i ( -1)); /* { dg-warning "into a region" } */
T ( 1, "%i_", i ( 1)); /* { dg-warning "character ._. at offset 2 past the end" } */ T ( 1, "%i_", i ( 1)); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 1, "_%i", i ( 1)); /* { dg-warning "into a region" } */ T ( 1, "_%i", i ( 1)); /* { dg-warning "into a region" } */
T ( 1, "_%i_",i ( 1)); /* { dg-warning "into a region" } */ T ( 1, "_%i_",i ( 1)); /* { dg-warning "into a region" } */
T ( 1, "%o", i ( 0)); /* { dg-warning "nul past the end" } */ T ( 1, "%o", i ( 0)); /* { dg-warning "nul past the end" } */
...@@ -102,7 +105,7 @@ void test_sprintf_chk_integer_value (void) ...@@ -102,7 +105,7 @@ void test_sprintf_chk_integer_value (void)
T ( 2, "%i", i ( 10)); /* { dg-warning "nul past the end" } */ T ( 2, "%i", i ( 10)); /* { dg-warning "nul past the end" } */
T ( 2, "%i_", i ( 0)); /* { dg-warning "nul past the end" } */ T ( 2, "%i_", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 2, "_%i", i ( 0)); /* { dg-warning "nul past the end" } */ T ( 2, "_%i", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 2, "_%i_",i ( 0)); /* { dg-warning "character ._. at offset 3 past the end" } */ T ( 2, "_%i_",i ( 0)); /* { dg-warning " 1 byte into a region of size 0" } */
T ( 2, "%o", i ( 1)); T ( 2, "%o", i ( 1));
T ( 2, "%o", i ( 7)); T ( 2, "%o", i ( 7));
T ( 2, "%o", i ( 010)); /* { dg-warning "nul past the end" } */ T ( 2, "%o", i ( 010)); /* { dg-warning "nul past the end" } */
...@@ -211,10 +214,10 @@ void test_sprintf_chk_range_schar (void) ...@@ -211,10 +214,10 @@ void test_sprintf_chk_range_schar (void)
T ( 3, "%i", R ( 0, 99)); T ( 3, "%i", R ( 0, 99));
T ( 3, "%i", R ( 0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */ T ( 3, "%i", R ( 0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
/* The following call may write as few as 3 bytes and as many as 5. /* The following call may write as few as 2 bytes and as many as 4.
It's a judgment call how best to diagnose it to make the potential It's a judgment call how best to diagnose it to make the potential
problem clear. */ problem clear. */
T ( 3, "%i%i", R (1, 10), R (9, 10)); /* { dg-warning "may write a terminating nul past the end|.%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 "directive writing between 1 and 2 bytes into a region of size between 1 and 2" } */
T ( 4, "%i%i", R (10, 11), R (12, 13)); /* { dg-warning "nul past the end" } */ T ( 4, "%i%i", R (10, 11), R (12, 13)); /* { dg-warning "nul past the end" } */
...@@ -224,7 +227,11 @@ void test_sprintf_chk_range_schar (void) ...@@ -224,7 +227,11 @@ void test_sprintf_chk_range_schar (void)
T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 10)); /* { dg-warning "may write a terminating nul past the end" } */ T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 10)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */ T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 10), R (0, 9), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */ T ( 6, "%i_%i_%i", R (0, 10), R (0, 9), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 10)); /* { dg-warning "may write a terminating nul past the end|.%i. directive writing between 1 and 2 bytes into a region of size 1" } */ T ( 6, "%hhi_%hi_%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 between 1 and 2" } */
T ( 6, "%3i|%2i/%1i", R (0, 99), R (0, 99), R (0, 99)); /* { dg-warning "./. directive writing 1 byte into a region of size 0" } */
T ( 6, "%.3i|%.2i/%i", R (0, 99), R (0, 99), R (0, 99)); /* { dg-warning "./. directive writing 1 byte into a region of size 0" } */
T ( 6, "%.3i|%.2i/%i", R (0, 119), R (0, 99), R (0, 99)); /* { dg-warning "./. directive writing 1 byte into a region of size 0" } */
T ( 6, "%.3i|%.2i/%i", R (0, 1), R (0, 2), R (0, 3)); /* { dg-warning "./. directive writing 1 byte into a region of size 0" } */
} }
void test_sprintf_chk_range_uchar (void) void test_sprintf_chk_range_uchar (void)
......
...@@ -12,14 +12,14 @@ void test (void) ...@@ -12,14 +12,14 @@ void test (void)
The redundant argument is there to get around GCC bug 77799. */ The redundant argument is there to get around GCC bug 77799. */
sprintf (dst + 2, "1", 0); sprintf (dst + 2, "1", 0);
/* { dg-warning "writing a terminating nul past the end of the destination" "nul warning" { target *-*-* } .-1 } /* { dg-warning "writing a terminating nul past the end of the destination" "nul warning" { target *-*-* } .-1 }
{ dg-message "format output 2 bytes into a destination of size 1" "note" { target *-*-* } .-2 } { dg-message ".sprintf. output 2 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat output: redundant argument" } { dg-begin-multiline-output "-Wformat output: redundant argument" }
sprintf (dst + 2, "1", 0); sprintf (dst + 2, "1", 0);
^~~ ^~~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "-Wformat-overflow output" } { dg-begin-multiline-output "-Wformat-overflow output" }
sprintf (dst + 2, "1", 0); sprintf (dst + 2, "1", 0);
~^ ^
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" } { dg-begin-multiline-output "note" }
sprintf (dst + 2, "1", 0); sprintf (dst + 2, "1", 0);
...@@ -29,15 +29,15 @@ void test (void) ...@@ -29,15 +29,15 @@ void test (void)
/* Verify thet the caret points at the first format character written /* Verify thet the caret points at the first format character written
past the end of the destination. */ past the end of the destination. */
sprintf (dst, "1234", 0); sprintf (dst, "1234", 0);
/* { dg-warning "writing format character .4. at offset 3 past the end of the destination" "nul warning" { target *-*-* } .-1 } /* { dg-warning "writing 4 bytes into a region of size 3" "overlong format string" { target *-*-* } .-1 }
{ dg-message "format output 5 bytes into a destination of size 3" "note" { target *-*-* } .-2 } { dg-message ".sprintf. output 5 bytes into a destination of size 3" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat output: redundant argument" } { dg-begin-multiline-output "-Wformat output: redundant argument" }
sprintf (dst, "1234", 0); sprintf (dst, "1234", 0);
^~~~~~ ^~~~~~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "-Wformat-overflow output" } { dg-begin-multiline-output "-Wformat-overflow output" }
sprintf (dst, "1234", 0); sprintf (dst, "1234", 0);
^ ~~~^
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" } { dg-begin-multiline-output "note" }
sprintf (dst, "1234", 0); sprintf (dst, "1234", 0);
...@@ -48,15 +48,15 @@ void test (void) ...@@ -48,15 +48,15 @@ void test (void)
past the end of the destination and the rest of the format string past the end of the destination and the rest of the format string
is underlined. */ is underlined. */
sprintf (dst, "12345", 0); sprintf (dst, "12345", 0);
/* { dg-warning "writing format character .4. at offset 3 past the end of the destination" "nul warning" { target *-*-* } .-1 } /* { dg-warning "writing 5 bytes into a region of size 3" "nul warning" { target *-*-* } .-1 }
{ dg-message "format output 6 bytes into a destination of size 3" "note" { target *-*-* } .-2 } { dg-message ".sprintf. output 6 bytes into a destination of size 3" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat output: redundant argument" } { dg-begin-multiline-output "-Wformat output: redundant argument" }
sprintf (dst, "12345", 0); sprintf (dst, "12345", 0);
^~~~~~~ ^~~~~~~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "-Wformat-overflow output" } { dg-begin-multiline-output "-Wformat-overflow output" }
sprintf (dst, "12345", 0); sprintf (dst, "12345", 0);
^~ ~~~^~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" } { dg-begin-multiline-output "note" }
sprintf (dst, "12345", 0); sprintf (dst, "12345", 0);
...@@ -67,10 +67,10 @@ void test (void) ...@@ -67,10 +67,10 @@ void test (void)
get around GCC bug 77671. */ get around GCC bug 77671. */
sprintf (dst + 2, "%-s", "1"); sprintf (dst + 2, "%-s", "1");
/* { dg-warning "writing a terminating nul past the end of the destination" "warning" { target *-*-* } .-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-message ".sprintf. output 2 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat-overflow output" } { dg-begin-multiline-output "-Wformat-overflow output" }
sprintf (dst + 2, "%-s", "1"); sprintf (dst + 2, "%-s", "1");
~~~^ ^
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-begin-multiline-output "note" } { dg-begin-multiline-output "note" }
sprintf (dst + 2, "%-s", "1"); sprintf (dst + 2, "%-s", "1");
...@@ -79,7 +79,7 @@ void test (void) ...@@ -79,7 +79,7 @@ void test (void)
sprintf (dst + 2, "%-s", "abcd"); sprintf (dst + 2, "%-s", "abcd");
/* { dg-warning ".%-s. directive writing 4 bytes into a region of size 1" "warning" { target *-*-* } .-1 } /* { 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-message ".sprintf. output 5 bytes into a destination of size 1" "note" { target *-*-* } .-2 }
{ dg-begin-multiline-output "-Wformat-overflow output" } { dg-begin-multiline-output "-Wformat-overflow output" }
sprintf (dst + 2, "%-s", "abcd"); sprintf (dst + 2, "%-s", "abcd");
^~~ ~~~~~~ ^~~ ~~~~~~
...@@ -105,8 +105,8 @@ extern char *ptr; ...@@ -105,8 +105,8 @@ extern char *ptr;
/* Evaluate to an array of SIZE characters when non-negative and LINE /* Evaluate to an array of SIZE characters when non-negative and LINE
is not set or set to the line the macro is on, or to a pointer to is not set or set to the line the macro is on, or to a pointer to
an unknown object otherwise. */ an unknown object otherwise. */
#define buffer(size) \ #define buffer(size) \
(0 <= size && (!LINE || __LINE__ == LINE) \ (0 <= size && (!LINE || __LINE__ == LINE) \
? buffer + sizeof buffer - size : ptr) ? buffer + sizeof buffer - size : ptr)
/* Verify that the note printed along with the diagnostic mentions /* Verify that the note printed along with the diagnostic mentions
...@@ -124,7 +124,7 @@ void test_sprintf_note (void) ...@@ -124,7 +124,7 @@ void test_sprintf_note (void)
^~ ^~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-message "format output 4 bytes into a destination of size 0" "" { target *-*-* } .-7 } { dg-message ".__builtin_sprintf. output 4 bytes into a destination of size 0" "" { target *-*-* } .-7 }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "" }
__builtin_sprintf (buffer (0), "%c%s%i", '1', "2", 3); __builtin_sprintf (buffer (0), "%c%s%i", '1', "2", 3);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -137,7 +137,7 @@ void test_sprintf_note (void) ...@@ -137,7 +137,7 @@ void test_sprintf_note (void)
^~ ~~~~ ^~ ~~~~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-message "format output 6 bytes into a destination of size 1" "" { target *-*-* } .-7 } { dg-message ".__builtin_sprintf. output 6 bytes into a destination of size 1" "" { target *-*-* } .-7 }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "" }
__builtin_sprintf (buffer (1), "%c%s%i", '1', "23", 45); __builtin_sprintf (buffer (1), "%c%s%i", '1', "23", 45);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -150,7 +150,7 @@ void test_sprintf_note (void) ...@@ -150,7 +150,7 @@ void test_sprintf_note (void)
^~ ^~
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-message "format output 6 bytes into a destination of size 2" "" { target *-*-* } .-7 } { dg-message ".__builtin_sprintf. output 6 bytes into a destination of size 2" "" { target *-*-* } .-7 }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "" }
__builtin_sprintf (buffer (2), "%c%s%i", '1', "2", 345); __builtin_sprintf (buffer (2), "%c%s%i", '1', "2", 345);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -160,10 +160,10 @@ void test_sprintf_note (void) ...@@ -160,10 +160,10 @@ void test_sprintf_note (void)
/* { dg-warning "41: writing a terminating nul past the end of the destination" "" { target *-*-* } .-1 } /* { dg-warning "41: writing a terminating nul past the end of the destination" "" { target *-*-* } .-1 }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "" }
__builtin_sprintf (buffer (6), "%c%s%i", '1', "2", 3456); __builtin_sprintf (buffer (6), "%c%s%i", '1', "2", 3456);
~~~~~~^ ^
{ dg-end-multiline-output "" } { dg-end-multiline-output "" }
{ dg-message "format output 7 bytes into a destination of size 6" "" { target *-*-* } .-7 } { dg-message ".__builtin_sprintf. output 7 bytes into a destination of size 6" "" { target *-*-* } .-7 }
{ dg-begin-multiline-output "" } { dg-begin-multiline-output "" }
__builtin_sprintf (buffer (6), "%c%s%i", '1', "2", 3456); __builtin_sprintf (buffer (6), "%c%s%i", '1', "2", 3456);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -52,7 +52,7 @@ void fuint (unsigned j, char *p) ...@@ -52,7 +52,7 @@ void fuint (unsigned j, char *p)
if (j > 999) if (j > 999)
return; return;
snprintf (p, 4, "%3u", j); /* { dg-bogus "may be truncated" "unsigned int" { xfail *-*-* } } */ snprintf (p, 4, "%3u", j);
} }
void fint (int j, char *p) void fint (int j, char *p)
...@@ -62,7 +62,7 @@ void fint (int j, char *p) ...@@ -62,7 +62,7 @@ void fint (int j, char *p)
if (k > 999) if (k > 999)
return; return;
snprintf (p, 4, "%3u", k); /* { dg-bogus "may be truncated" "signed int" { xfail *-*-* } } */ snprintf (p, 4, "%3u", k);
} }
void fulong (unsigned long j, char *p) void fulong (unsigned long j, char *p)
...@@ -70,7 +70,7 @@ void fulong (unsigned long j, char *p) ...@@ -70,7 +70,7 @@ void fulong (unsigned long j, char *p)
if (j > 999) if (j > 999)
return; return;
snprintf (p, 4, "%3lu", j); /* { dg-bogus "may be truncated" "unsigned long" { xfail *-*-* } } */ snprintf (p, 4, "%3lu", j);
} }
void flong (long j, char *p) void flong (long j, char *p)
...@@ -80,7 +80,7 @@ void flong (long j, char *p) ...@@ -80,7 +80,7 @@ void flong (long j, char *p)
if (k > 999) if (k > 999)
return; return;
snprintf (p, 4, "%3lu", k); /* { dg-bogus "may be truncated" "signed long" { xfail *-*-* } } */ snprintf (p, 4, "%3lu", k);
} }
void fullong (unsigned long long j, char *p) void fullong (unsigned long long j, char *p)
...@@ -88,7 +88,7 @@ void fullong (unsigned long long j, char *p) ...@@ -88,7 +88,7 @@ void fullong (unsigned long long j, char *p)
if (j > 999) if (j > 999)
return; return;
snprintf (p, 4, "%3llu", j); /* { dg-bogus "may be truncated" "signed long" { xfail *-*-* } } */ snprintf (p, 4, "%3llu", j);
} }
void fllong (long long j, char *p) void fllong (long long j, char *p)
...@@ -98,7 +98,7 @@ void fllong (long long j, char *p) ...@@ -98,7 +98,7 @@ void fllong (long long j, char *p)
if (k > 999) if (k > 999)
return; return;
snprintf (p, 4, "%3llu", k); /* { dg-bogus "may be truncated" "unsigned long long" { xfail *-*-* } } */ snprintf (p, 4, "%3llu", k);
} }
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ /* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
...@@ -32,14 +32,16 @@ void test_integer_cst (void) ...@@ -32,14 +32,16 @@ void test_integer_cst (void)
void test_integer_var (int i) void test_integer_var (int i)
{ {
T (0, "%*d", INT_MIN, i); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*d", INT_MIN, i); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*d", INT_MAX, i); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*d", INT_MIN, i); /* { dg-warning "writing between 1 and 11 bytes" } */
/* The following writes INT_MAX digits and, when i is negative, a minus /* The following writes INT_MAX digits and, when i is negative, a minus
sign. */ sign. */
T (0, "%.*d", INT_MAX, i); /* { dg-warning "writing between 2147483647 and 2147483648 bytes" } */ T (0, "%.*d", INT_MAX, i); /* { dg-warning "writing between 2147483647 and 2147483648 bytes" } */
T (0, "%.*d", INT_MIN, i); /* { dg-warning "writing between 1 and 11 bytes" } */
/* The following writes a range because of the possible minus sign. */
T (0, "%.*d", INT_MAX, i); /* { dg-warning "writing between 2147483647 and 2147483648 bytes" } */
T (0, "%*.*d", INT_MIN, INT_MIN, i); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*.*d", INT_MIN, INT_MIN, i); /* { dg-warning "writing 2147483648 bytes" } */
/* The following writes INT_MAX digits and, when i is negative, a minus /* The following writes INT_MAX digits and, when i is negative, a minus
...@@ -52,7 +54,10 @@ void test_floating_a_cst (void) ...@@ -52,7 +54,10 @@ void test_floating_a_cst (void)
T (0, "%*a", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*a", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*a", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ T (0, "%*a", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*a", INT_MIN, 0.); /* { dg-warning "writing 6 bytes" } */ /* %a is poorly specified and as a result some implementations trim
redundant trailing zeros (e.g., Glibc) and others don't (e.g.,
Solaris). */
T (0, "%.*a", INT_MIN, 0.); /* { dg-warning "writing between 6 and 20 bytes" } */
T (0, "%.*a", INT_MAX, 0.); /* { dg-warning "writing 2147483654 bytes" } */ T (0, "%.*a", INT_MAX, 0.); /* { dg-warning "writing 2147483654 bytes" } */
...@@ -111,7 +116,7 @@ void test_floating_f_cst (void) ...@@ -111,7 +116,7 @@ void test_floating_f_cst (void)
T (0, "%*f", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*f", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*f", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ T (0, "%*f", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 8 byte" } */ T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 8 bytes" } */
T (0, "%.*f", INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */ T (0, "%.*f", INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */
...@@ -178,14 +183,14 @@ void test_string_cst (void) ...@@ -178,14 +183,14 @@ void test_string_cst (void)
void test_string_var (const char *s) void test_string_var (const char *s)
{ {
T (0, "%*s", INT_MIN, s); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*s", INT_MIN, s); /* { dg-warning "writing 2147483648 or more bytes" } */
T (0, "%*s", INT_MAX, s); /* { dg-warning "writing 2147483647 bytes" } */ T (0, "%*s", INT_MAX, s); /* { dg-warning "writing 2147483647 or more bytes" } */
T (0, "%.*s", INT_MIN, s); /* { dg-warning "writing a terminating nul" } */ T (0, "%.*s", INT_MIN, s); /* { dg-warning "writing a terminating nul" } */
T (0, "%.*s", INT_MAX, s); /* { dg-warning "writing between 0 and 2147483647 bytes" } */ T (0, "%.*s", INT_MAX, s); /* { dg-warning "writing up to 2147483647 bytes" } */
T (0, "%*.*s", INT_MIN, INT_MIN, s); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*.*s", INT_MIN, INT_MIN, s); /* { dg-warning "writing 2147483648 or more bytes" } */
T (0, "%*.*s", INT_MAX, INT_MAX, s); /* { dg-warning "writing 2147483647 bytes" } */ T (0, "%*.*s", INT_MAX, INT_MAX, s); /* { dg-warning "writing 2147483647 bytes" } */
} }
...@@ -37,19 +37,17 @@ void test_a (int w, int p, double x) ...@@ -37,19 +37,17 @@ void test_a (int w, int p, double x)
T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */ T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */ T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
T1 ("%*.a", w); /* { dg-warning "writing 6 or more bytes" } */ T1 ("%*.a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T1 ("%*.0a", w); /* { dg-warning "writing 6 or more bytes" } */ T1 ("%*.0a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T1 ("%*.1a", w); /* { dg-warning "writing 8 or more bytes" } */ T1 ("%*.1a", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T1 ("%*.2a", w); /* { dg-warning "writing 9 or more bytes" } */ T1 ("%*.2a", w); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
T1 ("%.*a", p); /* { dg-warning "writing 6 or more bytes" } */ T1 ("%.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%1.*a", p); /* { dg-warning "writing 6 or more bytes" } */ T1 ("%1.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%2.*a", p); /* { dg-warning "writing 6 or more bytes" } */ T1 ("%2.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%3.*a", p); /* { dg-warning "writing 6 or more bytes" } */ T1 ("%3.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */ T2 ("%*.*a", w, p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
} }
/* Exercise %e. */ /* Exercise %e. */
...@@ -69,19 +67,17 @@ void test_e (int w, int p, double x) ...@@ -69,19 +67,17 @@ void test_e (int w, int p, double x)
T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */ T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */ T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
T1 ("%*.e", w); /* { dg-warning "writing 5 or more bytes" } */ T1 ("%*.e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
T1 ("%*.0e", w); /* { dg-warning "writing 5 or more bytes" } */ T1 ("%*.0e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
T1 ("%*.1e", w); /* { dg-warning "writing 7 or more bytes" } */ T1 ("%*.1e", w); /* { dg-warning "writing between 7 and 2147483648 bytes" } */
T1 ("%*.2e", w); /* { dg-warning "writing 8 or more bytes" } */ T1 ("%*.2e", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T1 ("%.*e", p); /* { dg-warning "writing 5 or more bytes" } */ T1 ("%.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%1.*e", p); /* { dg-warning "writing 5 or more bytes" } */ T1 ("%1.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%2.*e", p); /* { dg-warning "writing 5 or more bytes" } */ T1 ("%2.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%3.*e", p); /* { dg-warning "writing 5 or more bytes" } */ T1 ("%3.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */ T2 ("%*.*e", w, p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
} }
/* Exercise %f. */ /* Exercise %f. */
...@@ -103,19 +99,17 @@ void test_f (int w, int p, double x) ...@@ -103,19 +99,17 @@ void test_f (int w, int p, double x)
T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */ T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */ T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
T1 ("%*.f", w); /* { dg-warning "writing 1 or more bytes" } */ T1 ("%*.f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
T1 ("%*.0f", w); /* { dg-warning "writing 1 or more bytes" } */ T1 ("%*.0f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
T1 ("%*.1f", w); /* { dg-warning "writing 3 or more bytes" } */ T1 ("%*.1f", w); /* { dg-warning "writing between 3 and 2147483648 bytes" } */
T1 ("%*.2f", w); /* { dg-warning "writing 4 or more bytes" } */ T1 ("%*.2f", w); /* { dg-warning "writing between 4 and 2147483648 bytes" } */
T1 ("%.*f", p); /* { dg-warning "writing 1 or more bytes" } */ T1 ("%.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T1 ("%1.*f", p); /* { dg-warning "writing 1 or more bytes" } */ T1 ("%1.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T1 ("%2.*f", p); /* { dg-warning "writing 2 or more bytes" } */ T1 ("%2.*f", p); /* { dg-warning "writing between 2 and 2147483958 bytes" } */
T1 ("%3.*f", p); /* { dg-warning "writing 3 or more bytes" } */ T1 ("%3.*f", p); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */ T2 ("%*.*f", w, p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
} }
/* Exercise %g. The expected output is the lesser of %e and %f. */ /* Exercise %g. The expected output is the lesser of %e and %f. */
...@@ -156,18 +150,18 @@ void test_a_va (va_list va) ...@@ -156,18 +150,18 @@ void test_a_va (va_list va)
T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */ T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */ T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
T ("%*.a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%*.a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T ("%*.0a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%*.0a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T ("%*.1a"); /* { dg-warning "writing 8 or more bytes" } */ T ("%*.1a"); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T ("%*.2a"); /* { dg-warning "writing 9 or more bytes" } */ T ("%*.2a"); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
T ("%.*a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%1.*a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%1.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%2.*a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%2.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%6.*a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%6.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%9.*a"); /* { dg-warning "writing 9 or more bytes" } */ T ("%9.*a"); /* { dg-warning "writing between 9 and 2147483658 bytes" } */
T ("%*.*a"); /* { dg-warning "writing 6 or more bytes" } */ T ("%*.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
} }
/* Exercise %e. */ /* Exercise %e. */
...@@ -195,12 +189,12 @@ void test_e_va (va_list va) ...@@ -195,12 +189,12 @@ void test_e_va (va_list va)
T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */ T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
T ("%7.e"); /* { dg-warning "writing 7 bytes" } */ T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
T ("%.*e"); /* { dg-warning "writing 5 or more bytes" } */ T ("%.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T ("%1.*e"); /* { dg-warning "writing 5 or more bytes" } */ T ("%1.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T ("%6.*e"); /* { dg-warning "writing 6 or more bytes" } */ T ("%6.*e"); /* { dg-warning "writing between 6 and 2147483655 bytes" } */
T ("%9.*e"); /* { dg-warning "writing 9 or more bytes" } */ T ("%9.*e"); /* { dg-warning "writing between 9 and 2147483655 bytes" } */
T ("%*.*e"); /* { dg-warning "writing 5 or more bytes" } */ T ("%*.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
} }
/* Exercise %f. */ /* Exercise %f. */
...@@ -232,11 +226,11 @@ void test_f_va (va_list va) ...@@ -232,11 +226,11 @@ void test_f_va (va_list va)
T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */ T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */ T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
T ("%.*f"); /* { dg-warning "writing 1 or more bytes" } */ T ("%.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T ("%1.*f"); /* { dg-warning "writing 1 or more bytes" } */ T ("%1.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T ("%3.*f"); /* { dg-warning "writing 3 or more bytes" } */ T ("%3.*f"); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
T ("%*.*f"); /* { dg-warning "writing 1 or more bytes" } */ T ("%*.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
} }
/* Exercise %g. The expected output is the lesser of %e and %f. */ /* Exercise %g. The expected output is the lesser of %e and %f. */
...@@ -268,9 +262,9 @@ void test_g_va (va_list va) ...@@ -268,9 +262,9 @@ void test_g_va (va_list va)
T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */ T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */ T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
T ("%.*g"); /* { dg-warning "writing 1 or more bytes" } */ T ("%.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
T ("%1.*g"); /* { dg-warning "writing 1 or more bytes" } */ T ("%1.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
T ("%4.*g"); /* { dg-warning "writing 4 or more bytes" } */ T ("%4.*g"); /* { dg-warning "writing between 4 and 310 bytes" } */
T ("%*.*g"); /* { dg-warning "writing 1 or more bytes" } */ T ("%*.*g"); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
} }
...@@ -3,10 +3,8 @@ ...@@ -3,10 +3,8 @@
constant folding. With optimization enabled the test will fail to constant folding. With optimization enabled the test will fail to
link if any of the assertions fails. Without optimization the test link if any of the assertions fails. Without optimization the test
aborts at runtime if any of the assertions fails. */ aborts at runtime if any of the assertions fails. */
/* { dg-do run } /* { dg-do run } */
The h and hh length modifiers are a C99 feature (see PR 78959). /* { dg-additional-options "-O2 -Wall -Wno-pedantic -fprintf-return-value" } */
{ dg-require-effective-target c99_runtime }
{ dg-additional-options "-O2 -Wall -Wno-pedantic -fprintf-return-value" } */
#ifndef LINE #ifndef LINE
# define LINE 0 # define LINE 0
...@@ -320,7 +318,6 @@ test_d_i (int i, long li) ...@@ -320,7 +318,6 @@ test_d_i (int i, long li)
#if __SIZEOF_SHORT__ == 2 #if __SIZEOF_SHORT__ == 2
RNG ( 1, 6, 7, "%hi", i); RNG ( 1, 6, 7, "%hi", i);
RNG ( 1, 5, 6, "%hu", i); RNG ( 1, 5, 6, "%hu", i);
RNG ( 1, 6, 7, "%.1hi", i); RNG ( 1, 6, 7, "%.1hi", i);
RNG ( 2, 6, 7, "%.2hi", i); RNG ( 2, 6, 7, "%.2hi", i);
RNG ( 3, 6, 7, "%.3hi", i); RNG ( 3, 6, 7, "%.3hi", i);
...@@ -328,7 +325,6 @@ test_d_i (int i, long li) ...@@ -328,7 +325,6 @@ test_d_i (int i, long li)
RNG ( 5, 6, 7, "%.5hi", i); RNG ( 5, 6, 7, "%.5hi", i);
RNG ( 6, 7, 8, "%.6hi", i); RNG ( 6, 7, 8, "%.6hi", i);
RNG ( 7, 8, 9, "%.7hi", i); RNG ( 7, 8, 9, "%.7hi", i);
#elif __SIZEOF_SHORT__ == 4 #elif __SIZEOF_SHORT__ == 4
RNG ( 1, 11, 12, "%hi", i); RNG ( 1, 11, 12, "%hi", i);
RNG ( 1, 10, 11, "%hu", i); RNG ( 1, 10, 11, "%hu", i);
...@@ -474,16 +470,21 @@ test_a_double (double d) ...@@ -474,16 +470,21 @@ test_a_double (double d)
EQL ( 6, 7, "%.0a", 0.0); /* 0x0p+0 */ EQL ( 6, 7, "%.0a", 0.0); /* 0x0p+0 */
EQL ( 6, 7, "%.0a", 1.0); /* 0x8p-3 */ EQL ( 6, 7, "%.0a", 1.0); /* 0x8p-3 */
EQL ( 6, 7, "%.0a", 2.0); /* 0x8p-2 */ EQL ( 6, 7, "%.0a", 2.0); /* 0x8p-2 */
EQL ( 8, 9, "%.1a", 3.0); /* 0xc.0p-2 */
EQL ( 9, 10, "%.2a", 4.0); /* 0x8.00p-1 */
EQL (10, 11, "%.3a", 5.0); /* 0xa.000p-1 */
EQL (11, 12, "%.*a", 4, 6.0); /* 0xc.0000p-1 */ /* The decimal point may be up to MB_LEN_MAX long. */
EQL (12, 13, "%.*a", 5, 7.0); /* 0xe.00000p-1 */ RNG ( 8, 13, 14, "%.1a", 3.0); /* 0xc.0p-2 */
RNG ( 9, 14, 15, "%.2a", 4.0); /* 0x8.00p-1 */
RNG (10, 15, 16, "%.3a", 5.0); /* 0xa.000p-1 */
RNG (11, 16, 17, "%.*a", 4, 6.0); /* 0xc.0000p-1 */
RNG (12, 17, 18, "%.*a", 5, 7.0); /* 0xe.00000p-1 */
/* d is in [ 0, -DBL_MAX ] */ /* d is in [ 0, -DBL_MAX ] */
RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */ RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */
RNG ( 6, 12, 13, "%.1a", d); /* 0x0p+0 ... -0x2.0p+1023 */ /* %a is poorly specified and allows for implementations divergence:
RNG ( 6, 13, 14, "%.2a", d); /* 0x0p+0 ... -0x2.00p+1023 */ some (such as Glibc) trim redundant trailing zeros after decimal
point and others (e.g., Solaris) don't. */
RNG ( 8, 30, 31, "%.1a", d); /* 0x0.0p+0 ... -0x2.0...0p+1023 */
RNG ( 9, 30, 31, "%.2a", d); /* 0x0.00p+0 ... -0x2.00...0p+1023 */
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
...@@ -492,159 +493,161 @@ test_a_long_double (void) ...@@ -492,159 +493,161 @@ test_a_long_double (void)
EQL ( 6, 7, "%.0La", 0.0L); /* 0x0p+0 */ EQL ( 6, 7, "%.0La", 0.0L); /* 0x0p+0 */
EQL ( 6, 7, "%.0La", 1.0L); /* 0x8p-3 */ EQL ( 6, 7, "%.0La", 1.0L); /* 0x8p-3 */
EQL ( 6, 7, "%.0La", 2.0L); /* 0x8p-2 */ 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 */ RNG ( 8, 13, 14, "%.1La", 3.0L); /* 0xc.0p-2 */
RNG ( 9, 14, 15, "%.2La", 4.0L); /* 0xa.00p-1 */
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_e_double (double d) test_e_double (double d)
{ {
EQL (12, 13, "%e", 1.0e0); RNG (12, 17, 18, "%e", 1.0e0);
EQL (13, 14, "%e", -1.0e0); RNG (13, 18, 19, "%e", -1.0e0);
EQL (12, 13, "%e", 1.0e+1); RNG (12, 17, 18, "%e", 1.0e+1);
EQL (13, 14, "%e", -1.0e+1); RNG (13, 18, 19, "%e", -1.0e+1);
EQL (12, 13, "%e", 1.0e+12); RNG (12, 17, 18, "%e", 1.0e+12);
EQL (13, 14, "%e", -1.0e+12); RNG (13, 18, 19, "%e", -1.0e+12);
EQL (13, 14, "%e", 1.0e+123); RNG (13, 18, 19, "%e", 1.0e+123);
EQL (14, 15, "%e", -1.0e+123); RNG (14, 19, 20, "%e", -1.0e+123);
EQL (12, 13, "%e", 9.999e+99); RNG (12, 17, 18, "%e", 9.999e+99);
EQL (12, 13, "%e", 9.9999e+99); RNG (12, 17, 18, "%e", 9.9999e+99);
EQL (12, 13, "%e", 9.99999e+99); RNG (12, 17, 18, "%e", 9.99999e+99);
/* The actual output of the following directive depends on the rounding /* The actual output of the following directive depends on the rounding
mode. */ mode. */
/* EQL (12, "%e", 9.9999994e+99); */ /* RNG (12, "%e", 9.9999994e+99); */
EQL (12, 13, "%e", 1.0e-1); RNG (12, 17, 18, "%e", 1.0e-1);
EQL (12, 13, "%e", 1.0e-12); RNG (12, 17, 18, "%e", 1.0e-12);
EQL (13, 14, "%e", 1.0e-123); RNG (13, 18, 19, "%e", 1.0e-123);
RNG (12, 14, 15, "%e", d); RNG (12, 19, 20, "%e", d);
RNG ( 5, 7, 8, "%.e", d); RNG ( 5, 11, 12, "%.e", d);
RNG ( 5, 7, 8, "%.0e", d); RNG ( 5, 12, 13, "%.0e", d);
RNG ( 7, 9, 10, "%.1e", d); RNG ( 7, 14, 15, "%.1e", d);
RNG ( 8, 10, 11, "%.2e", d); RNG ( 8, 15, 16, "%.2e", d);
RNG ( 9, 11, 12, "%.3e", d); RNG ( 9, 16, 17, "%.3e", d);
RNG (10, 12, 13, "%.4e", d); RNG (10, 17, 18, "%.4e", d);
RNG (11, 13, 14, "%.5e", d); RNG (11, 18, 19, "%.5e", d);
RNG (12, 14, 15, "%.6e", d); RNG (12, 19, 20, "%.6e", d);
RNG (13, 15, 16, "%.7e", d); RNG (13, 20, 21, "%.7e", d);
RNG (4006, 4008, 4009, "%.4000e", d); RNG (4006, 4013, 4014, "%.4000e", d);
RNG ( 5, 7, 8, "%.*e", 0, d); RNG ( 5, 7, 8, "%.*e", 0, d);
RNG ( 7, 9, 10, "%.*e", 1, d); RNG ( 7, 14, 15, "%.*e", 1, d);
RNG ( 8, 10, 11, "%.*e", 2, d); RNG ( 8, 15, 16, "%.*e", 2, d);
RNG ( 9, 11, 12, "%.*e", 3, d); RNG ( 9, 16, 17, "%.*e", 3, d);
RNG (10, 12, 13, "%.*e", 4, d); RNG (10, 17, 18, "%.*e", 4, d);
RNG (11, 13, 14, "%.*e", 5, d); RNG (11, 18, 19, "%.*e", 5, d);
RNG (12, 14, 15, "%.*e", 6, d); RNG (12, 19, 20, "%.*e", 6, d);
RNG (13, 15, 16, "%.*e", 7, d); RNG (13, 20, 21, "%.*e", 7, d);
RNG (4006, 4008, 4009, "%.*e", 4000, d); RNG (4006, 4013, 4014, "%.*e", 4000, d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_e_long_double (long double d) test_e_long_double (long double d)
{ {
EQL (12, 13, "%Le", 1.0e0L); RNG (12, 17, 18, "%Le", 1.0e0L);
EQL (13, 14, "%Le", -1.0e0L); RNG (13, 18, 19, "%Le", -1.0e0L);
EQL (12, 13, "%Le", 1.0e+1L); RNG (12, 17, 18, "%Le", 1.0e+1L);
EQL (13, 14, "%Le", -1.0e+1L); RNG (13, 18, 19, "%Le", -1.0e+1L);
EQL (12, 13, "%Le", 1.0e+12L); RNG (12, 18, 19, "%Le", 1.0e+12L);
EQL (13, 14, "%Le", -1.0e+12L); RNG (13, 19, 20, "%Le", -1.0e+12L);
EQL (13, 14, "%Le", 1.0e+123L); RNG (13, 19, 20, "%Le", 1.0e+123L);
EQL (14, 15, "%Le", -1.0e+123L); RNG (14, 20, 21, "%Le", -1.0e+123L);
EQL (12, 13, "%Le", 9.999e+99L); RNG (12, 18, 19, "%Le", 9.999e+99L);
EQL (12, 13, "%Le", 9.9999e+99L); RNG (12, 18, 19, "%Le", 9.9999e+99L);
EQL (12, 13, "%Le", 9.99999e+99L); RNG (12, 18, 19, "%Le", 9.99999e+99L);
#if __DBL_DIG__ < __LDBL_DIG__ #if __DBL_DIG__ < __LDBL_DIG__
EQL (12, 13, "%Le", 9.999999e+99L); RNG (12, 17, 18, "%Le", 9.999999e+99L);
#else #else
RNG (12, 13, 14, "%Le", 9.999999e+99L); RNG (12, 18, 19, "%Le", 9.999999e+99L);
#endif #endif
/* The actual output of the following directive depends on the rounding /* The actual output of the following directive depends on the rounding
mode. */ mode. */
/* EQL (12, "%Le", 9.9999994e+99L); */ /* RNG (12, "%Le", 9.9999994e+99L); */
EQL (12, 13, "%Le", 1.0e-1L); RNG (12, 17, 18, "%Le", 1.0e-1L);
EQL (12, 13, "%Le", 1.0e-12L); RNG (12, 17, 18, "%Le", 1.0e-12L);
EQL (13, 14, "%Le", 1.0e-123L); RNG (13, 18, 19, "%Le", 1.0e-123L);
EQL ( 6, 7, "%.0Le", 1.0e-111L); EQL ( 6, 7, "%.0Le", 1.0e-111L);
EQL ( 8, 9, "%.1Le", 1.0e-111L);
EQL (19, 20, "%.12Le", 1.0e-112L); RNG ( 8, 13, 14, "%.1Le", 1.0e-111L);
EQL (20, 21, "%.13Le", 1.0e-113L); RNG (19, 25, 25, "%.12Le", 1.0e-112L);
RNG (20, 26, 27, "%.13Le", 1.0e-113L);
/* The following correspond to the double results plus 1 for the upper /* The following correspond to the double results plus 1 for the upper
bound accounting for the four-digit exponent. */ bound accounting for the four-digit exponent. */
RNG (12, 15, 16, "%Le", d); /* 0.000000e+00 ... -1.189732e+4932 */ RNG (12, 20, 21, "%Le", d); /* 0.000000e+00 ... -1.189732e+4932 */
RNG ( 5, 8, 9, "%.Le", d); RNG ( 5, 8, 9, "%.Le", d);
RNG ( 5, 9, 10, "%.0Le", d); RNG ( 5, 9, 10, "%.0Le", d);
RNG ( 7, 10, 11, "%.1Le", d); /* 0.0e+00 ... -1.2e+4932 */ RNG ( 7, 15, 16, "%.1Le", d); /* 0.0e+00 ... -1.2e+4932 */
RNG ( 8, 11, 12, "%.2Le", d); /* 0.00e+00 ... -1.19e+4932 */ RNG ( 8, 16, 17, "%.2Le", d); /* 0.00e+00 ... -1.19e+4932 */
RNG ( 9, 12, 13, "%.3Le", d); RNG ( 9, 17, 18, "%.3Le", d);
RNG (10, 13, 14, "%.4Le", d); RNG (10, 18, 19, "%.4Le", d);
RNG (11, 14, 15, "%.5Le", d); RNG (11, 19, 20, "%.5Le", d);
RNG (12, 15, 16, "%.6Le", d); /* same as plain "%Le" */ RNG (12, 20, 21, "%.6Le", d); /* same as plain "%Le" */
RNG (13, 16, 17, "%.7Le", d); /* 0.0000000e+00 ... -1.1897315e+4932 */ RNG (13, 21, 22, "%.7Le", d); /* 0.0000000e+00 ... -1.1897315e+4932 */
RNG ( 5, 9, 10, "%.*Le", 0, d); RNG ( 5, 9, 10, "%.*Le", 0, d);
RNG ( 7, 10, 11, "%.*Le", 1, d); RNG ( 7, 15, 16, "%.*Le", 1, d);
RNG ( 8, 11, 12, "%.*Le", 2, d); RNG ( 8, 16, 17, "%.*Le", 2, d);
RNG ( 9, 12, 13, "%.*Le", 3, d); RNG ( 9, 17, 18, "%.*Le", 3, d);
RNG (10, 13, 14, "%.*Le", 4, d); RNG (10, 18, 19, "%.*Le", 4, d);
RNG (11, 14, 15, "%.*Le", 5, d); RNG (11, 19, 20, "%.*Le", 5, d);
RNG (12, 15, 16, "%.*Le", 6, d); RNG (12, 20, 21, "%.*Le", 6, d);
RNG (13, 16, 17, "%.*Le", 7, d); RNG (13, 21, 22, "%.*Le", 7, d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_f_double (double d) test_f_double (double d)
{ {
EQL ( 8, 9, "%f", 0.0e0); RNG ( 8, 13, 14, "%f", 0.0e0);
EQL ( 8, 9, "%f", 0.1e0); RNG ( 8, 13, 14, "%f", 0.1e0);
EQL ( 8, 9, "%f", 0.12e0); RNG ( 8, 13, 14, "%f", 0.12e0);
EQL ( 8, 9, "%f", 0.123e0); RNG ( 8, 13, 14, "%f", 0.123e0);
EQL ( 8, 9, "%f", 0.1234e0); RNG ( 8, 13, 14, "%f", 0.1234e0);
EQL ( 8, 9, "%f", 0.12345e0); RNG ( 8, 13, 14, "%f", 0.12345e0);
EQL ( 8, 9, "%f", 0.123456e0); RNG ( 8, 13, 14, "%f", 0.123456e0);
EQL ( 8, 9, "%f", 1.234567e0); RNG ( 8, 13, 14, "%f", 1.234567e0);
EQL ( 9, 10, "%f", 1.0e+1); RNG ( 9, 14, 15, "%f", 1.0e+1);
EQL ( 20, 21, "%f", 1.0e+12); RNG ( 20, 26, 27, "%f", 1.0e+12);
EQL (130, 131, "%f", 1.0e+123); RNG (130, 136, 137, "%f", 1.0e+123);
EQL ( 8, 9, "%f", 1.0e-1); RNG ( 8, 13, 14, "%f", 1.0e-1);
EQL ( 8, 9, "%f", 1.0e-12); RNG ( 8, 13, 14, "%f", 1.0e-12);
EQL ( 8, 9, "%f", 1.0e-123); RNG ( 8, 13, 14, "%f", 1.0e-123);
RNG ( 8, 317, 318, "%f", d); RNG ( 8, 322, 323, "%f", d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_f_long_double (void) test_f_long_double (void)
{ {
EQL ( 8, 9, "%Lf", 0.0e0L); RNG ( 8, 15, 16, "%Lf", 0.0e0L);
EQL ( 8, 9, "%Lf", 0.1e0L); RNG ( 8, 14, 15, "%Lf", 0.1e0L);
EQL ( 8, 9, "%Lf", 0.12e0L); RNG ( 8, 14, 15, "%Lf", 0.12e0L);
EQL ( 8, 9, "%Lf", 0.123e0L); RNG ( 8, 14, 15, "%Lf", 0.123e0L);
EQL ( 8, 9, "%Lf", 0.1234e0L); RNG ( 8, 14, 15, "%Lf", 0.1234e0L);
EQL ( 8, 9, "%Lf", 0.12345e0L); RNG ( 8, 14, 15, "%Lf", 0.12345e0L);
EQL ( 8, 9, "%Lf", 0.123456e0L); RNG ( 8, 14, 15, "%Lf", 0.123456e0L);
EQL ( 8, 9, "%Lf", 1.234567e0L); RNG ( 8, 14, 15, "%Lf", 1.234567e0L);
EQL ( 9, 10, "%Lf", 1.0e+1L); RNG ( 9, 15, 16, "%Lf", 1.0e+1L);
EQL ( 20, 21, "%Lf", 1.0e+12L); RNG ( 20, 26, 27, "%Lf", 1.0e+12L);
EQL (130, 131, "%Lf", 1.0e+123L); RNG (130, 136, 137, "%Lf", 1.0e+123L);
EQL ( 8, 9, "%Lf", 1.0e-1L); RNG ( 8, 14, 15, "%Lf", 1.0e-1L);
EQL ( 8, 9, "%Lf", 1.0e-12L); RNG ( 8, 14, 15, "%Lf", 1.0e-12L);
EQL ( 8, 9, "%Lf", 1.0e-123L); RNG ( 8, 14, 15, "%Lf", 1.0e-123L);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
...@@ -652,52 +655,54 @@ test_g_double (double d) ...@@ -652,52 +655,54 @@ test_g_double (double d)
{ {
/* Numbers exactly representable in binary floating point. */ /* Numbers exactly representable in binary floating point. */
EQL ( 1, 2, "%g", 0.0); EQL ( 1, 2, "%g", 0.0);
EQL ( 3, 4, "%g", 1.0 / 2);
EQL ( 4, 5, "%g", 1.0 / 4); RNG ( 3, 8, 9, "%g", 1.0 / 2);
EQL ( 5, 6, "%g", 1.0 / 8); RNG ( 4, 9, 10, "%g", 1.0 / 4);
EQL ( 6, 7, "%g", 1.0 / 16); RNG ( 5, 10, 11, "%g", 1.0 / 8);
EQL ( 7, 8, "%g", 1.0 / 32); RNG ( 6, 11, 12, "%g", 1.0 / 16);
EQL ( 8, 9, "%g", 1.0 / 64); RNG ( 7, 12, 13, "%g", 1.0 / 32);
EQL ( 9, 10, "%g", 1.0 / 128); RNG ( 8, 13, 14, "%g", 1.0 / 64);
EQL ( 10, 11, "%g", 1.0 / 256); RNG ( 9, 14, 15, "%g", 1.0 / 128);
EQL ( 10, 11, "%g", 1.0 / 512); RNG ( 10, 15, 16, "%g", 1.0 / 256);
RNG ( 10, 16, 17, "%g", 1.0 / 512);
/* Numbers that are not exactly representable. */ /* Numbers that are not exactly representable. */
RNG ( 3, 8, 9, "%g", 0.1); RNG ( 3, 13, 14, "%g", 0.1);
RNG ( 4, 8, 9, "%g", 0.12); RNG ( 4, 13, 14, "%g", 0.12);
RNG ( 5, 8, 9, "%g", 0.123); RNG ( 5, 13, 14, "%g", 0.123);
RNG ( 6, 8, 9, "%g", 0.1234); RNG ( 6, 13, 14, "%g", 0.1234);
RNG ( 7, 8, 9, "%g", 0.12345); RNG ( 7, 13, 14, "%g", 0.12345);
RNG ( 8, 8, 9, "%g", 0.123456); RNG ( 8, 13, 14, "%g", 0.123456);
RNG ( 4, 7, 8, "%g", 0.123e+1); RNG ( 4, 17, 18, "%g", 0.123e+1);
EQL ( 8, 9, "%g", 0.123e+12); RNG ( 8, 18, 19, "%g", 0.123e+12);
RNG ( 9, 12, 13, "%g", 0.123e+134); RNG ( 9, 19, 20, "%g", 0.123e+134);
RNG ( 1, 13, 14, "%g", d); RNG ( 1, 18, 19, "%g", d);
RNG ( 1, 7, 8, "%.g", d); RNG ( 1, 12, 13, "%.g", d);
RNG ( 1, 7, 8, "%.0g", d); RNG ( 1, 12, 13, "%.0g", d);
RNG ( 1, 7, 8, "%.1g", d); RNG ( 1, 12, 13, "%.1g", d);
RNG ( 1, 9, 10, "%.2g", d); RNG ( 1, 14, 15, "%.2g", d);
RNG ( 1, 10, 11, "%.3g", d); RNG ( 1, 15, 16, "%.3g", d);
RNG ( 1, 11, 12, "%.4g", d); RNG ( 1, 16, 17, "%.4g", d);
RNG ( 1, 12, 13, "%.5g", d); RNG ( 1, 17, 18, "%.5g", d);
RNG ( 1, 13, 14, "%.6g", d); RNG ( 1, 18, 19, "%.6g", d);
RNG ( 1, 14, 15, "%.7g", d); RNG ( 1, 19, 20, "%.7g", d);
RNG ( 1, 15, 16, "%.8g", d); RNG ( 1, 20, 21, "%.8g", d);
RNG ( 1,310,311, "%.9999g", d); RNG ( 1, 315, 316, "%.9999g", d);
RNG ( 1, 7, 8, "%.*g", 0, d); RNG ( 1, 12, 13, "%.*g", 0, d);
RNG ( 1, 7, 8, "%.*g", 1, d); RNG ( 1, 12, 13, "%.*g", 1, d);
RNG ( 1, 9, 10, "%.*g", 2, d); RNG ( 1, 14, 15, "%.*g", 2, d);
RNG ( 1, 10, 11, "%.*g", 3, d); RNG ( 1, 15, 16, "%.*g", 3, d);
RNG ( 1, 11, 12, "%.*g", 4, d); RNG ( 1, 16, 17, "%.*g", 4, d);
RNG ( 1, 12, 13, "%.*g", 5, d); RNG ( 1, 17, 18, "%.*g", 5, d);
RNG ( 1, 13, 14, "%.*g", 6, d); RNG ( 1, 18, 19, "%.*g", 6, d);
RNG ( 1, 14, 15, "%.*g", 7, d); RNG ( 1, 19, 20, "%.*g", 7, d);
RNG ( 1, 15, 16, "%.*g", 8, d); RNG ( 1, 20, 21, "%.*g", 8, d);
RNG ( 1,310,311, "%.*g", 9999, d);
RNG ( 1, 315, 316, "%.*g", 9999, d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
...@@ -705,36 +710,27 @@ test_g_long_double (void) ...@@ -705,36 +710,27 @@ test_g_long_double (void)
{ {
/* Numbers exactly representable in binary floating point. */ /* Numbers exactly representable in binary floating point. */
EQL ( 1, 2, "%Lg", 0.0L); EQL ( 1, 2, "%Lg", 0.0L);
EQL ( 3, 4, "%Lg", 1.0L / 2); RNG ( 3, 8, 9, "%Lg", 1.0L / 2);
EQL ( 4, 5, "%Lg", 1.0L / 4); RNG ( 4, 9, 10, "%Lg", 1.0L / 4);
EQL ( 5, 6, "%Lg", 1.0L / 8); RNG ( 5, 10, 11, "%Lg", 1.0L / 8);
EQL ( 6, 7, "%Lg", 1.0L / 16); RNG ( 6, 11, 12, "%Lg", 1.0L / 16);
EQL ( 7, 8, "%Lg", 1.0L / 32); RNG ( 7, 12, 13, "%Lg", 1.0L / 32);
EQL ( 8, 9, "%Lg", 1.0L / 64); RNG ( 8, 13, 14, "%Lg", 1.0L / 64);
EQL ( 9, 10, "%Lg", 1.0L / 128); RNG ( 9, 14, 15, "%Lg", 1.0L / 128);
EQL ( 10, 11, "%Lg", 1.0L / 256); RNG ( 10, 15, 16, "%Lg", 1.0L / 256);
EQL ( 10, 11, "%Lg", 1.0L / 512); RNG ( 10, 15, 16, "%Lg", 1.0L / 512);
/* Numbers that are not exactly representable. */ /* Numbers that are not exactly representable. */
#if __LDBL_DIG__ < 31 RNG ( 3, 13, 14, "%Lg", 0.1L);
/* x86_64, for example, represents 0.1 as 1.000000...1...e-1 RNG ( 4, 13, 14, "%Lg", 0.12L);
and formats it as either "0.1" (when rounded down) or "0.100001" RNG ( 5, 13, 14, "%Lg", 0.123L);
(rounded up). */ RNG ( 6, 13, 14, "%Lg", 0.1234L);
RNG ( 3, 8, 9, "%Lg", 0.1L); RNG ( 7, 13, 14, "%Lg", 0.12345L);
#else RNG ( 8, 13, 14, "%Lg", 0.123456L);
/* powerpc64 represents 0.1 as 9.999999...6e-2 and formats it
as "0.0999999" (rounded down) or "0.1" (rounded up). */ RNG ( 4, 12, 13, "%Lg", 0.123e+1L);
RNG ( 3, 9, 10, "%Lg", 0.1L); RNG ( 8, 13, 14, "%Lg", 0.123e+12L);
#endif RNG ( 9, 17, 18, "%Lg", 0.123e+134L);
RNG ( 4, 8, 9, "%Lg", 0.12L);
RNG ( 5, 8, 9, "%Lg", 0.123L);
RNG ( 6, 8, 9, "%Lg", 0.1234L);
RNG ( 7, 8, 9, "%Lg", 0.12345L);
RNG ( 8, 8, 9, "%Lg", 0.123456L);
RNG ( 4, 7, 8, "%Lg", 0.123e+1L);
EQL ( 8, 9, "%Lg", 0.123e+12L);
RNG ( 9, 12, 13, "%Lg", 0.123e+134L);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment