PR middle-end/79692 - [7 Regression] -Wformat-overflow false positive

gcc/ChangeLog:

	PR middle-end/79692
	* gimple-ssa-sprintf.c
	(directive::known_width_and_precision): New function.
	(format_integer): Use it.
	(get_mpfr_format_length): Consider the full range of precision
	when computing %g output with the # flag.  Set the likely byte
	count to 3 rather than 1 when precision is indeterminate.
	(format_floating): Correct the lower bound of precision.

gcc/testsuite/ChangeLog:

	PR middle-end/79692
	* gcc.dg/tree-ssa/builtin-sprintf-2.c: Add test cases.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: Correct %#g.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-3.c: Ditto.

From-SVN: r245822
This commit is contained in:
Martin Sebor 2017-03-01 23:39:59 +00:00 committed by Martin Sebor
parent 538d7dba84
commit 71dedb336f
7 changed files with 441 additions and 38 deletions

View File

@ -1,3 +1,14 @@
2017-03-01 Martin Sebor <msebor@redhat.com>
PR middle-end/79692
* gimple-ssa-sprintf.c
(directive::known_width_and_precision): New function.
(format_integer): Use it.
(get_mpfr_format_length): Consider the full range of precision
when computing %g output with the # flag. Set the likely byte
count to 3 rather than 1 when precision is indeterminate.
(format_floating): Correct the lower bound of precision.
2017-03-01 Bill Schmidt <wschmidt@linux.vnet.ibm.com>
* doc/invoke.texi: Document default code model for 64-bit Linux.

View File

