/* Format conversion function that given a directive and an argument
returns the formatting result. */
- fmtresult (*fmtfunc) (const directive &, tree, range_query *);
+ fmtresult (*fmtfunc) (const directive &, tree, pointer_query &);
/* Return True when the format flag CHR has been used. */
bool get_flag (char chr) const
/* Return the result of formatting a no-op directive (such as '%n'). */
static fmtresult
-format_none (const directive &, tree, range_query *)
+format_none (const directive &, tree, pointer_query &)
{
fmtresult res (0);
return res;
/* Return the result of formatting the '%%' directive. */
static fmtresult
-format_percent (const directive &, tree, range_query *)
+format_percent (const directive &, tree, pointer_query &)
{
fmtresult res (1);
return res;
used when the directive argument or its value isn't known. */
static fmtresult
-format_integer (const directive &dir, tree arg, range_query *query)
+format_integer (const directive &dir, tree arg, pointer_query &ptr_qry)
{
tree intmax_type_node;
tree uintmax_type_node;
/* Try to determine the range of values of the integer argument
(range information is not available for pointers). */
value_range vr;
- query->range_of_expr (vr, arg, dir.info->callstmt);
+ ptr_qry.rvals->range_of_expr (vr, arg, dir.info->callstmt);
if (!vr.varying_p () && !vr.undefined_p ())
{
if (code == INTEGER_CST)
{
arg = gimple_assign_rhs1 (def);
- return format_integer (dir, arg, query);
+ return format_integer (dir, arg, ptr_qry);
}
if (code == NOP_EXPR)
/* For unsigned conversions/directives or signed when
the minimum is positive, use the minimum and maximum to compute
the shortest and longest output, respectively. */
- res.range.min = format_integer (dir, argmin, query).range.min;
- res.range.max = format_integer (dir, argmax, query).range.max;
+ res.range.min = format_integer (dir, argmin, ptr_qry).range.min;
+ res.range.max = format_integer (dir, argmax, ptr_qry).range.max;
}
else if (tree_int_cst_sgn (argmax) < 0)
{
/* For signed conversions/directives if maximum is negative,
use the minimum as the longest output and maximum as the
shortest output. */
- res.range.min = format_integer (dir, argmax, query).range.min;
- res.range.max = format_integer (dir, argmin, query).range.max;
+ res.range.min = format_integer (dir, argmax, ptr_qry).range.min;
+ res.range.max = format_integer (dir, argmin, ptr_qry).range.max;
}
else
{
length of the output of both minimum and maximum and pick the
longer. */
unsigned HOST_WIDE_INT max1
- = format_integer (dir, argmin, query).range.max;
+ = format_integer (dir, argmin, ptr_qry).range.max;
unsigned HOST_WIDE_INT max2
- = format_integer (dir, argmax, query).range.max;
+ = format_integer (dir, argmax, ptr_qry).range.max;
res.range.min
- = format_integer (dir, integer_zero_node, query).range.min;
+ = format_integer (dir, integer_zero_node, ptr_qry).range.min;
res.range.max = MAX (max1, max2);
}
ARG. */
static fmtresult
-format_floating (const directive &dir, tree arg, range_query *)
+format_floating (const directive &dir, tree arg, pointer_query &)
{
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
tree type = (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll
static fmtresult
get_string_length (tree str, gimple *stmt, unsigned HOST_WIDE_INT max_size,
- unsigned eltsize, range_query *query)
+ unsigned eltsize, pointer_query &ptr_qry)
{
if (!str)
return fmtresult ();
c_strlen_data lendata = { };
lendata.maxbound = str;
if (eltsize == 1)
- get_range_strlen_dynamic (str, stmt, &lendata, query);
+ get_range_strlen_dynamic (str, stmt, &lendata, ptr_qry);
else
{
/* Determine the length of the shortest and longest string referenced
return res;
}
+ /* The minimum length of the string. */
HOST_WIDE_INT min
= (tree_fits_uhwi_p (lendata.minlen)
? tree_to_uhwi (lendata.minlen)
: 0);
+ /* The maximum length of the string; initially set to MAXBOUND which
+ may be less than MAXLEN, but may be adjusted up below. */
HOST_WIDE_INT max
= (lendata.maxbound && tree_fits_uhwi_p (lendata.maxbound)
? tree_to_uhwi (lendata.maxbound)
: HOST_WIDE_INT_M1U);
- const bool unbounded = integer_all_onesp (lendata.maxlen);
+ /* True if either the maximum length is unknown or (conservatively)
+ the array bound is less than the maximum length. That can happen
+ when the length of the string is unknown but the array in which
+ the string is stored is a member of a struct. The warning uses
+ the size of the member as the upper bound but the optimization
+ doesn't. The optimization could still use the size of
+ enclosing object as the upper bound but that's not done here. */
+ const bool unbounded
+ = (integer_all_onesp (lendata.maxlen)
+ || (lendata.maxbound
+ && tree_int_cst_lt (lendata.maxbound, 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
vsprinf). */
static fmtresult
-format_character (const directive &dir, tree arg, range_query *query)
+format_character (const directive &dir, tree arg, pointer_query &ptr_qry)
{
fmtresult res;
res.range.min = 0;
HOST_WIDE_INT min, max;
- if (get_int_range (arg, dir.info->callstmt, &min, &max, false, 0, query))
+ if (get_int_range (arg, dir.info->callstmt, &min, &max, false, 0,
+ ptr_qry.rvals))
{
if (min == 0 && max == 0)
{
vsprinf). */
static fmtresult
-format_string (const directive &dir, tree arg, range_query *query)
+format_string (const directive &dir, tree arg, pointer_query &ptr_qry)
{
fmtresult res;
}
fmtresult slen =
- get_string_length (arg, dir.info->callstmt, arg_size, count_by, query);
+ get_string_length (arg, dir.info->callstmt, arg_size, count_by, ptr_qry);
if (slen.range.min == slen.range.max
&& slen.range.min < HOST_WIDE_INT_MAX)
{
/* Format plain string (part of the format string itself). */
static fmtresult
-format_plain (const directive &dir, tree, range_query *)
+format_plain (const directive &dir, tree, pointer_query &)
{
fmtresult res (dir.len);
return res;
static bool
format_directive (const call_info &info,
format_result *res, const directive &dir,
- range_query *query)
+ pointer_query &ptr_qry)
{
/* Offset of the beginning of the directive from the beginning
of the format string. */
return false;
/* Compute the range of lengths of the formatted output. */
- fmtresult fmtres = dir.fmtfunc (dir, dir.arg, query);
+ fmtresult fmtres = dir.fmtfunc (dir, dir.arg, ptr_qry);
/* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are
that caused the processing to be terminated early). */
static bool
-compute_format_length (call_info &info, format_result *res, range_query *query)
+compute_format_length (call_info &info, format_result *res,
+ pointer_query &ptr_qry)
{
if (dump_file)
{
{
directive dir (&info, dirno);
- size_t n = parse_directive (info, dir, res, pf, &argno, query);
+ size_t n = parse_directive (info, dir, res, pf, &argno, ptr_qry.rvals);
/* Return failure if the format function fails. */
- if (!format_directive (info, res, dir, query))
+ if (!format_directive (info, res, dir, ptr_qry))
return false;
/* Return success when the directive is zero bytes long and it's
never set to true again). */
res.posunder4k = posunder4k && dstptr;
- bool success = compute_format_length (info, &res, ptr_qry.rvals);
+ bool success = compute_format_length (info, &res, ptr_qry);
if (res.warned)
suppress_warning (info.callstmt, info.warnopt ());
--- /dev/null
+/* PR tree-optimization/104119 - unexpected -Wformat-overflow after strlen
+ in ILP32 since Ranger integration
+ Verify that unlike -Wformat-overflow the sprintf optimization doesn't
+ assume the length of a string isn't bounded by the size of the array
+ member it's stored in.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* memcpy (void*, const void*, size_t);
+int snprintf (char*, size_t, const char*, ...);
+char* strcpy (char*, const char*);
+size_t strlen (const char*);
+
+extern void keep_call_on_line (int);
+extern void elim_call_on_line (int);
+
+void sink (void*, ...);
+
+struct __attribute__ ((packed)) S
+{
+ char a4[4], b4[4], ax[];
+};
+
+extern struct S es;
+
+void test_extern_decl_memcpy (void)
+{
+ struct S *p = &es;
+
+ /* Set strlen (P->A4) to [3, PTRDIFF - 2]. */
+ memcpy (p->a4, "123", 3);
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ keep_call_on_line (__LINE__);
+}
+
+void test_extern_decl_strcpy_3 (void)
+{
+ struct S *p = &es;
+
+ /* Set strlen (P->A4) to 3. */
+ strcpy (p->a4, "123");
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ elim_call_on_line (__LINE__);
+}
+
+void test_extern_decl_strcpy_X (const char *s)
+{
+ struct S *p = &es;
+
+ /* Set strlen (P->A4) to [0, PTRDIFF_MAX - 2]. */
+ strcpy (p->a4, s);
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ keep_call_on_line (__LINE__);
+}
+
+size_t test_extern_decl_strlen (void)
+{
+ struct S *p = &es;
+
+ /* Set strlen (P->A4) to [0, PTRDIFF - 2]. */
+ size_t n = strlen (p->a4);
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ keep_call_on_line (__LINE__);
+ return n;
+}
+
+
+static struct S ss;
+
+/* Store and read SS to prevent optimizers from assuming it's unchanged. */
+
+extern void set_ss (struct S *p)
+{
+ if (ss.a4[(unsigned char)*p->a4])
+ __builtin_memcpy (&ss, p, sizeof ss);
+}
+
+
+void test_static_decl_memcpy (void)
+{
+ struct S *p = &ss;
+
+ /* Set strlen (P->A4) to [3, PTRDIFF - 2]. */
+ memcpy (p->a4, "123", 3);
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ keep_call_on_line (__LINE__);
+}
+
+void test_static_decl_strcpy_3 (void)
+{
+ struct S *p = &ss;
+
+ /* Set strlen (P->A4) to 3. */
+ strcpy (p->a4, "123");
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ elim_call_on_line (__LINE__);
+}
+
+void test_static_decl_strcpy_X (const char *s)
+{
+ struct S *p = &ss;
+
+ /* Set strlen (P->A4) to [0, PTRDIFF_MAX - 2]. */
+ strcpy (p->a4, s);
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ keep_call_on_line (__LINE__);
+}
+
+size_t test_static_decl_strlen (void)
+{
+ struct S *p = &ss;
+
+ /* Set strlen (P->A4) to [0, PTRDIFF - 2]. */
+ size_t n = strlen (p->a4);
+ int i = snprintf (0, 0, "%s", p->a4);
+ if (i > 4)
+ keep_call_on_line (__LINE__);
+ return n;
+}
+
+/* { dg-final { scan-tree-dump-times "keep_call_on_line" 6 "optimized" } }
+ { dg-final { scan-tree-dump-not "elim_call_on_line" "optimized" } } */
--- /dev/null
+/* PR tree-optimization/104119 - unexpected -Wformat-overflow after strlen
+ in ILP32 since Ranger integration
+ { dg-do compile }
+ { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+int sprintf (char*, const char*, ...);
+size_t strlen (const char*);
+
+void sink (void*, ...);
+
+struct __attribute__ ((packed)) S
+{
+ char a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9], ax[];
+};
+
+extern struct S s;
+extern char a4[4], a7[7], a8[8];
+
+
+void test_decl (void)
+{
+ struct S *p = &s;
+
+ {
+ size_t n = strlen (p->a3);
+ sprintf (a4, "%s", p->a3); // { dg-bogus "-Wformat-overflow" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a4);
+ sprintf (a4, "%s", p->a4); // { dg-bogus "-Wformat-overflow" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a5);
+ sprintf (a4, "%s", p->a5); // { dg-warning "may write a terminating nul past the end" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a7);
+ sprintf (a8, "%s", p->a7); // { dg-bogus "-Wformat-overflow" }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->a8);
+ sprintf (a8, "%s", p->a8); // { dg-bogus "-Wformat-overflow" }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->a9);
+ sprintf (a8, "%s", p->a9); // { dg-warning "may write a terminating nul past the end " }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->ax);
+ sprintf (a7, "%s", p->ax); // { dg-bogus "-Wformat-overflow" "pr??????" { xfail ilp32 } }
+ sink (a7, n);
+ }
+}
+
+
+/* Verify the warning with a pointer to an allocated object with nonstant
+ size in known range. */
+
+void test_alloc_5_8 (int n)
+{
+ if (n < 5 || 8 < n)
+ n = 5;
+
+ struct S *p = (struct S*)malloc (sizeof *p + n);
+ sink (p); // initialize *p
+
+ {
+ size_t n = strlen (p->a3);
+ sprintf (a4, "%s", p->a3); // { dg-bogus "-Wformat-overflow" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a4);
+ sprintf (a4, "%s", p->a4); // { dg-bogus "-Wformat-overflow" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a5);
+ sprintf (a4, "%s", p->a5); // { dg-warning "may write a terminating nul past the end" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a7);
+ sprintf (a8, "%s", p->a7); // { dg-bogus "-Wformat-overflow" }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->a8);
+ sprintf (a8, "%s", p->a8); // { dg-bogus "-Wformat-overflow" }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->a9);
+ sprintf (a8, "%s", p->a9); // { dg-warning "may write a terminating nul past the end " }
+ sink (a8, n);
+ }
+
+ {
+ /* The size of the flexible array member p->ax is between 5 and 8
+ bytes so the length of the string stored in it is at most 7.
+ Verify the warning triggers based on its size and also gets
+ the length right. */
+ size_t n = strlen (p->ax);
+ sprintf (a4, "%s", p->ax); // { dg-warning "writing up to 7 bytes " }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->ax);
+ sprintf (a8, "%s", p->ax);
+ sink (a8, n);
+ }
+}
+
+
+void test_ptr (struct S *p)
+{
+ {
+ size_t n = strlen (p->a3);
+ sprintf (a4, "%s", p->a3); // { dg-bogus "-Wformat-overflow" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a4);
+ sprintf (a4, "%s", p->a4); // { dg-bogus "-Wformat-overflow" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a5);
+ sprintf (a4, "%s", p->a5); // { dg-warning "may write a terminating nul past the end" }
+ sink (a4, n);
+ }
+
+ {
+ size_t n = strlen (p->a7);
+ sprintf (a8, "%s", p->a7); // { dg-bogus "-Wformat-overflow" }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->a8);
+ sprintf (a8, "%s", p->a8); // { dg-bogus "-Wformat-overflow" }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->a9);
+ sprintf (a8, "%s", p->a9); // { dg-warning "may write a terminating nul past the end " }
+ sink (a8, n);
+ }
+
+ {
+ size_t n = strlen (p->ax);
+ sprintf (a8, "%s", p->ax); // { dg-bogus "-Wformat-overflow" "pr??????" { xfail ilp32 } }
+ sink (a8, n);
+ }
+}
} laststmt;
static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
-static bool get_range_strlen_dynamic (tree, gimple *s, c_strlen_data *,
- bitmap, range_query *, unsigned *);
+static bool get_range_strlen_dynamic (tree, gimple *, c_strlen_data *,
+ bitmap, pointer_query *, unsigned *);
/* Sets MINMAX to either the constant value or the range VAL is in
and returns either the constant value or VAL on success or null
static bool
get_range_strlen_phi (tree src, gphi *phi,
c_strlen_data *pdata, bitmap visited,
- range_query *rvals, unsigned *pssa_def_max)
+ pointer_query *ptr_qry, unsigned *pssa_def_max)
{
if (!bitmap_set_bit (visited, SSA_NAME_VERSION (src)))
return true;
continue;
c_strlen_data argdata = { };
- if (!get_range_strlen_dynamic (arg, phi, &argdata, visited, rvals,
+ if (!get_range_strlen_dynamic (arg, phi, &argdata, visited, ptr_qry,
pssa_def_max))
{
pdata->maxlen = build_all_ones_cst (size_type_node);
return true;
}
+/* Return the maximum possible length of the string PTR that's less
+ than MAXLEN given the size of the object of subobject it points
+ to at the given STMT. MAXLEN is the maximum length of the string
+ determined so far. Return null when no such maximum can be
+ determined. */
+
+static tree
+get_maxbound (tree ptr, gimple *stmt, offset_int maxlen,
+ pointer_query *ptr_qry)
+{
+ access_ref aref;
+ if (!ptr_qry->get_ref (ptr, stmt, &aref))
+ return NULL_TREE;
+
+ offset_int sizrem = aref.size_remaining ();
+ if (sizrem <= 0)
+ return NULL_TREE;
+
+ if (sizrem < maxlen)
+ maxlen = sizrem - 1;
+
+ /* Try to determine the maximum from the subobject at the offset.
+ This handles MEM [&some-struct, member-offset] that's often
+ the result of folding COMPONENT_REF [some-struct, member]. */
+ tree reftype = TREE_TYPE (aref.ref);
+ if (!RECORD_OR_UNION_TYPE_P (reftype)
+ || aref.offrng[0] != aref.offrng[1]
+ || !wi::fits_shwi_p (aref.offrng[0]))
+ return wide_int_to_tree (size_type_node, maxlen);
+
+ HOST_WIDE_INT off = aref.offrng[0].to_shwi ();
+ tree fld = field_at_offset (reftype, NULL_TREE, off);
+ if (!fld || !DECL_SIZE_UNIT (fld))
+ return wide_int_to_tree (size_type_node, maxlen);
+
+ offset_int size = wi::to_offset (DECL_SIZE_UNIT (fld));
+ if (maxlen < size)
+ return wide_int_to_tree (size_type_node, maxlen);
+
+ return wide_int_to_tree (size_type_node, size - 1);
+}
+
/* Attempt to determine the length of the string SRC. On success, store
the length in *PDATA and return true. Otherwise, return false.
VISITED is a bitmap of visited PHI nodes. RVALS points to the valuation
static bool
get_range_strlen_dynamic (tree src, gimple *stmt,
c_strlen_data *pdata, bitmap visited,
- range_query *rvals, unsigned *pssa_def_max)
+ pointer_query *ptr_qry, unsigned *pssa_def_max)
{
int idx = get_stridx (src, stmt);
if (!idx)
{
gimple *def_stmt = SSA_NAME_DEF_STMT (src);
if (gphi *phi = dyn_cast<gphi *>(def_stmt))
- return get_range_strlen_phi (src, phi, pdata, visited, rvals,
+ return get_range_strlen_phi (src, phi, pdata, visited, ptr_qry,
pssa_def_max);
}
else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
{
value_range vr;
- rvals->range_of_expr (vr, si->nonzero_chars, si->stmt);
+ ptr_qry->rvals->range_of_expr (vr, si->nonzero_chars, si->stmt);
if (range_int_cst_p (&vr))
{
pdata->minlen = vr.min ();
else if (pdata->minlen && TREE_CODE (pdata->minlen) == SSA_NAME)
{
value_range vr;
- rvals->range_of_expr (vr, si->nonzero_chars, stmt);
+ ptr_qry->rvals->range_of_expr (vr, si->nonzero_chars, stmt);
if (range_int_cst_p (&vr))
{
pdata->minlen = vr.min ();
pdata->maxlen = vr.max ();
- pdata->maxbound = pdata->maxlen;
+ offset_int max = offset_int::from (vr.upper_bound (0), SIGNED);
+ if (tree maxbound = get_maxbound (si->ptr, stmt, max, ptr_qry))
+ pdata->maxbound = maxbound;
+ else
+ pdata->maxbound = pdata->maxlen;
}
else
{
void
get_range_strlen_dynamic (tree src, gimple *stmt, c_strlen_data *pdata,
- range_query *rvals)
+ pointer_query &ptr_qry)
{
auto_bitmap visited;
tree maxbound = pdata->maxbound;
unsigned limit = param_ssa_name_def_chain_limit;
- if (!get_range_strlen_dynamic (src, stmt, pdata, visited, rvals, &limit))
+ if (!get_range_strlen_dynamic (src, stmt, pdata, visited, &ptr_qry, &limit))
{
/* On failure extend the length range to an impossible maximum
(a valid MAXLEN must be less than PTRDIFF_MAX - 1). Other
/* Set MAXBOUND to an arbitrary non-null non-integer node as a request
to have it set to the length of the longest string in a PHI. */
lendata.maxbound = arg;
- get_range_strlen_dynamic (arg, stmt, &lendata, ptr_qry.rvals);
+ get_range_strlen_dynamic (arg, stmt, &lendata, ptr_qry);
unsigned HOST_WIDE_INT maxbound = HOST_WIDE_INT_M1U;
if (tree_fits_uhwi_p (lendata.maxbound)
struct c_strlen_data;
extern void get_range_strlen_dynamic (tree, gimple *, c_strlen_data *,
- class range_query *);
+ pointer_query &);
/* APIs internal to strlen pass. Defined in gimple-ssa-sprintf.cc. */
extern bool handle_printf_call (gimple_stmt_iterator *, pointer_query &);