aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-ssa-sprintf.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2017-02-03 02:18:59 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2017-02-02 19:18:59 -0700
commitebee1eb978f5168267f8ee9dc530f620d5639943 (patch)
tree8d6cf5d91b0b93aaba4c736c8852ac2813fa9fe1 /gcc/gimple-ssa-sprintf.c
parent92d8bb06dc66c41c1b27906e680b79b12b2f51df (diff)
downloadgcc-ebee1eb978f5168267f8ee9dc530f620d5639943.zip
gcc-ebee1eb978f5168267f8ee9dc530f620d5639943.tar.gz
gcc-ebee1eb978f5168267f8ee9dc530f620d5639943.tar.bz2
PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX in glibc sysdeps/posix/tempname.c
gcc/testsuite/ChangeLog: PR middle-end/79275 * gcc.dg/tree-ssa/builtin-sprintf-warn-11.c: New test. * gcc.dg/tree-ssa/pr79275.c: New test. gcc/ChangeLog: PR middle-end/79275 * gimple-ssa-sprintf.c (get_string_length): Set lower bound to zero. (format_string): Tighten up the range of output for non-constant strings and correct the expected range for wide non-constant strings. From-SVN: r245142
Diffstat (limited to 'gcc/gimple-ssa-sprintf.c')
-rw-r--r--gcc/gimple-ssa-sprintf.c91
1 files changed, 69 insertions, 22 deletions
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 11f4174..9e099f0 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -1832,10 +1832,11 @@ get_string_length (tree str)
}
else
{
- /* When the upper bound is unknown (as assumed to be excessive)
+ /* When the upper bound is unknown (it can be zero or excessive)
set the likely length to the greater of 1 and the length of
- the shortest string. */
+ the shortest string and reset the lower bound to zero. */
res.range.likely = res.range.min ? res.range.min : warn_level > 1;
+ res.range.min = 0;
}
res.range.unlikely = res.range.max;
@@ -1986,43 +1987,89 @@ format_string (const directive &dir, tree arg)
}
else
{
- /* For a '%s' and '%ls' directive with a non-constant string,
- the minimum number of characters is the greater of WIDTH
- and either 0 in mode 1 or the smaller of PRECISION and 1
- in mode 2, and the maximum is PRECISION or -1 to disable
- tracking. */
+ /* For a '%s' and '%ls' directive with a non-constant string (either
+ one of a number of strings of known length or an unknown string)
+ the minimum number of characters is lesser of PRECISION[0] and
+ the length of the shortest known string or zero, and the maximum
+ is the lessser of the length of the longest known string or
+ PTRDIFF_MAX and PRECISION[1]. The likely length is either
+ the minimum at level 1 and the greater of the minimum and 1
+ at level 2. This result is adjust upward for width (if it's
+ specified). */
+
+ if (dir.modifier == FMT_LEN_l)
+ {
+ /* A wide character converts to as few as zero bytes. */
+ slen.range.min = 0;
+ if (slen.range.max < target_int_max ())
+ slen.range.max *= target_mb_len_max ();
+
+ if (slen.range.likely < target_int_max ())
+ slen.range.likely *= 2;
+
+ if (slen.range.likely < target_int_max ())
+ slen.range.unlikely *= target_mb_len_max ();
+ }
+
+ res.range = slen.range;
if (dir.prec[0] >= 0)
{
+ /* Adjust the minimum to zero if the string length is unknown,
+ or at most the lower bound of the precision otherwise. */
if (slen.range.min >= target_int_max ())
- slen.range.min = 0;
+ res.range.min = 0;
else if ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.min)
- {
- slen.range.min = dir.prec[0];
- slen.range.likely = slen.range.min;
- }
+ res.range.min = dir.prec[0];
+ /* Make both maxima no greater than the upper bound of precision. */
if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max
|| slen.range.max >= target_int_max ())
{
- slen.range.max = dir.prec[1];
- slen.range.likely = slen.range.max;
+ res.range.max = dir.prec[1];
+ res.range.unlikely = dir.prec[1];
}
+
+ /* If precision is constant, set the likely counter to the lesser
+ of it and the maximum string length. Otherwise, if the lower
+ bound of precision is greater than zero, set the likely counter
+ to the minimum. Otherwise set it to zero or one based on
+ the warning level. */
+ if (dir.prec[0] == dir.prec[1])
+ res.range.likely
+ = ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.max
+ ? dir.prec[0] : slen.range.max);
+ else if (dir.prec[0] > 0)
+ res.range.likely = res.range.min;
+ else
+ res.range.likely = warn_level > 1;
+ }
+ else if (dir.prec[1] >= 0)
+ {
+ res.range.min = 0;
+ if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
+ res.range.max = dir.prec[1];
+ res.range.likely = dir.prec[1] ? warn_level > 1 : 0;
}
else if (slen.range.min >= target_int_max ())
{
- slen.range.min = 0;
- slen.range.max = HOST_WIDE_INT_MAX;
- /* At level one strings of unknown length are assumed to be
+ res.range.min = 0;
+ res.range.max = HOST_WIDE_INT_MAX;
+ /* At level 1 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.likely = warn_level > 1;
+ }
+ else
+ {
+ /* A string of unknown length unconstrained by precision is
+ assumed to be empty at level 1 and just one character long
+ at higher levels. */
+ if (res.range.likely >= target_int_max ())
+ res.range.likely = warn_level > 1;
}
- slen.range.unlikely = slen.range.max;
-
- res.range = slen.range;
- res.knownrange = slen.knownrange;
+ res.range.unlikely = res.range.max;
}
/* Bump up the byte counters if WIDTH is greater. */