@ -692,6 +692,16 @@ struct directive
{
get_int_range (arg, integer_type_node, prec, prec + 1, false, -1);
}
/* Return true if both width and precision are known to be
either constant or in some range, false otherwise. */
bool known_width_and_precision () const
{
return ((width[1] < 0
|| (unsigned HOST_WIDE_INT)width[1] <= target_int_max ())
&& (prec[1] < 0
|| (unsigned HOST_WIDE_INT)prec[1] < target_int_max ()));
}
};
/* Return the logarithm of X in BASE. */
@ -1180,10 +1190,10 @@ format_integer (const directive &dir, tree arg)
/* As a special case, a precision of zero with a zero argument
results in zero bytes except in base 8 when the '#' flag is
specified, and for signed conversions in base 8 and 10 when
flags when either the space or '+' flag has been specified
when it results in just one byte (with width having the normal
effect). This must extend to the case of a specified precision
with an unknown value because it can be zero. */
either the space or '+' flag has been specified and it results
in just one byte (with width having the normal effect). This
must extend to the case of a specified precision with
an unknown value because it can be zero. */
res.range.min = ((base == 8 && dir.get_flag ('#')) || maybesign);
if (res.range.min == 0 && dir.prec[0] != dir.prec[1])
{
@ -1254,10 +1264,12 @@ format_integer (const directive &dir, tree arg)
argmax = wide_int_to_tree (argtype, max);
/* Set KNOWNRANGE if the argument is in a known subrange
of the directive's type (KNOWNRANGE may be reset below). */
of the directive's type and neither width nor precision
is unknown. (KNOWNRANGE may be reset below). */
res.knownrange
= (!tree_int_cst_equal (TYPE_MIN_VALUE (dirtype), argmin)
|| !tree_int_cst_equal (TYPE_MAX_VALUE (dirtype), argmax));
= ((!tree_int_cst_equal (TYPE_MIN_VALUE (dirtype), argmin)
|| !tree_int_cst_equal (TYPE_MAX_VALUE (dirtype), argmax))
&& dir.known_width_and_precision ());
res.argmin = argmin;
res.argmax = argmax;
@ -1421,12 +1433,12 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec,
HOST_WIDE_INT p = prec;
if (spec == 'G')
if (spec == 'G' && !strchr (flags, '#'))
{
/* For G/g, precision gives the maximum number of significant
digits which is bounded by LDBL_MAX_10_EXP, or, for a 128
bit IEEE extended precision, 4932. Using twice as much
here should be more than sufficient for any real format. */
/* For G/g without the pound flag, precision gives the maximum number
of significant digits which is bounded by LDBL_MAX_10_EXP, or, for
a 128 bit IEEE extended precision, 4932. Using twice as much here
should be more than sufficient for any real format. */
if ((IEEE_MAX_10_EXP * 2) < prec)
prec = IEEE_MAX_10_EXP * 2;
p = prec;
@ -1609,7 +1621,12 @@ format_floating (const directive &dir)
/* Compute the upper bound for -TYPE_MAX. */
res.range.max = format_floating_max (type, 'f', dir.prec[1]);
res.range.likely = res.range.min;
/* The minimum output with unknown precision is a single byte
(e.g., "0") but the more likely output is 3 bytes ("0.0"). */
if (dir.prec[0] < 0 && dir.prec[1] > 0)
res.range.likely = 3;
else
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
@ -1625,10 +1642,43 @@ format_floating (const directive &dir)
/* The %g output depends on precision and the exponent of
the argument. Since the value of the argument isn't known
the lower bound on the range of bytes (not counting flags
or width) is 1. */
res.range.min = flagmin;
res.range.max = format_floating_max (type, 'g', dir.prec[1]);
res.range.likely = res.range.max;
or width) is 1 plus radix (i.e., either "0" or "0." for
"%g" and "%#g", respectively, with a zero argument). */
res.range.min = flagmin + radix;
char spec = 'g';
HOST_WIDE_INT maxprec = dir.prec[1];
if (radix && maxprec)
{
/* When the pound flag (radix) is set, trailing zeros aren't
trimmed and so the longest output is the same as for %e,
except with precision minus 1 (as specified in C11). */
spec = 'e';
if (maxprec > 0)
--maxprec;
else if (maxprec < 0)
maxprec = 5;
}
res.range.max = format_floating_max (type, spec, maxprec);
/* The likely output is either the maximum computed above
minus 1 (assuming the maximum is positive) when precision
is known (or unspecified), or the same minimum as for %e
(which is computed for a non-negative argument). Unlike
for the other specifiers above the likely output isn't
the minimum because for %g that's 1 which is unlikely. */
if (dir.prec[1] < 0
|| (unsigned HOST_WIDE_INT)dir.prec[1] < target_int_max ())
res.range.likely = res.range.max - 1;
else
{
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
res.range.likely = (flagmin
+ radix
+ minprec
+ 2 /* e+ */ + 2);
}
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
@ -1657,24 +1707,63 @@ format_floating (const directive &dir, tree arg)
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
/* For an indeterminate precision the lower bound must be assumed
to be zero. */
if (TOUPPER (dir.specifier) == 'A')
{
/* Get the number of fractional decimal digits needed to represent
the argument without a loss of accuracy. */
tree type = arg ? TREE_TYPE (arg) :
(dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll
? long_double_type_node : double_type_node);
unsigned fmtprec
= REAL_MODE_FORMAT (TYPE_MODE (type))->p;
/* The precision of the IEEE 754 double format is 53.
The precision of all other GCC binary double formats
is 56 or less. */
unsigned maxprec = fmtprec <= 56 ? 13 : 15;
/* For %a, leave the minimum precision unspecified to let
MFPR trim trailing zeros (as it and many other systems
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)
if (dir.prec[1] < 0)
{
unsigned fmtprec
= REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->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;
/* Both bounds are negative implies that precision has
not been specified. */
prec[0] = maxprec;
prec[1] = -1;
}
else if (dir.prec[0] < 0)
{
/* With a negative lower bound and a non-negative upper
bound set the minimum precision to zero and the maximum
to the greater of the maximum precision (i.e., with
trailing zeros present) and the specified upper bound. */
prec[0] = 0;
prec[1] = dir.prec[1] < maxprec ? maxprec : dir.prec[1];
}
}
else if (dir.prec[0] < 0)
{
if (dir.prec[1] < 0)
{
/* A precision in a strictly negative range is ignored and
the default of 6 is used instead. */
prec[0] = prec[1] = 6;
}
else
{
/* For a precision in a partly negative range, the lower bound
must be assumed to be zero and the new upper bound is the
greater of 6 (the default precision used when the specified
precision is negative) and the upper bound of the specified
range. */
prec[0] = 0;
prec[1] = dir.prec[1] < 6 ? 6 : dir.prec[1];
}
}
@ -1734,12 +1823,23 @@ format_floating (const directive &dir, tree arg)
res.range.max = tmp;
}
res.knownrange = true;
/* The range is known unless either width or precision is unknown. */
res.knownrange = dir.known_width_and_precision ();
/* For the same floating point constant, unless width or precision
is unknown, use the longer output as the likely maximum since
with round to nearest either is equally likely. Otheriwse, when
precision is unknown, use the greater of the minimum and 3 as
the likely output (for "0.0" since zero precision is unlikely). */
if (res.knownrange)
res.range.likely = res.range.max;
else if (res.range.min < 3
&& dir.prec[0] < 0
&& (unsigned HOST_WIDE_INT)dir.prec[1] == target_int_max ())
res.range.likely = 3;
else
res.range.likely = res.range.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))

