PR middle-end/78521 - [7 Regression] incorrect byte count in -Wformat-length...
PR middle-end/78521 - [7 Regression] incorrect byte count in -Wformat-length warning with non-constant width or precision PR middle-end/78520 - missing warning for snprintf with size greater than INT_MAX gcc/ChangeLog: PR middle-end/78520 * gimple-ssa-sprintf.c (target_max_value): Remove. (target_int_max, target_size_max): Use TYPE_MAX_VALUE. (get_width_and_precision): New function. (format_integer, format_floating, get_string_length, format_string): Correct handling of width and precision with unknown value. (format_directive): Add warning. (pass_sprintf_length::compute_format_length): Allow for precision to consist of a sole period with no asterisk or digits after it. gcc/testsuite/ChangeLog: PR middle-end/78520 * gcc.dg/tree-ssa/builtin-sprintf-5.c: Add test cases. * gcc.dg/tree-ssa/builtin-sprintf-6.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Add test cases. * gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Add test cases. From-SVN: r242935
This commit is contained in:
parent
b3a5bff4d7
commit
de6aa93370
@ -1,3 +1,15 @@
|
||||
2016-11-28 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/78520
|
||||
* gimple-ssa-sprintf.c (target_max_value): Remove.
|
||||
(target_int_max, target_size_max): Use TYPE_MAX_VALUE.
|
||||
(get_width_and_precision): New function.
|
||||
(format_integer, format_floating, get_string_length, format_string):
|
||||
Correct handling of width and precision with unknown value.
|
||||
(format_directive): Add warning.
|
||||
(pass_sprintf_length::compute_format_length): Allow for precision
|
||||
to consist of a sole period with no asterisk or digits after it.
|
||||
|
||||
2016-11-28 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR rtl-optimization/78546
|
||||
|
@ -226,24 +226,10 @@ struct format_result
|
||||
|
||||
/* Return the value of INT_MIN for the target. */
|
||||
|
||||
static HOST_WIDE_INT
|
||||
static inline HOST_WIDE_INT
|
||||
target_int_min ()
|
||||
{
|
||||
const unsigned HOST_WIDE_INT int_min
|
||||
= HOST_WIDE_INT_M1U << (TYPE_PRECISION (integer_type_node) - 1);
|
||||
|
||||
return int_min;
|
||||
}
|
||||
|
||||
/* Return the largest value for TYPE on the target. */
|
||||
|
||||
static unsigned HOST_WIDE_INT
|
||||
target_max_value (tree type)
|
||||
{
|
||||
const unsigned HOST_WIDE_INT max_value
|
||||
= HOST_WIDE_INT_M1U >> (HOST_BITS_PER_WIDE_INT
|
||||
- TYPE_PRECISION (type) + 1);
|
||||
return max_value;
|
||||
return tree_to_shwi (TYPE_MIN_VALUE (integer_type_node));
|
||||
}
|
||||
|
||||
/* Return the value of INT_MAX for the target. */
|
||||
@ -251,7 +237,7 @@ target_max_value (tree type)
|
||||
static inline unsigned HOST_WIDE_INT
|
||||
target_int_max ()
|
||||
{
|
||||
return target_max_value (integer_type_node);
|
||||
return tree_to_uhwi (TYPE_MAX_VALUE (integer_type_node));
|
||||
}
|
||||
|
||||
/* Return the value of SIZE_MAX for the target. */
|
||||
@ -259,7 +245,7 @@ target_int_max ()
|
||||
static inline unsigned HOST_WIDE_INT
|
||||
target_size_max ()
|
||||
{
|
||||
return target_max_value (size_type_node);
|
||||
return tree_to_uhwi (TYPE_MAX_VALUE (size_type_node));
|
||||
}
|
||||
|
||||
/* Return the constant initial value of DECL if available or DECL
|
||||
@ -845,6 +831,43 @@ format_pointer (const conversion_spec &spec, tree arg)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Set *PWIDTH and *PPREC according to the width and precision specified
|
||||
in SPEC. Each is set to HOST_WIDE_INT_MIN when the corresponding
|
||||
field is specified but unknown, to zero for width and -1 for precision,
|
||||
respectively when it's not specified, or to a non-negative value
|
||||
corresponding to the known value. */
|
||||
static void
|
||||
get_width_and_precision (const conversion_spec &spec,
|
||||
HOST_WIDE_INT *pwidth, HOST_WIDE_INT *pprec)
|
||||
{
|
||||
HOST_WIDE_INT width = spec.have_width ? spec.width : 0;
|
||||
HOST_WIDE_INT prec = spec.have_precision ? spec.precision : -1;
|
||||
|
||||
if (spec.star_width)
|
||||
{
|
||||
if (TREE_CODE (spec.star_width) == INTEGER_CST)
|
||||
width = abs (tree_to_shwi (spec.star_width));
|
||||
else
|
||||
width = HOST_WIDE_INT_MIN;
|
||||
}
|
||||
|
||||
if (spec.star_precision)
|
||||
{
|
||||
if (TREE_CODE (spec.star_precision) == INTEGER_CST)
|
||||
{
|
||||
prec = tree_to_shwi (spec.star_precision);
|
||||
if (prec < 0)
|
||||
prec = 0;
|
||||
}
|
||||
else
|
||||
prec = HOST_WIDE_INT_MIN;
|
||||
}
|
||||
|
||||
*pwidth = width;
|
||||
*pprec = prec;
|
||||
}
|
||||
|
||||
|
||||
/* Return a range representing the minimum and maximum number of bytes
|
||||
that the conversion specification SPEC will write on output for the
|
||||
integer argument ARG when non-null. ARG may be null (for vararg
|
||||
@ -855,18 +878,11 @@ format_integer (const conversion_spec &spec, tree arg)
|
||||
{
|
||||
tree intmax_type_node;
|
||||
tree uintmax_type_node;
|
||||
/* Set WIDTH and PRECISION to either the values in the format
|
||||
specification or to zero. */
|
||||
int width = spec.have_width ? spec.width : 0;
|
||||
int prec = spec.have_precision ? spec.precision : 0;
|
||||
|
||||
if (spec.star_width)
|
||||
width = (TREE_CODE (spec.star_width) == INTEGER_CST
|
||||
? tree_to_shwi (spec.star_width) : 0);
|
||||
|
||||
if (spec.star_precision)
|
||||
prec = (TREE_CODE (spec.star_precision) == INTEGER_CST
|
||||
? tree_to_shwi (spec.star_precision) : 0);
|
||||
/* Set WIDTH and PRECISION based on the specification. */
|
||||
HOST_WIDE_INT width;
|
||||
HOST_WIDE_INT prec;
|
||||
get_width_and_precision (spec, &width, &prec);
|
||||
|
||||
bool sign = spec.specifier == 'd' || spec.specifier == 'i';
|
||||
|
||||
@ -936,15 +952,8 @@ format_integer (const conversion_spec &spec, tree arg)
|
||||
}
|
||||
else if (TREE_CODE (arg) == INTEGER_CST)
|
||||
{
|
||||
/* The minimum and maximum number of bytes produced by
|
||||
the directive. */
|
||||
fmtresult res;
|
||||
|
||||
/* When a constant argument has been provided use its value
|
||||
rather than type to determine the length of the output. */
|
||||
res.bounded = true;
|
||||
res.constant = true;
|
||||
res.knownrange = true;
|
||||
|
||||
/* Base to format the number in. */
|
||||
int base;
|
||||
@ -977,25 +986,56 @@ format_integer (const conversion_spec &spec, tree arg)
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Convert the argument to the type of the directive. */
|
||||
arg = fold_convert (dirtype, arg);
|
||||
int len;
|
||||
|
||||
maybesign |= spec.get_flag ('+');
|
||||
if ((prec == HOST_WIDE_INT_MIN || prec == 0) && integer_zerop (arg))
|
||||
{
|
||||
/* As a special case, a precision of zero with an argument
|
||||
of zero results in zero bytes regardless of flags (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. */
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert the argument to the type of the directive. */
|
||||
arg = fold_convert (dirtype, arg);
|
||||
|
||||
/* True when a conversion is preceded by a prefix indicating the base
|
||||
of the argument (octal or hexadecimal). */
|
||||
bool maybebase = spec.get_flag ('#');
|
||||
int len = tree_digits (arg, base, maybesign, maybebase);
|
||||
maybesign |= spec.get_flag ('+');
|
||||
|
||||
if (len < prec)
|
||||
len = prec;
|
||||
/* True when a conversion is preceded by a prefix indicating the base
|
||||
of the argument (octal or hexadecimal). */
|
||||
bool maybebase = spec.get_flag ('#');
|
||||
len = tree_digits (arg, base, maybesign, maybebase);
|
||||
|
||||
if (len < prec)
|
||||
len = prec;
|
||||
}
|
||||
|
||||
if (len < width)
|
||||
len = width;
|
||||
|
||||
res.range.max = len;
|
||||
res.range.min = res.range.max;
|
||||
res.bounded = true;
|
||||
/* The minimum and maximum number of bytes produced by the directive. */
|
||||
fmtresult res;
|
||||
|
||||
res.range.min = len;
|
||||
|
||||
/* The upper bound of the number of bytes is unlimited when either
|
||||
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.constant = true;
|
||||
res.knownrange = true;
|
||||
res.bounded = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -1106,8 +1146,10 @@ format_integer (const conversion_spec &spec, tree arg)
|
||||
or one whose value range cannot be determined, create a T_MIN
|
||||
constant if the argument's type is signed and T_MAX otherwise,
|
||||
and use those to compute the range of bytes that the directive
|
||||
can output. */
|
||||
argmin = build_int_cst (argtype, 1);
|
||||
can output. When precision is specified but unknown, use zero
|
||||
as the minimum since it results in no bytes on output (unless
|
||||
width is specified to be greater than 0). */
|
||||
argmin = build_int_cst (argtype, prec != HOST_WIDE_INT_MIN);
|
||||
|
||||
int typeprec = TYPE_PRECISION (dirtype);
|
||||
int argprec = TYPE_PRECISION (argtype);
|
||||
@ -1257,11 +1299,13 @@ format_floating (const conversion_spec &spec, int width, int prec)
|
||||
{
|
||||
/* The minimum output is "0x.p+0". */
|
||||
res.range.min = 6 + (prec > 0 ? prec : 0);
|
||||
res.range.max = format_floating_max (type, 'a', prec);
|
||||
res.range.max = (width == INT_MIN
|
||||
? HOST_WIDE_INT_MAX
|
||||
: format_floating_max (type, 'a', prec));
|
||||
|
||||
/* The output of "%a" is fully specified only when precision
|
||||
is explicitly specified. */
|
||||
res.bounded = -1 < prec;
|
||||
is explicitly specified and width isn't unknown. */
|
||||
res.bounded = INT_MIN != width && -1 < prec;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1274,13 +1318,16 @@ format_floating (const conversion_spec &spec, int width, int prec)
|
||||
res.range.min = (sign
|
||||
+ 1 /* unit */ + (prec < 0 ? 7 : prec ? prec + 1 : 0)
|
||||
+ 2 /* e+ */ + 2);
|
||||
/* The maximum output is the minimum plus sign (unless already
|
||||
included), plus the difference between the minimum exponent
|
||||
of 2 and the maximum exponent for the type. */
|
||||
res.range.max = res.range.min + !sign + logexpdigs - 2;
|
||||
/* Unless width is uknown the maximum output is the minimum plus
|
||||
sign (unless already included), plus the difference between
|
||||
the minimum exponent of 2 and the maximum exponent for the type. */
|
||||
res.range.max = (width == INT_MIN
|
||||
? HOST_WIDE_INT_M1U
|
||||
: res.range.min + !sign + logexpdigs - 2);
|
||||
|
||||
/* "%e" is fully specified and the range of bytes is bounded. */
|
||||
res.bounded = true;
|
||||
/* "%e" is fully specified and the range of bytes is bounded
|
||||
unless width is unknown. */
|
||||
res.bounded = INT_MIN != width;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1296,10 +1343,11 @@ format_floating (const conversion_spec &spec, int width, int prec)
|
||||
format_floating_max (double_type_node, 'f'),
|
||||
format_floating_max (long_double_type_node, 'f')
|
||||
};
|
||||
res.range.max = f_max [ldbl];
|
||||
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl];
|
||||
|
||||
/* "%f" is fully specified and the range of bytes is bounded. */
|
||||
res.bounded = true;
|
||||
/* "%f" is fully specified and the range of bytes is bounded
|
||||
unless width is unknown. */
|
||||
res.bounded = INT_MIN != width;
|
||||
break;
|
||||
}
|
||||
case 'G':
|
||||
@ -1313,10 +1361,11 @@ format_floating (const conversion_spec &spec, int width, int prec)
|
||||
format_floating_max (double_type_node, 'g'),
|
||||
format_floating_max (long_double_type_node, 'g')
|
||||
};
|
||||
res.range.max = g_max [ldbl];
|
||||
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl];
|
||||
|
||||
/* "%g" is fully specified and the range of bytes is bounded. */
|
||||
res.bounded = true;
|
||||
/* "%g" is fully specified and the range of bytes is bounded
|
||||
unless width is unknown. */
|
||||
res.bounded = INT_MIN != width;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1342,6 +1391,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
|
||||
static fmtresult
|
||||
format_floating (const conversion_spec &spec, tree arg)
|
||||
{
|
||||
/* Set WIDTH to -1 when it's not specified, to INT_MIN when it is
|
||||
specified by the asterisk to an unknown value, and otherwise to
|
||||
a non-negative value corresponding to the specified width. */
|
||||
int width = -1;
|
||||
int prec = -1;
|
||||
|
||||
@ -1354,12 +1406,13 @@ format_floating (const conversion_spec &spec, tree arg)
|
||||
else if (spec.star_width)
|
||||
{
|
||||
if (TREE_CODE (spec.star_width) == INTEGER_CST)
|
||||
width = tree_to_shwi (spec.star_width);
|
||||
else
|
||||
{
|
||||
res.range.min = res.range.max = HOST_WIDE_INT_M1U;
|
||||
return res;
|
||||
width = tree_to_shwi (spec.star_width);
|
||||
if (width < 0)
|
||||
width = -width;
|
||||
}
|
||||
else
|
||||
width = INT_MIN;
|
||||
}
|
||||
|
||||
if (spec.have_precision)
|
||||
@ -1370,6 +1423,7 @@ format_floating (const conversion_spec &spec, tree arg)
|
||||
prec = tree_to_shwi (spec.star_precision);
|
||||
else
|
||||
{
|
||||
/* FIXME: Handle non-constant precision. */
|
||||
res.range.min = res.range.max = HOST_WIDE_INT_M1U;
|
||||
return res;
|
||||
}
|
||||
@ -1409,9 +1463,9 @@ format_floating (const conversion_spec &spec, tree arg)
|
||||
*pfmt++ = *pf;
|
||||
|
||||
/* Append width when specified and precision. */
|
||||
if (width != -1)
|
||||
if (-1 < width)
|
||||
pfmt += sprintf (pfmt, "%i", width);
|
||||
if (prec != -1)
|
||||
if (-1 < prec)
|
||||
pfmt += sprintf (pfmt, ".%i", prec);
|
||||
|
||||
/* Append the MPFR 'R' floating type specifier (no length modifier
|
||||
@ -1438,16 +1492,24 @@ format_floating (const conversion_spec &spec, tree arg)
|
||||
*minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval);
|
||||
}
|
||||
|
||||
/* The range of output is known even if the result isn't bounded. */
|
||||
if (width == INT_MIN)
|
||||
{
|
||||
res.knownrange = false;
|
||||
res.range.max = HOST_WIDE_INT_MAX;
|
||||
}
|
||||
else
|
||||
res.knownrange = true;
|
||||
|
||||
/* The output of all directives except "%a" is fully specified
|
||||
and so the result is bounded unless it exceeds INT_MAX.
|
||||
For "%a" the output is fully specified only when precision
|
||||
is explicitly specified. */
|
||||
res.bounded = ((TOUPPER (spec.specifier) != 'A'
|
||||
|| (0 <= prec && (unsigned) prec < target_int_max ()))
|
||||
res.bounded = (res.knownrange
|
||||
&& (TOUPPER (spec.specifier) != 'A'
|
||||
|| (0 <= prec && (unsigned) prec < target_int_max ()))
|
||||
&& res.range.min < target_int_max ());
|
||||
|
||||
/* The range of output is known even if the result isn't bounded. */
|
||||
res.knownrange = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -1517,20 +1579,10 @@ get_string_length (tree str)
|
||||
static fmtresult
|
||||
format_string (const conversion_spec &spec, tree arg)
|
||||
{
|
||||
unsigned width = spec.have_width && spec.width > 0 ? spec.width : 0;
|
||||
int prec = spec.have_precision ? spec.precision : -1;
|
||||
|
||||
if (spec.star_width)
|
||||
{
|
||||
width = (TREE_CODE (spec.star_width) == INTEGER_CST
|
||||
? tree_to_shwi (spec.star_width) : 0);
|
||||
if (width > INT_MAX)
|
||||
width = 0;
|
||||
}
|
||||
|
||||
if (spec.star_precision)
|
||||
prec = (TREE_CODE (spec.star_precision) == INTEGER_CST
|
||||
? tree_to_shwi (spec.star_precision) : -1);
|
||||
/* Set WIDTH and PRECISION based on the specification. */
|
||||
HOST_WIDE_INT width;
|
||||
HOST_WIDE_INT prec;
|
||||
get_width_and_precision (spec, &width, &prec);
|
||||
|
||||
fmtresult res;
|
||||
|
||||
@ -1590,11 +1642,12 @@ format_string (const conversion_spec &spec, tree arg)
|
||||
res.range = slen.range;
|
||||
|
||||
/* The output of "%s" and "%ls" directives with a constant
|
||||
string is in a known range. For "%s" it is the length
|
||||
of the string. For "%ls" it is in the range [length,
|
||||
length * MB_LEN_MAX]. (The final range can be further
|
||||
constrained by width and precision but it's always known.) */
|
||||
res.knownrange = true;
|
||||
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 = -1 < width;
|
||||
|
||||
if (spec.modifier == FMT_LEN_l)
|
||||
{
|
||||
@ -1622,19 +1675,32 @@ format_string (const conversion_spec &spec, tree arg)
|
||||
if (0 <= prec)
|
||||
res.range.max = prec;
|
||||
}
|
||||
else
|
||||
else if (0 <= width)
|
||||
{
|
||||
/* The output od a "%s" directive with a constant argument
|
||||
is bounded, constant, and obviously in a known range. */
|
||||
/* The output of a "%s" directive with a constant argument
|
||||
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;
|
||||
res.constant = true;
|
||||
res.constant = prec != HOST_WIDE_INT_MIN;
|
||||
}
|
||||
else if (width == HOST_WIDE_INT_MIN)
|
||||
{
|
||||
/* Specified but unknown width makes the output unbounded. */
|
||||
res.range.max = HOST_WIDE_INT_MAX;
|
||||
}
|
||||
|
||||
if (0 <= prec && (unsigned)prec < res.range.min)
|
||||
if (0 <= prec && (unsigned HOST_WIDE_INT)prec < res.range.min)
|
||||
{
|
||||
res.range.min = prec;
|
||||
res.range.max = prec;
|
||||
}
|
||||
else if (prec == HOST_WIDE_INT_MIN)
|
||||
{
|
||||
/* When precision is specified but not known the lower
|
||||
bound is assumed to be as low as zero. */
|
||||
res.range.min = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1648,10 +1714,10 @@ format_string (const conversion_spec &spec, tree arg)
|
||||
{
|
||||
if (slen.range.min >= target_int_max ())
|
||||
slen.range.min = 0;
|
||||
else if ((unsigned)prec < slen.range.min)
|
||||
else if ((unsigned HOST_WIDE_INT)prec < slen.range.min)
|
||||
slen.range.min = prec;
|
||||
|
||||
if ((unsigned)prec < slen.range.max
|
||||
if ((unsigned HOST_WIDE_INT)prec < slen.range.max
|
||||
|| slen.range.max >= target_int_max ())
|
||||
slen.range.max = prec;
|
||||
}
|
||||
@ -1674,20 +1740,23 @@ format_string (const conversion_spec &spec, tree arg)
|
||||
}
|
||||
|
||||
/* Adjust the lengths for field width. */
|
||||
if (res.range.min < width)
|
||||
res.range.min = width;
|
||||
if (0 < width)
|
||||
{
|
||||
if (res.range.min < (unsigned HOST_WIDE_INT)width)
|
||||
res.range.min = width;
|
||||
|
||||
if (res.range.max < width)
|
||||
res.range.max = width;
|
||||
if (res.range.max < (unsigned HOST_WIDE_INT)width)
|
||||
res.range.max = width;
|
||||
|
||||
/* Adjust BOUNDED if width happens to make them equal. */
|
||||
if (res.range.min == res.range.max && res.range.min < target_int_max ()
|
||||
&& bounded)
|
||||
res.bounded = true;
|
||||
/* Adjust BOUNDED if width happens to make them equal. */
|
||||
if (res.range.min == res.range.max && res.range.min < target_int_max ()
|
||||
&& bounded)
|
||||
res.bounded = true;
|
||||
}
|
||||
|
||||
/* When precision is specified the range of characters on output
|
||||
is known to be bounded by it. */
|
||||
if (-1 < prec)
|
||||
if (-1 < width && -1 < prec)
|
||||
res.knownrange = true;
|
||||
|
||||
return res;
|
||||
@ -1803,7 +1872,7 @@ format_directive (const pass_sprintf_length::call_info &info,
|
||||
(int)cvtlen, cvtbeg, fmtres.range.min,
|
||||
navail);
|
||||
}
|
||||
else
|
||||
else if (fmtres.range.max < HOST_WIDE_INT_MAX)
|
||||
{
|
||||
const char* fmtstr
|
||||
= (info.bounded
|
||||
@ -1817,6 +1886,19 @@ format_directive (const pass_sprintf_length::call_info &info,
|
||||
(int)cvtlen, cvtbeg,
|
||||
fmtres.range.min, fmtres.range.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,
|
||||
OPT_Wformat_length_, fmtstr,
|
||||
(int)cvtlen, cvtbeg,
|
||||
fmtres.range.min, navail);
|
||||
}
|
||||
}
|
||||
else if (navail < fmtres.range.max
|
||||
&& (((spec.specifier == 's'
|
||||
@ -2273,13 +2355,22 @@ pass_sprintf_length::compute_format_length (const call_info &info,
|
||||
|
||||
if (dollar || !spec.star_width)
|
||||
{
|
||||
if (spec.have_width && spec.width == 0)
|
||||
if (spec.have_width)
|
||||
{
|
||||
/* The '0' that has been interpreted as a width above is
|
||||
actually a flag. Reset HAVE_WIDTH, set the '0' flag,
|
||||
and continue processing other flags. */
|
||||
spec.have_width = false;
|
||||
spec.set_flag ('0');
|
||||
if (spec.width == 0)
|
||||
{
|
||||
/* The '0' that has been interpreted as a width above is
|
||||
actually a flag. Reset HAVE_WIDTH, set the '0' flag,
|
||||
and continue processing other flags. */
|
||||
spec.have_width = false;
|
||||
spec.set_flag ('0');
|
||||
}
|
||||
else if (!dollar)
|
||||
{
|
||||
/* (Non-zero) width has been seen. The next character
|
||||
is either a period or a digit. */
|
||||
goto start_precision;
|
||||
}
|
||||
}
|
||||
/* When either '$' has been seen, or width has not been seen,
|
||||
the next field is the optional flags followed by an optional
|
||||
@ -2324,6 +2415,7 @@ pass_sprintf_length::compute_format_length (const call_info &info,
|
||||
}
|
||||
}
|
||||
|
||||
start_precision:
|
||||
if ('.' == *pf)
|
||||
{
|
||||
++pf;
|
||||
@ -2341,7 +2433,12 @@ pass_sprintf_length::compute_format_length (const call_info &info,
|
||||
++pf;
|
||||
}
|
||||
else
|
||||
return;
|
||||
{
|
||||
/* The decimal precision or the asterisk are optional.
|
||||
When neither is specified it's taken to be zero. */
|
||||
spec.precision = 0;
|
||||
spec.have_precision = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (*pf)
|
||||
@ -2701,9 +2798,9 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
|
||||
|
||||
if (idx_dstsize == HOST_WIDE_INT_M1U)
|
||||
{
|
||||
// For non-bounded functions like sprintf, to determine
|
||||
// the size of the destination from the object or pointer
|
||||
// passed to it as the first argument.
|
||||
/* For non-bounded functions like sprintf, determine the size
|
||||
of the destination from the object or pointer passed to it
|
||||
as the first argument. */
|
||||
dstsize = get_destination_size (gimple_call_arg (info.callstmt, 0));
|
||||
}
|
||||
else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
|
||||
@ -2715,10 +2812,18 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
|
||||
dstsize = tree_to_uhwi (size);
|
||||
/* No object can be larger than SIZE_MAX bytes (half the address
|
||||
space) on the target. This imposes a limit that's one byte
|
||||
less than that. */
|
||||
less than that.
|
||||
The functions are defined only for output of at most INT_MAX
|
||||
bytes. Specifying a bound in excess of that limit effectively
|
||||
defeats the bounds checking (and on some implementations such
|
||||
as Solaris cause the function to fail with EINVAL). */
|
||||
if (dstsize >= target_size_max () / 2)
|
||||
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
|
||||
"specified destination size %wu too large",
|
||||
"specified destination size %wu is too large",
|
||||
dstsize);
|
||||
else if (dstsize > target_int_max ())
|
||||
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
|
||||
"specified destination size %wu exceeds %<INT_MAX %>",
|
||||
dstsize);
|
||||
}
|
||||
else if (TREE_CODE (size) == SSA_NAME)
|
||||
|
@ -1,3 +1,11 @@
|
||||
2016-11-28 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/78520
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-5.c: Add test cases.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-6.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Add test cases.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Add test cases.
|
||||
|
||||
2016-11-28 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c++/72808
|
||||
|
@ -44,6 +44,26 @@ void test_arg_int (int i, int n)
|
||||
|
||||
for (i = -n; i != n; ++i)
|
||||
T (8, "%08x", i);
|
||||
|
||||
/* As a special case, a precision of zero with an argument of zero
|
||||
results in zero bytes (unless modified by width). */
|
||||
T (0, "%.0d", ival (0));
|
||||
T (0, "%.0i", ival (0));
|
||||
T (0, "%.0o", ival (0));
|
||||
T (0, "%.0u", ival (0));
|
||||
T (0, "%.0x", ival (0));
|
||||
|
||||
T (0, "%.*d", 0, ival (0));
|
||||
T (0, "%.*i", 0, ival (0));
|
||||
T (0, "%.*o", 0, ival (0));
|
||||
T (0, "%.*u", 0, ival (0));
|
||||
T (0, "%.*x", 0, ival (0));
|
||||
|
||||
T (1, "%1.0d", ival (0));
|
||||
T (1, "%1.0i", ival (0));
|
||||
T (1, "%1.0o", ival (0));
|
||||
T (1, "%1.0u", ival (0));
|
||||
T (1, "%1.0x", ival (0));
|
||||
}
|
||||
|
||||
void test_arg_string (const char *s)
|
||||
|
73
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-6.c
Normal file
73
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-6.c
Normal file
@ -0,0 +1,73 @@
|
||||
/* PR middle-end/78476 - snprintf(0, 0, ...) with known arguments not
|
||||
optimized away
|
||||
A negative test complementing builtin-sprintf-5.c to verify that calls
|
||||
to the function that do not return a constant are not optimized away.
|
||||
{ dg-compile }
|
||||
{ dg-options "-O2 -fdump-tree-optimized" }
|
||||
{ dg-require-effective-target int32plus } */
|
||||
|
||||
#define CONCAT(a, b) a ## b
|
||||
#define CAT(a, b) CONCAT (a, b)
|
||||
|
||||
#define T(...) \
|
||||
do { \
|
||||
int CAT (n, __LINE__) = __builtin_snprintf (0, 0, __VA_ARGS__); \
|
||||
sink (CAT (n, __LINE__)); \
|
||||
} while (0)
|
||||
|
||||
void sink (int);
|
||||
|
||||
static int
|
||||
int_range (int min, int max)
|
||||
{
|
||||
extern int int_value (void);
|
||||
int val = int_value ();
|
||||
if (val < min || max < val)
|
||||
val = min;
|
||||
return val;
|
||||
}
|
||||
|
||||
#define R(min, max) int_range (min, max)
|
||||
|
||||
void test_arg_int (int width, int prec, int i, int n)
|
||||
{
|
||||
T ("%i", i);
|
||||
T ("%1i", i);
|
||||
T ("%2i", i);
|
||||
T ("%3i", i);
|
||||
T ("%4i", i);
|
||||
|
||||
T ("%*i", width, 0);
|
||||
T ("%*i", width, 1);
|
||||
T ("%*i", width, i);
|
||||
|
||||
T ("%.*i", prec, 0);
|
||||
T ("%.*i", prec, 1);
|
||||
T ("%.*i", prec, i);
|
||||
T ("%.*i", 0, i);
|
||||
|
||||
T ("%i", R (1, 10));
|
||||
|
||||
for (i = -n; i != n; ++i)
|
||||
T ("%*x", n, i);
|
||||
}
|
||||
|
||||
void test_arg_string (int width, int prec, const char *s)
|
||||
{
|
||||
T ("%-s", s);
|
||||
T ("%1s", s);
|
||||
T ("%.1s", s);
|
||||
T ("%*s", width, s);
|
||||
T ("%.*s", prec, s);
|
||||
T ("%1.*s", prec, s);
|
||||
T ("%*.1s", width, s);
|
||||
T ("%*.*s", width, prec, s);
|
||||
T ("%*s", width, "123");
|
||||
T ("%.*s", prec, "123");
|
||||
T ("%1.*s", prec, "123");
|
||||
T ("%*.1s", width, "123");
|
||||
T ("%*.*s", width, prec, "123");
|
||||
}
|
||||
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "snprintf" 27 "optimized"} } */
|
@ -233,6 +233,8 @@ void test_sprintf_chk_s_const (void)
|
||||
T ( 1, "%*s", 1, s0); /* { dg-warning "nul past the end" } */
|
||||
T (-1, "%*s", 1, s0); /* No warning for unknown destination size. */
|
||||
|
||||
T (1, "%.s", "");
|
||||
T (1, "%.s", "123");
|
||||
T (1, "%.0s", "123");
|
||||
T (1, "%.0s", s3);
|
||||
T (1, "%.*s", 0, "123");
|
||||
@ -450,6 +452,24 @@ void test_sprintf_chk_hh_const (void)
|
||||
T (4, "%hhi %hhi", 10, 1); /* { dg-warning "nul past the end" } */
|
||||
T (4, "%hhi %hhi", 11, 12); /* { dg-warning "into a region" } */
|
||||
|
||||
/* As a special case, a precision of zero with an argument of zero
|
||||
results in zero bytes (unless modified by width). */
|
||||
T (1, "%.0hhd", 0);
|
||||
T (1, "%+.0hhd", 0);
|
||||
T (1, "%-.0hhd", 0);
|
||||
T (1, "% .0hhd", 0);
|
||||
T (1, "%0.0hhd", 0); /* { dg-warning ".0. flag ignored with precision" } */
|
||||
T (1, "%00.0hhd", 0); /* { dg-warning "repeated .0. flag in format" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%-0.0hhd", 0); /* { dg-warning ".0. flag ignored with .-. flag" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%.0hhi", 0);
|
||||
T (1, "%.0hho", 0);
|
||||
T (1, "%#.0hho", 0);
|
||||
T (1, "%.0hhx", 0);
|
||||
T (1, "%.0hhX", 0);
|
||||
T (1, "%#.0hhX", 0);
|
||||
|
||||
T (5, "%0*hhd %0*hhi", 0, 7, 0, 9);
|
||||
T (5, "%0*hhd %0*hhi", 1, 7, 1, 9);
|
||||
T (5, "%0*hhd %0*hhi", 1, 7, 2, 9);
|
||||
@ -546,14 +566,32 @@ void test_sprintf_chk_h_const (void)
|
||||
T (4, "%#hx", 0x100); /* { dg-warning "into a region" } */
|
||||
T (4, "%#hx", -1); /* { dg-warning "into a region" } */
|
||||
|
||||
/* As a special case, a precision of zero with an argument of zero
|
||||
results in zero bytes (unless modified by width). */
|
||||
T (1, "%.0hd", 0);
|
||||
T (1, "%+.0hd", 0);
|
||||
T (1, "%-.0hd", 0);
|
||||
T (1, "% .0hd", 0);
|
||||
T (1, "%0.0hd", 0); /* { dg-warning ".0. flag ignored with precision" } */
|
||||
T (1, "%00.0hd", 0); /* { dg-warning "repeated .0. flag in format" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%-0.0hd", 0); /* { dg-warning ".0. flag ignored with .-. flag" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%.0hi", 0);
|
||||
T (1, "%.0ho", 0);
|
||||
T (1, "%#.0ho", 0);
|
||||
T (1, "%.0hx", 0);
|
||||
T (1, "%.0hX", 0);
|
||||
T (1, "%#.0hX", 0);
|
||||
|
||||
#undef MAX
|
||||
#define MAX 65535
|
||||
|
||||
T (1, "%hhu", 0); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%hhu", 1); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%hhu", -1); /* { dg-warning "into a region" } */
|
||||
T (1, "%hhu", MAX); /* { dg-warning "into a region" } */
|
||||
T (1, "%hhu", MAX + 1); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%hu", 0); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%hu", 1); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%hu", -1); /* { dg-warning "into a region" } */
|
||||
T (1, "%hu", MAX); /* { dg-warning "into a region" } */
|
||||
T (1, "%hu", MAX + 1); /* { dg-warning "nul past the end" } */
|
||||
}
|
||||
|
||||
/* Exercise the "%d", "%i", "%o", "%u", and "%x" directives with
|
||||
@ -611,6 +649,24 @@ void test_sprintf_chk_integer_const (void)
|
||||
T ( 8, "%8u", 1); /* { dg-warning "nul past the end" } */
|
||||
T ( 9, "%8u", 1);
|
||||
|
||||
/* As a special case, a precision of zero with an argument of zero
|
||||
results in zero bytes (unless modified by width). */
|
||||
T (1, "%.0d", 0);
|
||||
T (1, "%+.0d", 0);
|
||||
T (1, "%-.0d", 0);
|
||||
T (1, "% .0d", 0);
|
||||
T (1, "%0.0d", 0); /* { dg-warning ".0. flag ignored with precision" } */
|
||||
T (1, "%00.0d", 0); /* { dg-warning "repeated .0. flag in format" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%-0.0d", 0); /* { dg-warning ".0. flag ignored with .-. flag" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%.0i", 0);
|
||||
T (1, "%.0o", 0);
|
||||
T (1, "%#.0o", 0);
|
||||
T (1, "%.0x", 0);
|
||||
T (1, "%.0X", 0);
|
||||
T (1, "%#.0X", 0);
|
||||
|
||||
T ( 7, "%1$i%2$i%3$i", 1, 23, 456);
|
||||
T ( 8, "%1$i%2$i%3$i%1$i", 1, 23, 456);
|
||||
T ( 8, "%1$i%2$i%3$i%2$i", 1, 23, 456); /* { dg-warning "nul past the end" } */
|
||||
@ -691,6 +747,24 @@ void test_sprintf_chk_j_const (void)
|
||||
|
||||
T ( 8, "%8ju", I (1)); /* { dg-warning "nul past the end" } */
|
||||
T ( 9, "%8ju", I (1));
|
||||
|
||||
/* As a special case, a precision of zero with an argument of zero
|
||||
results in zero bytes (unless modified by width). */
|
||||
T (1, "%.0jd", I (0));
|
||||
T (1, "%+.0jd", I (0));
|
||||
T (1, "%-.0jd", I (0));
|
||||
T (1, "% .0jd", I (0));
|
||||
T (1, "%0.0jd", I (0)); /* { dg-warning ".0. flag ignored with precision" } */
|
||||
T (1, "%00.0jd", I (0)); /* { dg-warning "repeated .0. flag in format" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%-0.0jd", I (0)); /* { dg-warning ".0. flag ignored with .-. flag" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%.0ji", I (0));
|
||||
T (1, "%.0jo", I (0));
|
||||
T (1, "%#.0jo", I (0));
|
||||
T (1, "%.0jx", I (0));
|
||||
T (1, "%.0jX", I (0));
|
||||
T (1, "%#.0jX", I (0));
|
||||
}
|
||||
|
||||
/* Exercise the "%ld", "%li", "%lo", "%lu", and "%lx" directives
|
||||
@ -747,6 +821,24 @@ void test_sprintf_chk_l_const (void)
|
||||
|
||||
T ( 8, "%8lu", 1L); /* { dg-warning "nul past the end" } */
|
||||
T ( 9, "%8lu", 1L);
|
||||
|
||||
/* As a special case, a precision of zero with an argument of zero
|
||||
results in zero bytes (unless modified by width). */
|
||||
T (1, "%.0ld", 0L);
|
||||
T (1, "%+.0ld", 0L);
|
||||
T (1, "%-.0ld", 0L);
|
||||
T (1, "% .0ld", 0L);
|
||||
T (1, "%0.0ld", 0L); /* { dg-warning ".0. flag ignored with precision" } */
|
||||
T (1, "%00.0ld", 0L); /* { dg-warning "repeated .0. flag in format" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%-0.0ld", 0L); /* { dg-warning ".0. flag ignored with .-. flag" } */
|
||||
/* { dg-warning ".0. flag ignored with precision" "" { target *-*-* } .-1 } */
|
||||
T (1, "%.0li", 0L);
|
||||
T (1, "%.0lo", 0L);
|
||||
T (1, "%#.0lo", 0L);
|
||||
T (1, "%.0lx", 0L);
|
||||
T (1, "%.0lX", 0L);
|
||||
T (1, "%#.0lX", 0L);
|
||||
}
|
||||
|
||||
/* Exercise the "%lld", "%lli", "%llo", "%llu", and "%llx" directives
|
||||
@ -858,37 +950,56 @@ void test_sprintf_chk_z_const (void)
|
||||
|
||||
void test_sprintf_chk_a_const (void)
|
||||
{
|
||||
T (-1, "%a", 0.0);
|
||||
T (-1, "%la", 0.0);
|
||||
T (-1, "%a", 0.0);
|
||||
T (-1, "%la", 0.0);
|
||||
T (-1, "%.a", 0.0);
|
||||
T (-1, "%.la", 0.0);
|
||||
T (-1, "%123.a", 0.0);
|
||||
T (-1, "%234.la", 0.0);
|
||||
T (-1, "%.345a", 0.0);
|
||||
T (-1, "%456.567la", 0.0);
|
||||
|
||||
/* The least number of bytes on output is 6 for "0x0p+0". When precision
|
||||
is missing the number of digits after the decimal point isn't fully
|
||||
specified by C (it seems like a defect). */
|
||||
T (0, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (0, "%la", 0.0); /* { dg-warning "into a region" } */
|
||||
T (1, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (2, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (3, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (4, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (5, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (6, "%a", 0.0); /* { dg-warning "writing a terminating nul" } */
|
||||
T (0, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (0, "%la", 0.0); /* { dg-warning "into a region" } */
|
||||
T (1, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (2, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (3, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (4, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (5, "%a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (6, "%a", 0.0); /* { dg-warning "writing a terminating nul" } */
|
||||
T (7, "%a", 0.0);
|
||||
|
||||
T (0, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (0, "%.0la", 0.0); /* { dg-warning "into a region" } */
|
||||
T (1, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (2, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (3, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (4, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (5, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (6, "%.0a", 0.0); /* { dg-warning "writing a terminating nul" } */
|
||||
T (0, "%.a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (0, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (0, "%.0la", 0.0); /* { dg-warning "into a region" } */
|
||||
T (1, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (2, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (3, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (4, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (5, "%.0a", 0.0); /* { dg-warning "into a region" } */
|
||||
T (6, "%.0a", 0.0); /* { dg-warning "writing a terminating nul" } */
|
||||
|
||||
T (7, "%6.a", 0.0);
|
||||
T (7, "%7.a", 0.0); /* { dg-warning "writing a terminating nul" } */
|
||||
T (7, "%7.1a", 0.0); /* { dg-warning "writing 8 bytes into a region of size 7" } */
|
||||
|
||||
T (7, "%.a", 0.0);
|
||||
T (7, "%.0a", 0.0);
|
||||
}
|
||||
|
||||
void test_sprintf_chk_e_const (void)
|
||||
{
|
||||
T (-1, "%E", 0.0);
|
||||
T (-1, "%lE", 0.0);
|
||||
T (-1, "%E", 0.0);
|
||||
T (-1, "%lE", 0.0);
|
||||
T (-1, "%.E", 0.0);
|
||||
T (-1, "%.lE", 0.0);
|
||||
T (-1, "%123.E", 0.0);
|
||||
T (-1, "%234.lE", 0.0);
|
||||
T (-1, "%.345E", 0.0);
|
||||
T (-1, "%.456lE", 0.0);
|
||||
|
||||
T ( 0, "%E", 0.0); /* { dg-warning "into a region" } */
|
||||
T ( 0, "%e", 0.0); /* { dg-warning "into a region" } */
|
||||
@ -910,8 +1021,10 @@ void test_sprintf_chk_e_const (void)
|
||||
T (16, "%.8e", -1.9e+104); /* { dg-warning "nul past the end" } */
|
||||
T (17, "%.8e", -2.0e+105); /* -2.00000000e+105 */
|
||||
|
||||
T ( 5, "%.e", 0.0); /* { dg-warning "nul past the end" } */
|
||||
T ( 5, "%.0e", 0.0); /* { dg-warning "nul past the end" } */
|
||||
T ( 5, "%.0e", 1.0); /* { dg-warning "nul past the end" } */
|
||||
T ( 6, "%.e", 1.0);
|
||||
T ( 6, "%.0e", 1.0);
|
||||
|
||||
/* The actual output of the following directives depends on the rounding
|
||||
@ -938,7 +1051,7 @@ void test_sprintf_chk_e_const (void)
|
||||
the value one, and unknown strings are assumed to have a zero
|
||||
length. */
|
||||
|
||||
void test_sprintf_chk_s_nonconst (int i, const char *s)
|
||||
void test_sprintf_chk_s_nonconst (int w, int p, const char *s)
|
||||
{
|
||||
T (-1, "%s", s);
|
||||
T ( 0, "%s", s); /* { dg-warning "nul past the end" } */
|
||||
@ -946,6 +1059,19 @@ void test_sprintf_chk_s_nonconst (int i, const char *s)
|
||||
T ( 1, "%.0s", s);
|
||||
T ( 1, "%.1s", s); /* { dg-warning "nul past the end" } */
|
||||
|
||||
/* The string argument is constant but the width and/or precision
|
||||
is not. */
|
||||
T ( 1, "%*s", w, "");
|
||||
T ( 1, "%*s", w, "1"); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%.*s", w, "");
|
||||
T ( 1, "%.*s", w, "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 3 or more bytes into a region of size 1" } */
|
||||
T ( 2, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 2" } */
|
||||
T ( 3, "%*s", w, "123"); /* { dg-warning "writing a terminating nul past the end" } */
|
||||
T ( 4, "%*s", w, "123");
|
||||
|
||||
/* The following will definitely write past the end of the buffer,
|
||||
but since at level 1 the length of an unknown string argument
|
||||
is assumed to be zero, it will write the terminating nul past
|
||||
@ -957,7 +1083,7 @@ void test_sprintf_chk_s_nonconst (int i, const char *s)
|
||||
/* Exercise the hh length modifier with all integer specifiers and
|
||||
a non-constant argument. */
|
||||
|
||||
void test_sprintf_chk_hh_nonconst (int a)
|
||||
void test_sprintf_chk_hh_nonconst (int w, int p, int a)
|
||||
{
|
||||
T (-1, "%hhd", a);
|
||||
|
||||
@ -999,11 +1125,48 @@ void test_sprintf_chk_hh_nonconst (int a)
|
||||
T (2, "%#hho", a); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%#hhx", a); /* { dg-warning ".%#hhx. directive writing between 3 and . bytes into a region of size 2" } */
|
||||
|
||||
T (3, "%0hhd", a);
|
||||
T (3, "%1hhd", a);
|
||||
T (3, "%2hhd", a);
|
||||
T (3, "%2hhi", a);
|
||||
T (3, "%2hho", a);
|
||||
T (3, "%2hhu", a);
|
||||
T (3, "%2hhx", a);
|
||||
T (3, "%2.hhx", a);
|
||||
|
||||
T (3, "%3hhd", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%3hhi", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%3hho", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%3hhu", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%3hhx", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%3.hhx", a); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (4, "%5hhd", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%6hhi", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%7hho", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%8hhu", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%9hhx", a); /* { dg-warning "into a region" } */
|
||||
|
||||
T (3, "%.hhd", a);
|
||||
T (3, "%.0hhd", a);
|
||||
T (3, "%.1hhd", a);
|
||||
T (3, "%.2hhd", a);
|
||||
T (3, "%.2hhi", a);
|
||||
T (3, "%.2hho", a);
|
||||
T (3, "%.2hhu", a);
|
||||
T (3, "%.2hhx", a);
|
||||
|
||||
T (3, "%.3hhd", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%.3hhi", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%.3hho", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%.3hhu", a); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%.3hhx", a); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (4, "%.5hhd", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%.6hhi", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%.7hho", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%.8hhu", a); /* { dg-warning "into a region" } */
|
||||
T (4, "%.9hhx", a); /* { dg-warning "into a region" } */
|
||||
|
||||
/* Exercise cases where the type of the actual argument (whose value
|
||||
and range are unknown) constrain the size of the output and so
|
||||
@ -1012,6 +1175,55 @@ void test_sprintf_chk_hh_nonconst (int a)
|
||||
T (2, "%hhd", (UChar)a);
|
||||
T (2, "%hhi", (UChar)a);
|
||||
T (2, "%-hhi", (UChar)a);
|
||||
|
||||
/* Exercise cases where the argument is known but width isn't. */
|
||||
T (0, "%*hhi", w, 0); /* { dg-warning "into a region" } */
|
||||
T (1, "%*hhi", w, 0); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%*hhi", w, 0);
|
||||
T (2, "%*hhi", w, 12); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%*hhi", w, 123); /* { dg-warning "into a region" } */
|
||||
|
||||
/* The argument is known but precision isn't. When the argument
|
||||
is zero only the first call can be diagnosed since a zero
|
||||
precision would result in no bytes on output. */
|
||||
T (0, "%.*hhi", p, 0); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%.*hhi", p, 0);
|
||||
T (2, "%.*hhi", p, 0);
|
||||
T (2, "%.*hhi", p, 12); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%.*hhi", p, 123); /* { dg-warning "into a region" } */
|
||||
|
||||
/* The argument is known but neither width nor precision is. */
|
||||
T (0, "%*.*hhi", w, p, 0); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%*.*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, 123); /* { dg-warning "into a region" } */
|
||||
|
||||
/* The argument and width are known but precision isn't. */
|
||||
T (0, "%1.*hhi", p, 0); /* { dg-warning "into a region" } */
|
||||
T (0, "%-1.*hhi", p, 0); /* { dg-warning "into a region" } */
|
||||
T (1, "%1.*hhi", p, 0); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%1.*hhi", p, 0);
|
||||
T (2, "%2.*hhi", p, 0); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%1.*hhi", p, 12); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%2.*hhi", p, 12); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (2, "%1.*hhi", p, 123); /* { dg-warning "into a region" } */
|
||||
T (2, "%2.*hhi", p, 123); /* { dg-warning "into a region" } */
|
||||
T (2, "%3.*hhi", p, 123); /* { dg-warning "into a region" } */
|
||||
|
||||
/* The argument and precision are known but width isn't. */
|
||||
T (0, "%*.1hhi", w, 0); /* { dg-warning "into a region" } */
|
||||
T (1, "%*.1hhi", w, 0); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%*.1hhi", w, 0);
|
||||
T (2, "%*.2hhi", w, 0); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%*.1hhi", w, 12); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%*.2hhi", w, 12); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%*.3hhi", w, 12); /* { dg-warning "into a region" } */
|
||||
|
||||
T (2, "%*.1hhi", w, 123); /* { dg-warning "into a region" } */
|
||||
T (2, "%*.2hhi", w, 123); /* { dg-warning "into a region" } */
|
||||
T (2, "%*.3hhi", w, 123); /* { dg-warning "into a region" } */
|
||||
}
|
||||
|
||||
/* Exercise the h length modifier with all integer specifiers and
|
||||
@ -1063,7 +1275,7 @@ void test_sprintf_chk_h_nonconst (int a)
|
||||
/* Exercise all integer specifiers with no modifier and a non-constant
|
||||
argument. */
|
||||
|
||||
void test_sprintf_chk_int_nonconst (int a)
|
||||
void test_sprintf_chk_int_nonconst (int w, int p, int a)
|
||||
{
|
||||
T (-1, "%d", a);
|
||||
|
||||
@ -1104,12 +1316,22 @@ void test_sprintf_chk_int_nonconst (int a)
|
||||
T (3, "%2o", a);
|
||||
T (3, "%2u", a);
|
||||
T (3, "%2x", a);
|
||||
|
||||
T (1, "%.*d", p, a);
|
||||
}
|
||||
|
||||
void test_sprintf_chk_e_nonconst (double d)
|
||||
void test_sprintf_chk_e_nonconst (int w, int p, double d)
|
||||
{
|
||||
T (-1, "%E", d);
|
||||
T (-1, "%lE", d);
|
||||
T (-1, "%E", d);
|
||||
T (-1, "%lE", d);
|
||||
T (-1, "%.E", d);
|
||||
T (-1, "%.lE", d);
|
||||
T (-1, "%*E", w, d);
|
||||
T (-1, "%*lE", w, d);
|
||||
T (-1, "%.*E", p, d);
|
||||
T (-1, "%.*lE", p, d);
|
||||
T (-1, "%*.*E", w, p, d);
|
||||
T (-1, "%*.*lE", w, p, d);
|
||||
|
||||
T ( 0, "%E", d); /* { dg-warning "writing between 12 and 14 bytes into a region of size 0" } */
|
||||
T ( 0, "%e", d); /* { dg-warning "into a region" } */
|
||||
@ -1123,9 +1345,9 @@ void test_sprintf_chk_e_nonconst (double d)
|
||||
T (14, "%E", d);
|
||||
T (14, "%e", d);
|
||||
|
||||
T (0, "%+E", d); /* { dg-warning "writing between 13 and 14 bytes into a region of size 0" } */
|
||||
T (0, "%-e", d); /* { dg-warning "writing between 12 and 14 bytes into a region of size 0" } */
|
||||
T (0, "% E", d); /* { dg-warning "writing between 13 and 14 bytes into a region of size 0" } */
|
||||
T ( 0, "%+E", d); /* { dg-warning "writing between 13 and 14 bytes into a region of size 0" } */
|
||||
T ( 0, "%-e", d); /* { dg-warning "writing between 12 and 14 bytes into a region of size 0" } */
|
||||
T ( 0, "% E", d); /* { dg-warning "writing between 13 and 14 bytes into a region of size 0" } */
|
||||
|
||||
/* The range of output of "%.0e" is between 5 and 7 bytes (not counting
|
||||
the terminating NUL. */
|
||||
@ -1136,6 +1358,9 @@ void test_sprintf_chk_e_nonconst (double d)
|
||||
the terminating NUL. */
|
||||
T ( 7, "%.1e", d); /* { dg-warning "writing a terminating nul past the end" } */
|
||||
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", w, d); /* { dg-warning "writing 12 or more bytes into a region of size 0" } */
|
||||
}
|
||||
|
||||
void test_sprintf_chk_f_nonconst (double d)
|
||||
@ -1204,7 +1429,6 @@ void test_vsprintf_chk_c (__builtin_va_list va)
|
||||
/* Here in the best case each argument will format as single character,
|
||||
causing the terminating NUL to be written past the end. */
|
||||
T (3, "%lc%c%c");
|
||||
|
||||
}
|
||||
|
||||
void test_vsprintf_chk_int (__builtin_va_list va)
|
||||
@ -1254,9 +1478,11 @@ void test_vsprintf_chk_int (__builtin_va_list va)
|
||||
#define T(size, fmt, ...) \
|
||||
__builtin_snprintf (buffer (size), objsize (size), fmt, __VA_ARGS__)
|
||||
|
||||
void test_snprintf_c_const (void)
|
||||
void test_snprintf_c_const (char *d)
|
||||
{
|
||||
T (-1, "%c", 0); /* { dg-warning "specified destination size \[0-9\]+ too large" } */
|
||||
T (-1, "%c", 0); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
|
||||
__builtin_snprintf (d, INT_MAX, "%c", 0);
|
||||
|
||||
/* Verify the full text of the diagnostic for just the distinct messages
|
||||
and use abbreviations in subsequent test cases. */
|
||||
@ -1306,7 +1532,7 @@ void test_snprintf_chk_c_const (void)
|
||||
the function by __builtin_object_size) is diagnosed. */
|
||||
__builtin___snprintf_chk (buffer, 3, 0, 2, " "); /* { dg-warning "always overflow|specified size 3 exceeds the size 2 of the destination" } */
|
||||
|
||||
T (-1, "%c", 0); /* { dg-warning "specified destination size \[^ \]* too large" } */
|
||||
T (-1, "%c", 0); /* { dg-warning "specified destination size \[^ \]* is too large" } */
|
||||
|
||||
T (0, "%c", 0);
|
||||
T (0, "%c%c", 0, 0);
|
||||
@ -1417,7 +1643,7 @@ void test_vsprintf_int (__builtin_va_list va)
|
||||
|
||||
void test_vsnprintf_s (__builtin_va_list va)
|
||||
{
|
||||
T (-1, "%s"); /* { dg-warning "specified destination size \[^ \]* too large" } */
|
||||
T (-1, "%s"); /* { dg-warning "specified destination size \[^ \]* is too large" } */
|
||||
|
||||
T (0, "%s");
|
||||
T (1, "%s");
|
||||
@ -1442,7 +1668,7 @@ void test_vsnprintf_chk_s (__builtin_va_list va)
|
||||
the function by __builtin_object_size) is diagnosed. */
|
||||
__builtin___vsnprintf_chk (buffer, 123, 0, 122, "%-s", va); /* { dg-warning "always overflow|specified size 123 exceeds the size 122 of the destination object" } */
|
||||
|
||||
__builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va); /* { dg-warning "always overflow|destination size .\[0-9\]+. too large" } */
|
||||
__builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va); /* { dg-warning "always overflow|destination size .\[0-9\]+. is too large" } */
|
||||
|
||||
T (0, "%s");
|
||||
T (1, "%s");
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-std=c99 -O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
#ifndef LINE
|
||||
# define LINE 0
|
||||
#endif
|
||||
@ -232,3 +234,48 @@ void test_sprintf_chk_range_sshort (signed short *a, signed short *b)
|
||||
T ( 4, "%i", Ra (998, 999));
|
||||
T ( 4, "%i", Ra (999, 1000)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
}
|
||||
|
||||
/* Verify that destination size in excess of INT_MAX (and, separately,
|
||||
in excess of the largest object) is diagnosed. The former because
|
||||
the functions are defined only for output of at most INT_MAX and
|
||||
specifying a large upper bound defeats the bounds checking (and,
|
||||
on some implementations such as Solaris, causes the function to
|
||||
fail. The latter because due to the limit of ptrdiff_t no object
|
||||
can be larger than PTRDIFF_MAX bytes. */
|
||||
|
||||
void test_too_large (char *d, int x, __builtin_va_list va)
|
||||
{
|
||||
const size_t imax = __INT_MAX__;
|
||||
const size_t imax_p1 = imax + 1;
|
||||
|
||||
__builtin_snprintf (d, imax, "%c", x);
|
||||
__builtin_snprintf (d, imax_p1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "" { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
|
||||
__builtin_vsnprintf (d, imax, "%c", va);
|
||||
__builtin_vsnprintf (d, imax_p1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
|
||||
__builtin___snprintf_chk (d, imax, 0, imax, "%c", x);
|
||||
__builtin___snprintf_chk (d, imax_p1, 0, imax_p1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
|
||||
__builtin___vsnprintf_chk (d, imax, 0, imax, "%c", va);
|
||||
__builtin___vsnprintf_chk (d, imax_p1, 0, imax_p1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
|
||||
const size_t ptrmax = __PTRDIFF_MAX__;
|
||||
const size_t ptrmax_m1 = ptrmax - 1;
|
||||
|
||||
__builtin_snprintf (d, ptrmax_m1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "" { target lp64 } } */
|
||||
__builtin_snprintf (d, ptrmax, " %c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
|
||||
__builtin_vsnprintf (d, ptrmax_m1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "" { target lp64 } } */
|
||||
__builtin_vsnprintf (d, ptrmax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
|
||||
__builtin___snprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "" { target lp64 } } */
|
||||
__builtin___snprintf_chk (d, ptrmax, 0, ptrmax, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
|
||||
__builtin___vsnprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "" { target lp64 } } */
|
||||
__builtin___vsnprintf_chk (d, ptrmax, 0, ptrmax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user