-/* Copyright (C) 2016-2018 Free Software Foundation, Inc.
+/* Copyright (C) 2016-2019 Free Software Foundation, Inc.
Contributed by Martin Sebor <msebor@redhat.com>.
This file is part of GCC.
#include "tree-ssa-propagate.h"
#include "calls.h"
#include "cfgloop.h"
+#include "tree-scalar-evolution.h"
+#include "tree-ssa-loop.h"
#include "intl.h"
#include "langhooks.h"
+#include "attribs.h"
#include "builtins.h"
#include "stor-layout.h"
class sprintf_dom_walker : public dom_walker
{
public:
- sprintf_dom_walker () : dom_walker (CDI_DOMINATORS) {}
+ sprintf_dom_walker ()
+ : dom_walker (CDI_DOMINATORS),
+ evrp_range_analyzer (false) {}
~sprintf_dom_walker () {}
edge before_dom_children (basic_block) FINAL OVERRIDE;
the return value optimization. */
bool knownrange;
- /* True if no individual directive resulted in more than 4095 bytes
- 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;
+ /* True if no individual directive could fail or result in more than
+ 4095 bytes 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.
+ Similarly, directives that can fail (such as wide character
+ directives) disable the optimization. */
+ bool posunder4k;
/* True when a floating point directive has been seen in the format
string. */
overlong strings just like the translated strings are. */
if (target_to_host_charmap['\0'] == 1)
{
- strncpy (hostr, targstr, hostsz - 4);
- if (strlen (targstr) >= hostsz)
- strcpy (hostr + hostsz - 4, "...");
+ size_t len = strlen (targstr);
+ if (len >= hostsz)
+ {
+ memcpy (hostr, targstr, hostsz - 4);
+ strcpy (hostr + hostsz - 4, "...");
+ }
+ else
+ memcpy (hostr, targstr, len + 1);
return hostr;
}
if (!*targstr)
break;
- if (size_t (ph - hostr) == hostsz - 4)
+ if (size_t (ph - hostr) == hostsz)
{
- *ph = '\0';
- strcat (ph, "...");
+ strcpy (ph - 4, "...");
break;
}
}
}
/* Convert the sequence of decimal digits in the execution character
- starting at S to a long, just like strtol does. Return the result
- and set *END to one past the last converted character. On range
- error set ERANGE to the digit that caused it. */
+ starting at *PS to a HOST_WIDE_INT, analogously to strtol. Return
+ the result and set *PS to one past the last converted character.
+ On range error set ERANGE to the digit that caused it. */
-static inline long
-target_strtol10 (const char **ps, const char **erange)
+static inline HOST_WIDE_INT
+target_strtowi (const char **ps, const char **erange)
{
unsigned HOST_WIDE_INT val = 0;
for ( ; ; ++*ps)
c -= '0';
/* Check for overflow. */
- if (val > (LONG_MAX - c) / 10LU)
+ if (val > ((unsigned HOST_WIDE_INT) HOST_WIDE_INT_MAX - c) / 10LU)
{
- val = LONG_MAX;
+ val = HOST_WIDE_INT_MAX;
*erange = *ps;
/* Skip the remaining digits. */
return val;
}
-/* Return the constant initial value of DECL if available or DECL
- otherwise. Same as the synonymous function in c/c-typeck.c. */
-
-static tree
-decl_constant_value (tree decl)
-{
- if (/* Don't change a variable array bound or initial value to a constant
- in a place where a variable is invalid. Note that DECL_INITIAL
- isn't valid for a PARM_DECL. */
- current_function_decl != 0
- && TREE_CODE (decl) != PARM_DECL
- && !TREE_THIS_VOLATILE (decl)
- && TREE_READONLY (decl)
- && DECL_INITIAL (decl) != 0
- && TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK
- /* This is invalid if initial value is not constant.
- If it has either a function call, a memory reference,
- or a variable, then re-evaluating it could give different results. */
- && TREE_CONSTANT (DECL_INITIAL (decl))
- /* Check for cases where this is sub-optimal, even though valid. */
- && TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR)
- return DECL_INITIAL (decl);
- return decl;
-}
-
/* Given FORMAT, set *PLOC to the source location of the format string
and return the format string if it is known or null otherwise. */
static const char*
get_format_string (tree format, location_t *ploc)
{
- if (VAR_P (format))
- {
- /* Pull out a constant value if the front end didn't. */
- format = decl_constant_value (format);
- STRIP_NOPS (format);
- }
-
- if (integer_zerop (format))
- {
- /* FIXME: Diagnose null format string if it hasn't been diagnosed
- by -Wformat (the latter diagnoses only nul pointer constants,
- this pass can do better). */
- return NULL;
- }
-
- HOST_WIDE_INT offset = 0;
-
- if (TREE_CODE (format) == POINTER_PLUS_EXPR)
- {
- tree arg0 = TREE_OPERAND (format, 0);
- tree arg1 = TREE_OPERAND (format, 1);
- STRIP_NOPS (arg0);
- STRIP_NOPS (arg1);
-
- if (TREE_CODE (arg1) != INTEGER_CST)
- return NULL;
-
- format = arg0;
-
- /* POINTER_PLUS_EXPR offsets are to be interpreted signed. */
- if (!cst_and_fits_in_hwi (arg1))
- return NULL;
-
- offset = int_cst_value (arg1);
- }
-
- if (TREE_CODE (format) != ADDR_EXPR)
- return NULL;
-
*ploc = EXPR_LOC_OR_LOC (format, input_location);
- format = TREE_OPERAND (format, 0);
-
- if (TREE_CODE (format) == ARRAY_REF
- && tree_fits_shwi_p (TREE_OPERAND (format, 1))
- && (offset += tree_to_shwi (TREE_OPERAND (format, 1))) >= 0)
- format = TREE_OPERAND (format, 0);
-
- if (offset < 0)
- return NULL;
-
- tree array_init;
- tree array_size = NULL_TREE;
-
- if (VAR_P (format)
- && TREE_CODE (TREE_TYPE (format)) == ARRAY_TYPE
- && (array_init = decl_constant_value (format)) != format
- && TREE_CODE (array_init) == STRING_CST)
- {
- /* Extract the string constant initializer. Note that this may
- include a trailing NUL character that is not in the array (e.g.
- const char a[3] = "foo";). */
- array_size = DECL_SIZE_UNIT (format);
- format = array_init;
- }
-
- if (TREE_CODE (format) != STRING_CST)
- return NULL;
-
- tree type = TREE_TYPE (format);
-
- scalar_int_mode char_mode;
- if (!is_int_mode (TYPE_MODE (TREE_TYPE (type)), &char_mode)
- || GET_MODE_SIZE (char_mode) != 1)
- {
- /* Wide format string. */
- return NULL;
- }
-
- const char *fmtstr = TREE_STRING_POINTER (format);
- unsigned fmtlen = TREE_STRING_LENGTH (format);
-
- if (array_size)
- {
- /* Variable length arrays can't be initialized. */
- gcc_assert (TREE_CODE (array_size) == INTEGER_CST);
-
- if (tree_fits_shwi_p (array_size))
- {
- HOST_WIDE_INT array_size_value = tree_to_shwi (array_size);
- if (array_size_value > 0
- && array_size_value == (int) array_size_value
- && fmtlen > array_size_value)
- fmtlen = array_size_value;
- }
- }
- if (offset)
- {
- if (offset >= fmtlen)
- return NULL;
-
- fmtstr += offset;
- fmtlen -= offset;
- }
-
- if (fmtlen < 1 || fmtstr[--fmtlen] != 0)
- {
- /* FIXME: Diagnose an unterminated format string if it hasn't been
- diagnosed by -Wformat. Similarly to a null format pointer,
- -Wformay diagnoses only nul pointer constants, this pass can
- do better). */
- return NULL;
- }
-
- return fmtstr;
+ return c_getstr (format);
}
-/* The format_warning_at_substring function is not used here in a way
- that makes using attribute format viable. Suppress the warning. */
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
+/* For convenience and brevity, shorter named entrypoints of
+ format_string_diagnostic_t::emit_warning_va and
+ format_string_diagnostic_t::emit_warning_n_va.
+ These have to be functions with the attribute so that exgettext
+ works properly. */
-/* For convenience and brevity. */
+static bool
+ATTRIBUTE_GCC_DIAG (5, 6)
+fmtwarn (const substring_loc &fmt_loc, location_t param_loc,
+ const char *corrected_substring, int opt, const char *gmsgid, ...)
+{
+ format_string_diagnostic_t diag (fmt_loc, NULL, param_loc, NULL,
+ corrected_substring);
+ va_list ap;
+ va_start (ap, gmsgid);
+ bool warned = diag.emit_warning_va (opt, gmsgid, &ap);
+ va_end (ap);
+
+ return warned;
+}
static bool
- (* const fmtwarn) (const substring_loc &, location_t,
- const char *, int, const char *, ...)
- = format_warning_at_substring;
+ATTRIBUTE_GCC_DIAG (6, 8) ATTRIBUTE_GCC_DIAG (7, 8)
+fmtwarn_n (const substring_loc &fmt_loc, location_t param_loc,
+ const char *corrected_substring, int opt, unsigned HOST_WIDE_INT n,
+ const char *singular_gmsgid, const char *plural_gmsgid, ...)
+{
+ format_string_diagnostic_t diag (fmt_loc, NULL, param_loc, NULL,
+ corrected_substring);
+ va_list ap;
+ va_start (ap, plural_gmsgid);
+ bool warned = diag.emit_warning_n_va (opt, n, singular_gmsgid, plural_gmsgid,
+ &ap);
+ va_end (ap);
+
+ return warned;
+}
/* Format length modifiers. */
/* Description of the result of conversion either of a single directive
or the whole format string. */
-struct fmtresult
+class fmtresult
{
+public:
/* Construct a FMTRESULT object with all counters initialized
to MIN. KNOWNRANGE is set when MIN is valid. */
fmtresult (unsigned HOST_WIDE_INT min = HOST_WIDE_INT_MAX)
- : argmin (), argmax (),
+ : argmin (), argmax (), nonstr (),
knownrange (min < HOST_WIDE_INT_MAX),
- nullp ()
+ mayfail (), nullp ()
{
range.min = min;
range.max = min;
KNOWNRANGE is set when both MIN and MAX are valid. */
fmtresult (unsigned HOST_WIDE_INT min, unsigned HOST_WIDE_INT max,
unsigned HOST_WIDE_INT likely = HOST_WIDE_INT_MAX)
- : argmin (), argmax (),
+ : argmin (), argmax (), nonstr (),
knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
- nullp ()
+ mayfail (), nullp ()
{
range.min = min;
range.max = max;
results in on output for an argument in the range above. */
result_range range;
+ /* Non-nul when the argument of a string directive is not a nul
+ terminated string. */
+ tree nonstr;
+
/* True when the range above is obtained from a known value of
a directive's argument or its bounds and not the result of
heuristics that depend on warning levels. */
bool knownrange;
+ /* True for a directive that may fail (such as wide character
+ directives). */
+ bool mayfail;
+
/* True when the argument is a null pointer. */
bool nullp;
};
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;
+ switch (base)
+ {
+ case 8:
+ return (prec + 2) / 3;
+ case 10:
+ /* Decimal approximation: yields 3, 5, 10, and 20 for precision
+ of 8, 16, 32, and 64 bits. */
+ return prec * 301 / 1000 + 1;
+ case 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;
+ gcc_unreachable ();
}
static bool
{
return bounded ? OPT_Wformat_truncation_ : OPT_Wformat_overflow_;
}
+
+ /* Return true for calls to file formatted functions. */
+ bool is_file_func () const
+ {
+ return (fncode == BUILT_IN_FPRINTF
+ || fncode == BUILT_IN_FPRINTF_CHK
+ || fncode == BUILT_IN_FPRINTF_UNLOCKED
+ || fncode == BUILT_IN_VFPRINTF
+ || fncode == BUILT_IN_VFPRINTF_CHK);
+ }
+
+ /* Return true for calls to string formatted functions. */
+ bool is_string_func () const
+ {
+ return (fncode == BUILT_IN_SPRINTF
+ || fncode == BUILT_IN_SPRINTF_CHK
+ || fncode == BUILT_IN_SNPRINTF
+ || fncode == BUILT_IN_SNPRINTF_CHK
+ || fncode == BUILT_IN_VSPRINTF
+ || fncode == BUILT_IN_VSPRINTF_CHK
+ || fncode == BUILT_IN_VSNPRINTF
+ || fncode == BUILT_IN_VSNPRINTF_CHK);
+ }
};
/* Return the result of formatting a no-op directive (such as '%n'). */
for (int i = 0; i < NUM_INT_N_ENTS; i++)
if (int_n_enabled_p[i])
{
- char name[50];
+ char name[50], altname[50];
sprintf (name, "__int%d unsigned", int_n_data[i].bitsize);
+ sprintf (altname, "__int%d__ unsigned", int_n_data[i].bitsize);
- if (strcmp (name, UINTMAX_TYPE) == 0)
+ if (strcmp (name, UINTMAX_TYPE) == 0
+ || strcmp (altname, UINTMAX_TYPE) == 0)
{
*pintmax = int_n_trees[i].signed_type;
*puintmax = int_n_trees[i].unsigned_type;
&& TYPE_PRECISION (argtype) <= TYPE_PRECISION (type))
{
/* Try to determine the range of values of the integer argument. */
- value_range *vr = vr_values->get_value_range (arg);
- if (vr->type == VR_RANGE
- && TREE_CODE (vr->min) == INTEGER_CST
- && TREE_CODE (vr->max) == INTEGER_CST)
+ const value_range *vr = vr_values->get_value_range (arg);
+ if (range_int_cst_p (vr))
{
HOST_WIDE_INT type_min
= (TYPE_UNSIGNED (argtype)
HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (argtype));
- *pmin = TREE_INT_CST_LOW (vr->min);
- *pmax = TREE_INT_CST_LOW (vr->max);
+ *pmin = TREE_INT_CST_LOW (vr->min ());
+ *pmax = TREE_INT_CST_LOW (vr->max ());
if (*pmin < *pmax)
{
{
/* Try to determine the range of values of the integer argument
(range information is not available for pointers). */
- value_range *vr = vr_values->get_value_range (arg);
- if (vr->type == VR_RANGE
- && TREE_CODE (vr->min) == INTEGER_CST
- && TREE_CODE (vr->max) == INTEGER_CST)
+ const value_range *vr = vr_values->get_value_range (arg);
+ if (range_int_cst_p (vr))
{
- argmin = vr->min;
- argmax = vr->max;
+ argmin = vr->min ();
+ argmax = vr->max ();
/* Set KNOWNRANGE if the argument is in a known subrange
of the directive's type and neither width nor precision
res.argmin = argmin;
res.argmax = argmax;
}
- else if (vr->type == VR_ANTI_RANGE)
+ else if (vr->kind () == VR_ANTI_RANGE)
{
/* Handle anti-ranges if/when bug 71690 is resolved. */
}
- else if (vr->type == VR_VARYING
- || vr->type == VR_UNDEFINED)
+ else if (vr->varying_p () || vr->undefined_p ())
{
/* The argument here may be the result of promoting the actual
argument to int. Try to determine the type of the actual
unsigned flagmin = (1 /* for the first digit */
+ (dir.get_flag ('+') | dir.get_flag (' ')));
+ /* The minimum is 3 for "inf" and "nan" for all specifiers, plus 1
+ for the plus sign/space with the '+' and ' ' flags, respectively,
+ unless reduced below. */
+ res.range.min = 2 + flagmin;
+
/* When the pound flag is set the decimal point is included in output
regardless of precision. Whether or not a decimal point is included
otherwise depends on the specification and precision. */
else if (dir.prec[0] > 0)
minprec = dir.prec[0] + !radix /* decimal point */;
- res.range.min = (2 /* 0x */
- + flagmin
- + radix
- + minprec
- + 3 /* p+0 */);
+ res.range.likely = (2 /* 0x */
+ + flagmin
+ + radix
+ + minprec
+ + 3 /* p+0 */);
res.range.max = format_floating_max (type, 'a', prec[1]);
- res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
non-zero, decimal point. */
HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
- /* The minimum output is "[-+]1.234567e+00" regardless
+ /* The likely minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */
- res.range.min = (flagmin
- + radix
- + minprec
- + 2 /* e+ */ + 2);
+ res.range.likely = (flagmin
+ + radix
+ + minprec
+ + 2 /* e+ */ + 2);
res.range.max = format_floating_max (type, 'e', prec[1]);
- res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
decimal point. */
HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
- /* The lower bound when precision isn't specified is 8 bytes
- ("1.23456" since precision is taken to be 6). When precision
- is zero, the lower bound is 1 byte (e.g., "1"). Otherwise,
- when precision is greater than zero, then the lower bound
- is 2 plus precision (plus flags). */
- res.range.min = flagmin + radix + minprec;
+ /* For finite numbers (i.e., not infinity or NaN) the lower bound
+ when precision isn't specified is 8 bytes ("1.23456" since
+ precision is taken to be 6). When precision is zero, the lower
+ bound is 1 byte (e.g., "1"). Otherwise, when precision is greater
+ than zero, then the lower bound is 2 plus precision (plus flags).
+ But in all cases, the lower bound is no greater than 3. */
+ unsigned HOST_WIDE_INT min = flagmin + radix + minprec;
+ if (min < res.range.min)
+ res.range.min = min;
/* Compute the upper bound for -TYPE_MAX. */
res.range.max = format_floating_max (type, 'f', prec[1]);
if (dir.prec[0] < 0 && dir.prec[1] > 0)
res.range.likely = 3;
else
- res.range.likely = res.range.min;
+ res.range.likely = min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
the lower bound on the range of bytes (not counting flags
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;
+ unsigned HOST_WIDE_INT min = flagmin + radix;
+ if (min < res.range.min)
+ res.range.min = min;
char spec = 'g';
HOST_WIDE_INT maxprec = dir.prec[1];
const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)));
+ if (!real_isfinite (rvp))
+ {
+ /* The format for Infinity and NaN is "[-]inf"/"[-]infinity"
+ and "[-]nan" with the choice being implementation-defined
+ but not locale dependent. */
+ bool sign = dir.get_flag ('+') || real_isneg (rvp);
+ res.range.min = 3 + sign;
+
+ res.range.likely = res.range.min;
+ res.range.max = res.range.min;
+ /* The unlikely maximum is "[-/+]infinity" or "[-/+][qs]nan".
+ For NaN, the C/POSIX standards specify two formats:
+ "[-/+]nan"
+ and
+ "[-/+]nan(n-char-sequence)"
+ No known printf implementation outputs the latter format but AIX
+ outputs QNaN and SNaN for quiet and signalling NaN, respectively,
+ so the unlikely maximum reflects that. */
+ res.range.unlikely = sign + (real_isinf (rvp) ? 8 : 4);
+
+ /* The range for infinity and NaN is known unless either width
+ or precision is unknown. Width has the same effect regardless
+ of whether the argument is finite. Precision is either ignored
+ (e.g., Glibc) or can have an effect on the short vs long format
+ such as inf/infinity (e.g., Solaris). */
+ res.knownrange = dir.known_width_and_precision ();
+
+ /* Adjust the range for width but ignore precision. */
+ res.adjust_for_width_or_precision (dir.width);
+
+ return res;
+ }
+
char fmtstr [40];
char *pfmt = fmtstr;
Used by the format_string function below. */
static fmtresult
-get_string_length (tree str)
+get_string_length (tree str, unsigned eltsize)
{
if (!str)
return fmtresult ();
- if (tree slen = c_strlen (str, 1))
- {
- /* Simply return the length of the string. */
- fmtresult res (tree_to_shwi (slen));
- return res;
- }
-
/* Determine the length of the shortest and longest string referenced
by STR. Strings of unknown lengths are bounded by the sizes of
arrays that subexpressions of STR may refer to. Pointers that
- aren't known to point any such arrays result in LENRANGE[1] set
- to SIZE_MAX. */
- tree lenrange[2];
- bool flexarray = get_range_strlen (str, lenrange);
-
- if (lenrange [0] || lenrange [1])
- {
- HOST_WIDE_INT min
- = (tree_fits_uhwi_p (lenrange[0])
- ? tree_to_uhwi (lenrange[0])
- : 0);
-
- HOST_WIDE_INT max
- = (tree_fits_uhwi_p (lenrange[1])
- ? tree_to_uhwi (lenrange[1])
- : HOST_WIDE_INT_M1U);
-
- /* 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
- actual length but perhaps by their maximum possible length). */
- if (res.range.max < target_int_max ())
- {
- 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 (it can be zero or excessive)
- set the likely length to the greater of 1 and the length of
- 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;
- }
-
- /* If the range of string length has been estimated from the size
- of an array at the end of a struct assume that it's longer than
- the array bound says it is in case it's used as a poor man's
- flexible array member, such as in struct S { char a[4]; }; */
- res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max;
+ aren't known to point any such arrays result in LENDATA.MAXLEN
+ set to SIZE_MAX. */
+ c_strlen_data lendata = { };
+ get_range_strlen (str, &lendata, eltsize);
+
+ /* Return the default result when nothing is known about the string. */
+ if (integer_all_onesp (lendata.maxbound)
+ && integer_all_onesp (lendata.maxlen))
+ return fmtresult ();
- return res;
+ HOST_WIDE_INT min
+ = (tree_fits_uhwi_p (lendata.minlen)
+ ? tree_to_uhwi (lendata.minlen)
+ : 0);
+
+ HOST_WIDE_INT max
+ = (tree_fits_uhwi_p (lendata.maxbound)
+ ? tree_to_uhwi (lendata.maxbound)
+ : HOST_WIDE_INT_M1U);
+
+ const bool unbounded = integer_all_onesp (lendata.maxlen);
+
+ /* Set the max/likely counters to unbounded when a minimum is known
+ but the maximum length isn't bounded. This implies that STR is
+ a conditional expression involving a string of known length and
+ and an expression of unknown/unbounded length. */
+ if (min
+ && (unsigned HOST_WIDE_INT)min < HOST_WIDE_INT_M1U
+ && unbounded)
+ max = HOST_WIDE_INT_M1U;
+
+ /* 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);
+ res.nonstr = lendata.decl;
+
+ /* Set RES.KNOWNRANGE to true if and only if all strings referenced
+ by STR are known to be bounded (though not necessarily by their
+ actual length but perhaps by their maximum possible length). */
+ if (res.range.max < target_int_max ())
+ {
+ 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 (it can be zero or excessive)
+ set the likely length to the greater of 1 and the length of
+ 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;
}
- return get_string_length (NULL_TREE);
+ res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
+
+ return res;
}
/* Return the minimum and maximum number of characters formatted
res.knownrange = true;
- if (dir.modifier == FMT_LEN_l)
+ if (dir.specifier == 'C'
+ || dir.modifier == FMT_LEN_l)
{
/* A wide character can result in as few as zero bytes. */
res.range.min = 0;
res.range.likely = 0;
res.range.unlikely = 0;
}
- else if (min > 0 && min < 128)
+ else if (min >= 0 && min < 128)
{
+ /* Be conservative if the target execution character set
+ is not a 1-to-1 mapping to the source character set or
+ if the source set is not ASCII. */
+ bool one_2_one_ascii
+ = (target_to_host_charmap[0] == 1 && target_to_host ('a') == 97);
+
/* 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.max = one_2_one_ascii ? 1 : target_mb_len_max ();;
res.range.likely = 1;
res.range.unlikely = target_mb_len_max ();
+ res.mayfail = !one_2_one_ascii;
}
else
{
res.range.max = target_mb_len_max ();
res.range.likely = 2;
res.range.unlikely = res.range.max;
+ /* Converting such a character may fail. */
+ res.mayfail = true;
}
}
else
res.range.max = target_mb_len_max ();
res.range.likely = 2;
res.range.unlikely = res.range.max;
+ res.mayfail = true;
}
}
else
fmtresult res;
/* Compute the range the argument's length can be in. */
- fmtresult slen = get_string_length (arg);
+ int count_by = 1;
+ if (dir.specifier == 'S' || dir.modifier == FMT_LEN_l)
+ {
+ /* Get a node for a C type that will be the same size
+ as a wchar_t on the target. */
+ tree node = get_typenode_from_name (MODIFIED_WCHAR_TYPE);
+
+ /* Now that we have a suitable node, get the number of
+ bytes it occupies. */
+ count_by = int_size_in_bytes (node);
+ gcc_checking_assert (count_by == 2 || count_by == 4);
+ }
+
+ fmtresult slen = get_string_length (arg, count_by);
if (slen.range.min == slen.range.max
&& slen.range.min < HOST_WIDE_INT_MAX)
{
/* A '%s' directive with a string argument with constant length. */
res.range = slen.range;
- if (dir.modifier == FMT_LEN_l)
+ if (dir.specifier == 'S'
+ || dir.modifier == FMT_LEN_l)
{
/* In the worst case the length of output of a wide string S
is bounded by MB_LEN_MAX * wcslen (S). */
/* Even a non-empty wide character string need not convert into
any bytes. */
res.range.min = 0;
+
+ /* A non-empty wide character conversion may fail. */
+ if (slen.range.max > 0)
+ res.mayfail = true;
}
else
{
at level 2. This result is adjust upward for width (if it's
specified). */
- if (dir.modifier == FMT_LEN_l)
+ if (dir.specifier == 'S'
+ || dir.modifier == FMT_LEN_l)
{
/* A wide character converts to as few as zero bytes. */
slen.range.min = 0;
if (slen.range.likely < target_int_max ())
slen.range.unlikely *= target_mb_len_max ();
+
+ /* A non-empty wide character conversion may fail. */
+ if (slen.range.max > 0)
+ res.mayfail = true;
}
res.range = slen.range;
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;
+ if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.unlikely)
+ res.range.unlikely = dir.prec[1];
}
else if (slen.range.min >= target_int_max ())
{
empty, while at level 1 they are assumed to be one byte
long. */
res.range.likely = warn_level > 1;
+ res.range.unlikely = HOST_WIDE_INT_MAX;
}
else
{
if (res.range.likely >= target_int_max ())
res.range.likely = warn_level > 1;
}
-
- res.range.unlikely = res.range.max;
}
+ /* If the argument isn't a nul-terminated string and the number
+ of bytes on output isn't bounded by precision, set NONSTR. */
+ if (slen.nonstr && slen.range.min < (unsigned HOST_WIDE_INT)dir.prec[0])
+ res.nonstr = slen.nonstr;
+
/* Bump up the byte counters if WIDTH is greater. */
return res.adjust_for_width_or_precision (dir.width);
}
/* This is the terminating nul. */
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, UNKNOWN_LOCATION, NULL, info.warnopt (),
- fmtstr, info.func);
+ 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")),
+ 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 "
- "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")));
- return fmtwarn (dirloc, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- res.min, navail);
+ const char *d = target_to_host (hostdir, sizeof hostdir, dir.beg);
+ if (!info.bounded)
+ return fmtwarn_n (dirloc, argloc, NULL, info.warnopt (), res.min,
+ "%<%.*s%> directive writing %wu byte into a "
+ "region of size %wu",
+ "%<%.*s%> directive writing %wu bytes into a "
+ "region of size %wu",
+ (int) dir.len, d, res.min, navail);
+ else if (maybe)
+ return fmtwarn_n (dirloc, argloc, NULL, info.warnopt (), res.min,
+ "%<%.*s%> directive output may be truncated "
+ "writing %wu byte into a region of size %wu",
+ "%<%.*s%> directive output may be truncated "
+ "writing %wu bytes into a region of size %wu",
+ (int) dir.len, d, res.min, navail);
+ else
+ return fmtwarn_n (dirloc, argloc, NULL, info.warnopt (), res.min,
+ "%<%.*s%> directive output truncated writing "
+ "%wu byte into a region of size %wu",
+ "%<%.*s%> directive output truncated writing "
+ "%wu bytes into a region of size %wu",
+ (int) dir.len, d, res.min, navail);
}
-
if (res.min == 0 && 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 %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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- res.max, navail);
- }
+ return fmtwarn (dirloc, argloc, NULL,
+ info.warnopt (),
+ 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"), (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ res.max, navail);
if (res.min == 0 && 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- res.likely, navail);
- }
+ /* This is a special case to avoid issuing the potentially
+ confusing warning:
+ writing 0 or more bytes into a region of size 0. */
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"), (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"),
+ (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ res.min, res.max, navail);
+
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"), (int) dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, navail);
}
{
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, UNKNOWN_LOCATION, NULL, info.warnopt (), fmtstr,
- info.func);
+ return fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (),
+ 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")), 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- res.min, avail_range.min, avail_range.max);
+ const char *d = target_to_host (hostdir, sizeof hostdir, dir.beg);
+ if (!info.bounded)
+ return fmtwarn_n (dirloc, argloc, NULL, info.warnopt (), res.min,
+ "%<%.*s%> directive writing %wu byte into a region "
+ "of size between %wu and %wu",
+ "%<%.*s%> directive writing %wu bytes into a region "
+ "of size between %wu and %wu", (int) dir.len, d,
+ res.min, avail_range.min, avail_range.max);
+ else if (maybe)
+ return fmtwarn_n (dirloc, argloc, NULL, info.warnopt (), res.min,
+ "%<%.*s%> directive output may be truncated writing "
+ "%wu byte into a region of size between %wu and %wu",
+ "%<%.*s%> directive output may be truncated writing "
+ "%wu bytes into a region of size between %wu and "
+ "%wu", (int) dir.len, d, res.min, avail_range.min,
+ avail_range.max);
+ else
+ return fmtwarn_n (dirloc, argloc, NULL, info.warnopt (), res.min,
+ "%<%.*s%> directive output truncated writing %wu "
+ "byte into a region of size between %wu and %wu",
+ "%<%.*s%> directive output truncated writing %wu "
+ "bytes into a region of size between %wu and %wu",
+ (int) dir.len, d, res.min, avail_range.min,
+ avail_range.max);
}
if (res.min == 0 && 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- res.max, avail_range.min, avail_range.max);
- }
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"),
+ (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ res.max, avail_range.min, avail_range.max);
if (res.min == 0 && 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- res.likely, avail_range.min, avail_range.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. */
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"),
+ (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
- target_to_host (hostdir, sizeof hostdir, 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, argloc, NULL,
- info.warnopt (), fmtstr, dir.len,
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"), (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ res.min, res.max, avail_range.min, avail_range.max);
+
+ return fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ 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"),
+ (int) dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, avail_range.min, avail_range.max);
}
if (fmtres.nullp)
{
fmtwarn (dirloc, argloc, NULL, info.warnopt (),
- "%<%.*s%> directive argument is null",
- dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg));
+ "%G%<%.*s%> directive argument is null",
+ info.callstmt, dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg));
/* Don't bother processing the rest of the format string. */
res->warned = true;
of 4095 bytes required to be supported? */
bool minunder4k = fmtres.range.min < 4096;
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
+ /* Clear POSUNDER4K in the overall result if the maximum has exceeded
+ the 4k (this is necessary to avoid the return value optimization
that may not be safe in the maximum case). */
if (!maxunder4k)
- res->under4k = false;
+ res->posunder4k = false;
+ /* Also clear POSUNDER4K if the directive may fail. */
+ if (fmtres.mayfail)
+ res->posunder4k = false;
if (!warned
/* Only warn at level 2. */
&& warn_level > 1
+ /* Only warn for string functions. */
+ && info.is_string_func ()
&& (!minunder4k
|| (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX)))
{
of C11. Warn on this only at level 2 but remember this and
prevent folding the return value when done. This allows for
the possibility of the actual libc call failing due to ENOMEM
- (like Glibc does under some conditions). */
+ (like Glibc does with very large precision or width).
+ Issue the "may exceed" warning only for string functions and
+ not for fprintf or printf. */
if (fmtres.range.min == fmtres.range.max)
- warned = fmtwarn (dirloc, argloc, NULL,
- info.warnopt (),
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
"%<%.*s%> directive output of %wu bytes exceeds "
- "minimum required size of 4095",
- dirlen,
+ "minimum required size of 4095", dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min);
- else
- {
- const char *fmtstr
- = (minunder4k
- ? G_("%<%.*s%> directive output between %wu and %wu "
- "bytes may exceed minimum required size of 4095")
- : G_("%<%.*s%> directive output between %wu and %wu "
- "bytes exceeds minimum required size of 4095"));
-
- warned = fmtwarn (dirloc, argloc, NULL,
- info.warnopt (), fmtstr, dirlen,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- fmtres.range.min, fmtres.range.max);
- }
+ else if (!minunder4k)
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output between %wu and %wu "
+ "bytes exceeds minimum required size of 4095",
+ dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min, fmtres.range.max);
+ else if (!info.retval_used () && info.is_string_func ())
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output between %wu and %wu "
+ "bytes may exceed minimum required size of "
+ "4095",
+ dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min, fmtres.range.max);
}
/* Has the likely and maximum directive output exceeded INT_MAX? */
&& maxximax
&& fmtres.range.max < HOST_WIDE_INT_MAX)))
{
- /* The directive output causes the total length of output
- to exceed INT_MAX bytes. */
-
- if (fmtres.range.min == fmtres.range.max)
- warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
- "%<%.*s%> directive output of %wu bytes causes "
- "result to exceed %<INT_MAX%>",
- dirlen,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- fmtres.range.min);
- else
+ if (fmtres.range.min > target_int_max ())
+ {
+ /* The directive output exceeds INT_MAX bytes. */
+ if (fmtres.range.min == fmtres.range.max)
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output of %wu bytes exceeds "
+ "%<INT_MAX%>", dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min);
+ else
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output between %wu and "
+ "%wu bytes exceeds %<INT_MAX%>", dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min, fmtres.range.max);
+ }
+ else if (res->range.min > target_int_max ())
{
- const char *fmtstr
- = (fmtres.range.min > target_int_max ()
- ? G_ ("%<%.*s%> directive output between %wu and %wu "
- "bytes causes result to exceed %<INT_MAX%>")
- : G_ ("%<%.*s%> directive output between %wu and %wu "
- "bytes may cause result to exceed %<INT_MAX%>"));
- warned = fmtwarn (dirloc, argloc, NULL,
- info.warnopt (), fmtstr, dirlen,
- target_to_host (hostdir, sizeof hostdir, dir.beg),
- fmtres.range.min, fmtres.range.max);
+ /* The directive output is under INT_MAX but causes the result
+ to exceed INT_MAX bytes. */
+ if (fmtres.range.min == fmtres.range.max)
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output of %wu bytes causes "
+ "result to exceed %<INT_MAX%>", dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min);
+ else
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output between %wu and "
+ "%wu bytes causes result to exceed %<INT_MAX%>",
+ dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min, fmtres.range.max);
}
+ else if ((!info.retval_used () || !info.bounded)
+ && (info.is_string_func ()))
+ /* Warn for calls to string functions that either aren't bounded
+ (sprintf) or whose return value isn't used. */
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive output between %wu and "
+ "%wu bytes may cause result to exceed "
+ "%<INT_MAX%>", dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg),
+ fmtres.range.min, fmtres.range.max);
+ }
+
+ if (!warned && fmtres.nonstr)
+ {
+ warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+ "%<%.*s%> directive argument is not a nul-terminated "
+ "string",
+ dirlen,
+ target_to_host (hostdir, sizeof hostdir, dir.beg));
+ if (warned && DECL_P (fmtres.nonstr))
+ inform (DECL_SOURCE_LOCATION (fmtres.nonstr),
+ "referenced argument declared here");
+ return false;
}
if (warned && fmtres.range.min < fmtres.range.likely
&& fmtres.range.likely < fmtres.range.max)
- /* Some languages have special plural rules even for large values,
- but it is periodic with period of 10, 100, 1000 etc. */
- inform_n (info.fmtloc,
- fmtres.range.likely > INT_MAX
- ? (fmtres.range.likely % 1000000) + 1000000
- : fmtres.range.likely,
+ inform_n (info.fmtloc, fmtres.range.likely,
"assuming directive output of %wu byte",
"assuming directive output of %wu bytes",
fmtres.range.likely);
res->warned |= warned;
- if (!dir.beg[0] && res->warned && info.objsize < HOST_WIDE_INT_MAX)
+ if (!dir.beg[0] && res->warned)
{
- /* If a warning has been issued for buffer overflow or truncation
- (but not otherwise) help the user figure out how big a buffer
- they need. */
-
location_t callloc = gimple_location (info.callstmt);
unsigned HOST_WIDE_INT min = res->range.min;
unsigned HOST_WIDE_INT max = res->range.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,
- "%qE output between %wu and %wu bytes into "
- "a destination of size %wu",
- 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
- inform (callloc,
- "%qE output %wu or more bytes into a destination of size %wu",
- info.func, min, info.objsize);
+ if (info.objsize < HOST_WIDE_INT_MAX)
+ {
+ /* If a warning has been issued for buffer overflow or truncation
+ help the user figure out how big a buffer they need. */
+
+ if (min == max)
+ inform_n (callloc, min,
+ "%qE output %wu byte into a destination of size %wu",
+ "%qE output %wu bytes into a destination of size %wu",
+ info.func, min, info.objsize);
+ else if (max < HOST_WIDE_INT_MAX)
+ inform (callloc,
+ "%qE output between %wu and %wu bytes into "
+ "a destination of size %wu",
+ 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
+ inform (callloc,
+ "%qE output %wu or more bytes into a destination of size "
+ "%wu",
+ info.func, min, info.objsize);
+ }
+ else if (!info.is_string_func ())
+ {
+ /* If the warning is for a file function function like fprintf
+ of printf with no destination size just print the computed
+ result. */
+ if (min == max)
+ inform_n (callloc, min,
+ "%qE output %wu byte", "%qE output %wu bytes",
+ info.func, min);
+ else if (max < HOST_WIDE_INT_MAX)
+ inform (callloc,
+ "%qE output between %wu and %wu bytes",
+ info.func, min, max);
+ else if (min < res->range.likely && res->range.likely < max)
+ inform (callloc,
+ "%qE output %wu or more bytes (assuming %wu)",
+ info.func, min, res->range.likely);
+ else
+ inform (callloc,
+ "%qE output %wu or more bytes",
+ info.func, min);
+ }
}
if (dump_file && *dir.beg)
return true;
}
-#pragma GCC diagnostic pop
-
/* Parse a format directive in function call described by INFO starting
at STR and populate DIR structure. Bump up *ARGNO by the number of
arguments extracted for the directive. Return the length of
width and sort it out later after the next character has
been seen. */
pwidth = pf;
- width = target_strtol10 (&pf, &werange);
+ width = target_strtowi (&pf, &werange);
}
else if (target_to_host (*pf) == '*')
{
{
werange = 0;
pwidth = pf;
- width = target_strtol10 (&pf, &werange);
+ width = target_strtowi (&pf, &werange);
}
else if (target_to_host (*pf) == '*')
{
if (ISDIGIT (target_to_host (*pf)))
{
pprec = pf;
- precision = target_strtol10 (&pf, &perange);
+ precision = target_strtowi (&pf, &perange);
}
else if (target_to_host (*pf) == '*')
{
dir.fmtfunc = format_none;
break;
+ case 'C':
case 'c':
+ /* POSIX wide character and C/POSIX narrow character. */
dir.fmtfunc = format_character;
break;
case 'S':
case 's':
+ /* POSIX wide string and C/POSIX narrow character string. */
dir.fmtfunc = format_string;
break;
}
else
{
- if (width == LONG_MAX && werange)
+ if (width == HOST_WIDE_INT_MAX && werange)
{
size_t begin = dir.beg - info.fmtstr + (pwidth - pcnt);
size_t caret = begin + (werange - pcnt);
substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
caret, begin, end);
- fmtwarn (dirloc, UNKNOWN_LOCATION, NULL,
- info.warnopt (), "%<%.*s%> directive width out of range",
- dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
+ fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (),
+ "%<%.*s%> directive width out of range", (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, dir.beg));
}
dir.set_width (width);
}
else
{
- if (precision == LONG_MAX && perange)
+ if (precision == HOST_WIDE_INT_MAX && perange)
{
size_t begin = dir.beg - info.fmtstr + (pprec - pcnt) - 1;
size_t caret = dir.beg - info.fmtstr + (perange - pcnt) - 1;
substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
caret, begin, end);
- fmtwarn (dirloc, UNKNOWN_LOCATION, NULL,
- info.warnopt (), "%<%.*s%> directive precision out of range",
- dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
+ fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (),
+ "%<%.*s%> directive precision out of range", (int) dir.len,
+ target_to_host (hostdir, sizeof hostdir, dir.beg));
}
dir.set_precision (precision);
res->range.min = res->range.max = 0;
/* 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
- 4K bytes) until determined otherwise. */
+ by the known range [0, 0] (with no conversion resulting in a failure
+ or producing more than 4K bytes) until determined otherwise. */
res->knownrange = true;
- res->under4k = true;
res->floating = false;
res->warned = false;
}
/* Return the size of the object referenced by the expression DEST if
- available, or -1 otherwise. */
+ available, or the maximum possible size otherwise. */
static unsigned HOST_WIDE_INT
get_destination_size (tree dest)
{
+ /* When there is no destination return the maximum. */
+ if (!dest)
+ return HOST_WIDE_INT_MAX;
+
/* Initialize object size info before trying to compute it. */
init_object_sizes ();
if (compute_builtin_object_size (dest, ost, &size))
return size;
- return HOST_WIDE_INT_M1U;
+ return HOST_WIDE_INT_MAX;
}
/* Return true if the call described by INFO with result RES safe to
const format_result &res, bool under4k,
unsigned HOST_WIDE_INT retval[2])
{
- if (under4k && !res.under4k)
+ if (under4k && !res.posunder4k)
return false;
/* The minimum return value. */
are badly declared. */
&& !stmt_ends_bb_p (info.callstmt))
{
- tree cst = build_int_cst (integer_type_node, retval[0]);
+ tree cst = build_int_cst (lhs ? TREE_TYPE (lhs) : integer_type_node,
+ retval[0]);
- if (lhs == NULL_TREE
- && info.nowrite)
+ if (lhs == NULL_TREE && info.nowrite)
{
/* Remove the call to the bounded function with a zero size
(e.g., snprintf(0, 0, "%i", 123)) if there is no lhs. */
}
}
}
- else if (lhs)
+ else if (lhs && types_compatible_p (TREE_TYPE (lhs), integer_type_node))
{
bool setrange = false;
return false;
}
+/* Return the zero-based index of the format string argument of a printf
+ like function and set *IDX_ARGS to the first format argument. When
+ no such index exists return UINT_MAX. */
+
+static unsigned
+get_user_idx_format (tree fndecl, unsigned *idx_args)
+{
+ tree attrs = lookup_attribute ("format", DECL_ATTRIBUTES (fndecl));
+ if (!attrs)
+ attrs = lookup_attribute ("format", TYPE_ATTRIBUTES (TREE_TYPE (fndecl)));
+
+ if (!attrs)
+ return UINT_MAX;
+
+ attrs = TREE_VALUE (attrs);
+
+ tree archetype = TREE_VALUE (attrs);
+ if (strcmp ("printf", IDENTIFIER_POINTER (archetype)))
+ return UINT_MAX;
+
+ attrs = TREE_CHAIN (attrs);
+ tree fmtarg = TREE_VALUE (attrs);
+
+ attrs = TREE_CHAIN (attrs);
+ tree elliparg = TREE_VALUE (attrs);
+
+ /* Attribute argument indices are 1-based but we use zero-based. */
+ *idx_args = tree_to_uhwi (elliparg) - 1;
+ return tree_to_uhwi (fmtarg) - 1;
+}
+
/* Determine if a GIMPLE CALL is to one of the sprintf-like built-in
functions and if so, handle it. Return true if the call is removed
and gsi_next should not be performed in the caller. */
call_info info = call_info ();
info.callstmt = gsi_stmt (*gsi);
- if (!gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
+ info.func = gimple_call_fndecl (info.callstmt);
+ if (!info.func)
return false;
- info.func = gimple_call_fndecl (info.callstmt);
- info.fncode = DECL_FUNCTION_CODE (info.func);
+ /* Format string argument number (valid for all functions). */
+ unsigned idx_format = UINT_MAX;
+ if (gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
+ info.fncode = DECL_FUNCTION_CODE (info.func);
+ else
+ {
+ unsigned idx_args;
+ idx_format = get_user_idx_format (info.func, &idx_args);
+ if (idx_format == UINT_MAX
+ || idx_format >= gimple_call_num_args (info.callstmt)
+ || idx_args > gimple_call_num_args (info.callstmt)
+ || !POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (info.callstmt,
+ idx_format))))
+ return false;
+ info.fncode = BUILT_IN_NONE;
+ info.argidx = idx_args;
+ }
/* The size of the destination as in snprintf(dest, size, ...). */
unsigned HOST_WIDE_INT dstsize = HOST_WIDE_INT_M1U;
/* The size of the destination determined by __builtin_object_size. */
unsigned HOST_WIDE_INT objsize = HOST_WIDE_INT_M1U;
- /* Buffer size argument number (snprintf and vsnprintf). */
- unsigned HOST_WIDE_INT idx_dstsize = HOST_WIDE_INT_M1U;
+ /* Zero-based buffer size argument number (snprintf and vsnprintf). */
+ unsigned idx_dstsize = UINT_MAX;
/* Object size argument number (snprintf_chk and vsnprintf_chk). */
- unsigned HOST_WIDE_INT idx_objsize = HOST_WIDE_INT_M1U;
+ unsigned idx_objsize = UINT_MAX;
- /* Format string argument number (valid for all functions). */
- unsigned idx_format;
+ /* Destinaton argument number (valid for sprintf functions only). */
+ unsigned idx_dstptr = 0;
switch (info.fncode)
{
+ case BUILT_IN_NONE:
+ // User-defined function with attribute format (printf).
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_FPRINTF:
+ // Signature:
+ // __builtin_fprintf (FILE*, format, ...)
+ idx_format = 1;
+ info.argidx = 2;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_FPRINTF_CHK:
+ // Signature:
+ // __builtin_fprintf_chk (FILE*, ost, format, ...)
+ idx_format = 2;
+ info.argidx = 3;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_FPRINTF_UNLOCKED:
+ // Signature:
+ // __builtin_fprintf_unnlocked (FILE*, format, ...)
+ idx_format = 1;
+ info.argidx = 2;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_PRINTF:
+ // Signature:
+ // __builtin_printf (format, ...)
+ idx_format = 0;
+ info.argidx = 1;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_PRINTF_CHK:
+ // Signature:
+ // __builtin_printf_chk (ost, format, ...)
+ idx_format = 1;
+ info.argidx = 2;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_PRINTF_UNLOCKED:
+ // Signature:
+ // __builtin_printf (format, ...)
+ idx_format = 0;
+ info.argidx = 1;
+ idx_dstptr = -1;
+ break;
+
case BUILT_IN_SPRINTF:
// Signature:
// __builtin_sprintf (dst, format, ...)
info.bounded = true;
break;
+ case BUILT_IN_VFPRINTF:
+ // Signature:
+ // __builtin_vprintf (FILE*, format, va_list)
+ idx_format = 1;
+ info.argidx = -1;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_VFPRINTF_CHK:
+ // Signature:
+ // __builtin___vfprintf_chk (FILE*, ost, format, va_list)
+ idx_format = 2;
+ info.argidx = -1;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_VPRINTF:
+ // Signature:
+ // __builtin_vprintf (format, va_list)
+ idx_format = 0;
+ info.argidx = -1;
+ idx_dstptr = -1;
+ break;
+
+ case BUILT_IN_VPRINTF_CHK:
+ // Signature:
+ // __builtin___vprintf_chk (ost, format, va_list)
+ idx_format = 1;
+ info.argidx = -1;
+ idx_dstptr = -1;
+ break;
+
case BUILT_IN_VSNPRINTF:
// Signature:
// __builtin_vsprintf (dst, size, format, va)
/* Set the global warning level for this function. */
warn_level = info.bounded ? warn_format_trunc : warn_format_overflow;
- /* The first argument is a pointer to the destination. */
- tree dstptr = gimple_call_arg (info.callstmt, 0);
+ /* For all string functions the first argument is a pointer to
+ the destination. */
+ tree dstptr = (idx_dstptr < gimple_call_num_args (info.callstmt)
+ ? gimple_call_arg (info.callstmt, 0) : NULL_TREE);
info.format = gimple_call_arg (info.callstmt, idx_format);
/* True when the destination size is constant as opposed to the lower
or upper bound of a range. */
bool dstsize_cst_p = true;
+ bool posunder4k = true;
- if (idx_dstsize == HOST_WIDE_INT_M1U)
+ if (idx_dstsize == UINT_MAX)
{
/* For non-bounded functions like sprintf, determine the size
of the destination from the object or pointer passed to it
/* Avoid warning if -Wstringop-overflow is specified since
it also warns for the same thing though only for the
checking built-ins. */
- if ((idx_objsize == HOST_WIDE_INT_M1U
+ if ((idx_objsize == UINT_MAX
|| !warn_stringop_overflow))
warning_at (gimple_location (info.callstmt), info.warnopt (),
"specified bound %wu exceeds maximum object size "
"%wu",
dstsize, target_size_max () / 2);
+ /* POSIX requires snprintf to fail if DSTSIZE is greater
+ than INT_MAX. Even though not all POSIX implementations
+ conform to the requirement, avoid folding in this case. */
+ posunder4k = false;
}
else if (dstsize > target_int_max ())
- warning_at (gimple_location (info.callstmt), info.warnopt (),
- "specified bound %wu exceeds %<INT_MAX%>",
- dstsize);
+ {
+ warning_at (gimple_location (info.callstmt), info.warnopt (),
+ "specified bound %wu exceeds %<INT_MAX%>",
+ dstsize);
+ /* POSIX requires snprintf to fail if DSTSIZE is greater
+ than INT_MAX. Avoid folding in that case. */
+ posunder4k = false;
+ }
}
else if (TREE_CODE (size) == SSA_NAME)
{
/* Try to determine the range of values of the argument
and use the greater of the two at level 1 and the smaller
of them at level 2. */
- value_range *vr = evrp_range_analyzer.get_value_range (size);
- if (vr->type == VR_RANGE
- && TREE_CODE (vr->min) == INTEGER_CST
- && TREE_CODE (vr->max) == INTEGER_CST)
- dstsize = (warn_level < 2
- ? TREE_INT_CST_LOW (vr->max)
- : TREE_INT_CST_LOW (vr->min));
+ const value_range *vr = evrp_range_analyzer.get_value_range (size);
+ if (range_int_cst_p (vr))
+ {
+ unsigned HOST_WIDE_INT minsize = TREE_INT_CST_LOW (vr->min ());
+ unsigned HOST_WIDE_INT maxsize = TREE_INT_CST_LOW (vr->max ());
+ dstsize = warn_level < 2 ? maxsize : minsize;
+
+ if (minsize > target_int_max ())
+ warning_at (gimple_location (info.callstmt), info.warnopt (),
+ "specified bound range [%wu, %wu] exceeds "
+ "%<INT_MAX%>",
+ minsize, maxsize);
+
+ /* POSIX requires snprintf to fail if DSTSIZE is greater
+ than INT_MAX. Avoid folding if that's possible. */
+ if (maxsize > target_int_max ())
+ posunder4k = false;
+ }
+ else if (vr->varying_p ())
+ {
+ /* POSIX requires snprintf to fail if DSTSIZE is greater
+ than INT_MAX. Since SIZE's range is unknown, avoid
+ folding. */
+ posunder4k = false;
+ }
/* The destination size is not constant. If the function is
bounded (e.g., snprintf) a lower bound of zero doesn't
}
}
- if (idx_objsize != HOST_WIDE_INT_M1U)
+ if (idx_objsize != UINT_MAX)
if (tree size = gimple_call_arg (info.callstmt, idx_objsize))
if (tree_fits_uhwi_p (size))
objsize = tree_to_uhwi (size);
/* For calls to non-bounded functions or to those of bounded
functions with a non-zero size, warn if the destination
pointer is null. */
- if (integer_zerop (dstptr))
+ if (dstptr && integer_zerop (dstptr))
{
/* This is diagnosed with -Wformat only when the null is a constant
pointer. The warning here diagnoses instances where the pointer
is not constant. */
location_t loc = gimple_location (info.callstmt);
warning_at (EXPR_LOC_OR_LOC (dstptr, loc),
- info.warnopt (), "null destination pointer");
+ info.warnopt (), "%Gnull destination pointer",
+ info.callstmt);
return false;
}
/* Avoid warning if -Wstringop-overflow is specified since
it also warns for the same thing though only for the
checking built-ins. */
- && (idx_objsize == HOST_WIDE_INT_M1U
+ && (idx_objsize == UINT_MAX
|| !warn_stringop_overflow))
{
warning_at (gimple_location (info.callstmt), info.warnopt (),
}
}
- if (integer_zerop (info.format))
+ /* Determine if the format argument may be null and warn if not
+ and if the argument is null. */
+ if (integer_zerop (info.format)
+ && gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
{
- /* This is diagnosed with -Wformat only when the null is a constant
- pointer. The warning here diagnoses instances where the pointer
- is not constant. */
location_t loc = gimple_location (info.callstmt);
warning_at (EXPR_LOC_OR_LOC (info.format, loc),
- info.warnopt (), "null format string");
+ info.warnopt (), "%Gnull format string",
+ info.callstmt);
return false;
}
including the terminating NUL. */
format_result res = format_result ();
+ /* I/O functions with no destination argument (i.e., all forms of fprintf
+ and printf) may fail under any conditions. Others (i.e., all forms of
+ sprintf) may only fail under specific conditions determined for each
+ directive. Clear POSUNDER4K for the former set of functions and set
+ it to true for the latter (it can only be cleared later, but it is
+ never set to true again). */
+ res.posunder4k = posunder4k && dstptr;
+
bool success = compute_format_length (info, &res);
+ if (res.warned)
+ gimple_set_no_warning (info.callstmt, true);
/* When optimizing and the printf return value optimization is enabled,
attempt to substitute the computed result for the return value of
init_target_to_host_charmap ();
calculate_dominance_info (CDI_DOMINATORS);
+ bool use_scev = optimize > 0 && flag_printf_return_value;
+ if (use_scev)
+ {
+ loop_optimizer_init (LOOPS_NORMAL);
+ scev_initialize ();
+ }
sprintf_dom_walker sprintf_dom_walker;
sprintf_dom_walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
+ if (use_scev)
+ {
+ scev_finalize ();
+ loop_optimizer_finalize ();
+ }
+
/* Clean up object size info. */
fini_object_sizes ();
return 0;