Constrain conservative string lengths to array sizes [PR104119].
Resolves: PR tree-optimization/104119 - unexpected -Wformat-overflow after strlen in ILP32 since Ranger integration gcc/ChangeLog: PR tree-optimization/104119 * gimple-ssa-sprintf.cc (struct directive): Change argument type. (format_none): Same. (format_percent): Same. (format_integer): Same. (format_floating): Same. (get_string_length): Same. (format_character): Same. (format_string): Same. (format_plain): Same. (format_directive): Same. (compute_format_length): Same. (handle_printf_call): Same. * tree-ssa-strlen.cc (get_range_strlen_dynamic): Same. Call get_maxbound. (get_range_strlen_phi): Same. (get_maxbound): New function. (strlen_pass::get_len_or_size): Adjust to parameter change. * tree-ssa-strlen.h (get_range_strlen_dynamic): Change argument type. gcc/testsuite/ChangeLog: PR tree-optimization/104119 * gcc.dg/tree-ssa/builtin-snprintf-13.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-warn-29.c: New test.
This commit is contained in:
parent
4e4252db03
commit
3c9f762ad0
|
@ -600,7 +600,7 @@ struct directive
|
|||
|
||||
/* 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
|
||||
|
@ -968,7 +968,7 @@ directive::set_precision (tree arg, range_query *query)
|
|||
/* 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;
|
||||
|
@ -977,7 +977,7 @@ format_none (const directive &, tree, range_query *)
|
|||
/* 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;
|
||||
|
@ -1199,7 +1199,7 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
|
|||
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;
|
||||
|
@ -1383,7 +1383,7 @@ format_integer (const directive &dir, tree arg, range_query *query)
|
|||
/* 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 ())
|
||||
{
|
||||
|
@ -1414,7 +1414,7 @@ format_integer (const directive &dir, tree arg, range_query *query)
|
|||
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)
|
||||
|
@ -1459,16 +1459,16 @@ format_integer (const directive &dir, tree arg, range_query *query)
|
|||
/* 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
|
||||
{
|
||||
|
@ -1477,11 +1477,11 @@ format_integer (const directive &dir, tree arg, range_query *query)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -1830,7 +1830,7 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
|
|||
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
|
||||
|
@ -2025,7 +2025,7 @@ format_floating (const directive &dir, tree arg, range_query *)
|
|||
|
||||
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 ();
|
||||
|
@ -2036,7 +2036,7 @@ get_string_length (tree str, gimple *stmt, unsigned HOST_WIDE_INT max_size,
|
|||
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
|
||||
|
@ -2084,17 +2084,30 @@ get_string_length (tree str, gimple *stmt, unsigned HOST_WIDE_INT max_size,
|
|||
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
|
||||
|
@ -2147,7 +2160,7 @@ get_string_length (tree str, gimple *stmt, unsigned HOST_WIDE_INT max_size,
|
|||
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;
|
||||
|
||||
|
@ -2160,7 +2173,8 @@ format_character (const directive &dir, tree arg, range_query *query)
|
|||
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)
|
||||
{
|
||||
|
@ -2457,7 +2471,7 @@ alias_offset (tree arg, HOST_WIDE_INT *arg_size,
|
|||
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;
|
||||
|
||||
|
@ -2495,7 +2509,7 @@ format_string (const directive &dir, tree arg, range_query *query)
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -2667,7 +2681,7 @@ format_string (const directive &dir, tree arg, range_query *query)
|
|||
/* 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;
|
||||
|
@ -3063,7 +3077,7 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &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. */
|
||||
|
@ -3088,7 +3102,7 @@ format_directive (const call_info &info,
|
|||
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
|
||||
|
@ -3990,7 +4004,8 @@ maybe_warn_overlap (call_info &info, format_result *res)
|
|||
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)
|
||||
{
|
||||
|
@ -4027,10 +4042,10 @@ compute_format_length (call_info &info, format_result *res, range_query *query)
|
|||
{
|
||||
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
|
||||
|
@ -4700,7 +4715,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry)
|
|||
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 ());
|
||||
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/* 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" } } */
|
|
@ -0,0 +1,179 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
|
@ -193,8 +193,8 @@ struct laststmt_struct
|
|||
} 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
|
||||
|
@ -1094,7 +1094,7 @@ dump_strlen_info (FILE *fp, gimple *stmt, range_query *rvals)
|
|||
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;
|
||||
|
@ -1113,7 +1113,7 @@ get_range_strlen_phi (tree src, gphi *phi,
|
|||
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);
|
||||
|
@ -1159,6 +1159,48 @@ get_range_strlen_phi (tree src, gphi *phi,
|
|||
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
|
||||
|
@ -1168,7 +1210,7 @@ get_range_strlen_phi (tree src, gphi *phi,
|
|||
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)
|
||||
|
@ -1177,7 +1219,7 @@ get_range_strlen_dynamic (tree src, gimple *stmt,
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1206,7 +1248,7 @@ get_range_strlen_dynamic (tree src, gimple *stmt,
|
|||
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 ();
|
||||
|
@ -1250,12 +1292,16 @@ get_range_strlen_dynamic (tree src, gimple *stmt,
|
|||
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
|
||||
{
|
||||
|
@ -1293,13 +1339,13 @@ get_range_strlen_dynamic (tree src, gimple *stmt,
|
|||
|
||||
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
|
||||
|
@ -4030,7 +4076,7 @@ strlen_pass::get_len_or_size (gimple *stmt, tree arg, int idx,
|
|||
/* 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)
|
||||
|
|
|
@ -33,7 +33,7 @@ extern tree get_range (tree, gimple *, wide_int[2],
|
|||
|
||||
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 &);
|
||||
|
|
Loading…
Reference in New Issue