View File

@ -1,3 +1,11 @@
2017-03-01 Martin Sebor <msebor@redhat.com>
PR middle-end/79692
* gcc.dg/tree-ssa/builtin-sprintf-2.c: Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: Correct %#g.
* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: New test.
* gcc.dg/tree-ssa/builtin-snprintf-3.c: Ditto.
2017-03-01 Uros Bizjak <ubizjak@gmail.com>
* gcc.target/i386/invsize-2.c: New test.

View File

@ -0,0 +1,77 @@
/* Verify the lower and upper bounds of floating directives with
precision whose range crosses zero.
{ do-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
static const double x = 1.23456789;
/* All calls to failure_range must be eliminated. */
extern void failure_range (int, int, int);
/* All calls to verify_{lo,hi}_bound must be retained. */
extern void verify_lo_bound (int, int);
extern void verify_hi_bound (int, int);
int test_a (int p)
{
if (p < -1 || 3 < p)
p = -1;
int n = __builtin_snprintf (0, 0, "%.*A", p, x);
if (n < 6 || 25 < n)
failure_range ('A', 6, 25);
if (n == 6) verify_lo_bound ('A', 6);
if (n == 25) verify_hi_bound ('A', 25);
return n;
}
int test_e (int p)
{
if (p < -1 || 3 < p)
p = -1;
int n = __builtin_snprintf (0, 0, "%.*E", p, x);
if (n < 5 || 17 < n)
failure_range ('E', 5, 17);
if (n == 5) verify_lo_bound ('E', 5);
if (n == 17) verify_hi_bound ('E', 17);
return n;
}
int test_f (int p)
{
if (p < -1 || 3 < p)
p = -1;
int n = __builtin_snprintf (0, 0, "%.*F", p, x);
if (n < 1 || 13 < n)
failure_range ('F', 1, 13);
if (n == 1) verify_lo_bound ('F', 1);
if (n == 13) verify_hi_bound ('F', 13);
return n;
}
int test_g (int p)
{
if (p < -1 || 3 < p)
p = -1;
int n = __builtin_snprintf (0, 0, "%.*G", p, x);
if (n < 1 || 12 < n)
failure_range ('G', 1, 12);
if (n == 1) verify_lo_bound ('G', 1);
if (n == 12) verify_hi_bound ('G', 12);
return n;
}
/* { dg-final { scan-tree-dump-times "snprintf" 4 "optimized"} }
{ dg-final { scan-tree-dump-not "failure_range" "optimized"} }
{ dg-final { scan-tree-dump-times "verify_" 8 "optimized"} } */

View File

@ -7,7 +7,7 @@
The test is compiled with warnings disabled to make sure the absence
of optimizations does not depend on the presence of warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized -ftrack-macro-expansion=0 -w" } */
/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized -w" } */
#ifndef LINE
# define LINE 0
@ -243,6 +243,14 @@ RNG (6, 6, 7, "%La", 0.0L) /* Glibc output: "0x0p+0" */
RNG (6, 6, 7, "%La", ld)
RNG (6, 6, 7, "%.4096La", ld)
/* Verify that the pound flag with unknown precision prevents the %g
directive from trimming trailing zeros as it otherwise does. As
a consequence, the result must be assumed to be as large as
precision. */
RNG (1, 315, 316, "%#.*g", i, d);
RNG (1, 4095, 4096, "%#.*g", i, d);
RNG (1, 4095, 4096, "%#.*g", i, 0.0);
/* Verify that the result of formatting an unknown string isn't optimized
into a non-negative range. The string could be longer that 4,095 bytes,
resulting in the formatting function having undefined behavior (and
@ -282,7 +290,7 @@ RNG (0, 6, 8, "%s%ls", "1", L"2");
/* 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" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } }
{ dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 127 "optimized" { target { ilp32 || lp64 } } } }
{ dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 96 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } }
No unconditional calls to abort should be made:
{ dg-final { scan-tree-dump-not ";\n *must_not_eliminate" "optimized" } } */

View File

@ -239,9 +239,11 @@ void test_g_va (va_list va)
T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */
T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */
T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */
/* The pound flag means the radix character is always present. */
T ("%#g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%#+g"); /* { dg-warning "between 3 and 13 bytes" } */
T ("%# g"); /* { dg-warning "between 3 and 13 bytes" } */
T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */

View File

@ -0,0 +1,197 @@
/* PR middle-end/79692 - -Wformat-overflow false positive on an integer
directive with unknown width
{ dg-do compile }
{ dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" }
{ dg-require-effective-target int32plus } */
typedef __SIZE_TYPE__ size_t;
typedef __WCHAR_TYPE__ wchar_t;
#define INT_MAX __INT_MAX__
#define INT_MIN (-INT_MAX - 1)
/* 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
below make use of LINE to avoid warnings for other lines. */
#ifndef LINE
# define LINE 0
#endif
void sink (char*, char*);
int dummy_sprintf (char*, const char*, ...);
char buffer [1024];
extern char *ptr;
int int_range (int min, int max)
{
extern int int_value (void);
int n = int_value ();
return n < min || max < n ? min : n;
}
unsigned uint_range (unsigned min, unsigned max)
{
extern unsigned uint_value (void);
unsigned n = uint_value ();
return n < min || max < n ? min : n;
}
/* Evaluate to an array of SIZE characters when non-negative, or to
a pointer to an unknown object otherwise. */
#define buffer(size) \
((0 <= size) ? buffer + sizeof buffer - (size) : ptr)
/* 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
argument) issue diagnostics by correctly determining the size of
the destination buffer. */
#define T(size, ...) \
(FUNC (sprintf) (buffer (size), __VA_ARGS__), \
sink (buffer, ptr))
/* Return a signed integer in the range [MIN, MAX]. */
#define R(min, max) int_range (min, max)
void test_unknown_width_integer (int w, int i)
{
T (10, "%*d", w, i);
T (10, "%*d", w, R (0, 12345));
T (10, "%*i", w, i);
T (10, "%*i", w, R (0, 12345));
T (10, "%*o", w, i);
T (10, "%*o", w, R (0, 12345));
T (10, "%*i", w, i);
T (10, "%*i", w, R (0, 12345));
}
void test_unknown_width_floating (int w, double d)
{
T ( 7, "%*a", w, d);
T (21, "%*a", w, 3.141);
T (12, "%*e", w, d); /* { dg-warning "writing a terminating nul" } */
T (12, "%#*e", w, d); /* { dg-warning "writing a terminating nul" } */
T (13, "%*e", w, d);
T (13, "%#*e", w, d);
T (13, "%*e", w, 3.141);
T ( 8, "%*f", w, d); /* { dg-warning "writing a terminating nul" } */
T ( 8, "%#*f", w, d); /* { dg-warning "writing a terminating nul" } */
T ( 9, "%*f", w, d);
T ( 9, "%#*f", w, d);
T ( 9, "%*f", w, 3.141);
T ( 9, "%#*f", w, 3.141);
T (12, "%*g", w, d); /* { dg-warning "may write a terminating nul" } */
T (13, "%*g", w, d);
T (13, "%*g", w, 3.141);
}
void test_unknown_precision_integer (int p, int i, double d)
{
T (10, "%.*d", p, i);
T (10, "%.*d", p, R (0, 12345));
T (10, "%.*i", p, i);
T (10, "%.*i", p, R (0, 12345));
T (10, "%.*o", p, i);
T (10, "%.*o", p, R (0, 12345));
T (10, "%.*i", p, i);
T (10, "%.*i", p, R (0, 12345));
}
void test_unknown_precision_floating (int p, double d)
{
T ( 7, "%.*a", p, d);
T (21, "%.*a", p, 3.141);
/* "%.0e", 0.0 results in 5 bytes: "0e+00" */
T ( 5, "%.*e", p, d); /* { dg-warning "writing a terminating nul" } */
/* "%#.0e", 0.0 results in 6 bytes: "0.e+00" */
T ( 6, "%#.*e", p, d); /* { dg-warning "writing a terminating nul" } */
T ( 6, "%.*e", p, d);
T ( 6, "%.*e", p, 3.141);
T ( 6, "%#.*e", p, 3.141); /* { dg-warning "writing a terminating nul" } */
T ( 7, "%#.*e", p, 3.141);
/* "%.0f", 0.0 results in 1 byte: "0" but precision of at least 1
is likely, resulting in "0.0". */
T ( 3, "%.*f", p, d); /* { dg-warning "may write a terminating nul" } */
/* "%#.0f", 0.0 results in 2 bytes: "0." but precision of at least 1
is likely, resulting in "0.0". */
T ( 3, "%#.*f", p, d); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%.*f", p, d);
T ( 4, "%#.*f", p, d);
T ( 3, "%.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%.*f", p, 3.141);
T ( 3, "%#.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%#.*f", p, 3.141);
T (12, "%.*g", p, d); /* { dg-warning "may write a terminating nul" } */
T (12, "%#.*g", p, d); /* { dg-warning "may write a terminating nul" } */
T (13, "%.*g", p, d);
T (13, "%#.*g", p, d);
T ( 6, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */
T ( 7, "%#.*g", R (-1, 0), d);
T ( 6, "%#.*g", R ( 0, 0), d);/* { dg-warning "may write a terminating nul" } */
T ( 7, "%#.*g", R ( 0, 0), d);
T ( 6, "%#.*g", R ( 0, 1), d);/* { dg-warning "may write a terminating nul" } */
T ( 7, "%#.*g", R ( 0, 1), d);
T ( 3, "%.*g", p, 3.141); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%.*g", p, 3.141);
T ( 3, "%#.*g", p, 3.141); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%#.*g", p, 3.141);
}
void test_unknown_width_and_precision_integer (int w, int p, int i)
{
T (10, "%*.*d", w, p, i);
T (10, "%*.*d", w, p, R (0, 12345));
T (10, "%*.*i", w, p, i);
T (10, "%*.*i", w, p, R (0, 12345));
T (10, "%*.*o", w, p, i);
T (10, "%*.*o", w, p, R (0, 12345));
T (10, "%*.*i", w, p, i);
T (10, "%*.*i", w, p, R (0, 12345));
}
void test_unknown_width_and_precision_floating (int w, int p, double d)
{
T ( 7, "%*.*a", w, p, d);
T (21, "%*.*a", w, p, 3.141);
/* "%0.0e", 0.0 results in 5 bytes: "0e+00" */
T ( 5, "%*.*e", w, p, d); /* { dg-warning "writing a terminating nul" } */
/* "%#0.0e", 0.0 results in 6 bytes: "0.e+00" */
T ( 6, "%#*.*e", w, p, d); /* { dg-warning "writing a terminating nul" } */
T ( 6, "%*.*e", w, p, d);
T ( 6, "%*.*e", w, p, 3.141);
T ( 6, "%#*.*e", w, p, 3.141);/* { dg-warning "writing a terminating nul" } */
T ( 7, "%#*.*e", w, p, 3.141);
T ( 3, "%*.*f", w, p, d); /* { dg-warning "may write a terminating nul" } */
T ( 3, "%#*.*f", w, p, d); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%*.*f", w, p, d);
T ( 4, "%*.*f", w, p, 3.141);
T ( 4, "%#*.*f", w, p, 3.141);
T (13, "%*.*g", w, p, d);
T (13, "%#*.*g", w, p, d);
T (13, "%*.*g", w, p, 3.141);
T (13, "%#*.*g", w, p, 3.141);
}