PR middle-end/105604 - ICE: in tree_to_shwi with vla in struct and sprintf

gcc/ChangeLog:

	PR middle-end/105604
	* gimple-ssa-sprintf.cc (set_aggregate_size_and_offset): Add comments.
	(get_origin_and_offset_r): Remove null handling.  Handle variable array
	sizes.
	(get_origin_and_offset): Handle null argument here.  Simplify.
	(alias_offset): Update comment.
	* pointer-query.cc (field_at_offset): Update comment.  Handle members
	of variable-length types.

gcc/testsuite/ChangeLog:

	PR middle-end/105604
	* gcc.dg/Wrestrict-24.c: New test.
	* gcc.dg/Wrestrict-25.c: New test.
	* gcc.dg/Wrestrict-26.c: New test.

Co-authored-by: Richard Biener <rguenther@suse.de>
(cherry picked from commit 10d1986aee)
This commit is contained in:
Martin Sebor 2022-05-24 16:01:12 -06:00 committed by Richard Biener
parent e919fae01b
commit 740771a620
5 changed files with 368 additions and 35 deletions

View File

@ -2232,8 +2232,9 @@ format_character (const directive &dir, tree arg, pointer_query &ptr_qry)
}
/* If TYPE is an array or struct or union, increment *FLDOFF by the starting
offset of the member that *OFF point into and set *FLDSIZE to its size
in bytes and decrement *OFF by the same. Otherwise do nothing. */
offset of the member that *OFF points into if one can be determined and
set *FLDSIZE to its size in bytes and decrement *OFF by the same.
Otherwise do nothing. */
static void
set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff,
@ -2249,9 +2250,9 @@ set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff,
if (array_elt_at_offset (type, *off, &index, &arrsize))
{
*fldoff += index;
*off -= index;
*fldsize = arrsize;
}
/* Otherwise leave *FLDOFF et al. unchanged. */
}
else if (RECORD_OR_UNION_TYPE_P (type))
{
@ -2269,11 +2270,12 @@ set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff,
*fldoff += index;
*off -= index;
}
/* Otherwise leave *FLDOFF et al. unchanged. */
}
}
/* For an expression X of pointer type, recursively try to find the same
origin (object or pointer) as Y it references and return such a Y.
/* For an expression X of pointer type, recursively try to find its origin
(either object DECL or pointer such as PARM_DECL) Y and return such a Y.
When X refers to an array element or struct member, set *FLDOFF to
the offset of the element or member from the beginning of the "most
derived" object and *FLDSIZE to its size. When nonnull, set *OFF to
@ -2284,9 +2286,6 @@ static tree
get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
HOST_WIDE_INT *off)
{
if (!x)
return NULL_TREE;
HOST_WIDE_INT sizebuf = -1;
if (!fldsize)
fldsize = &sizebuf;
@ -2308,23 +2307,33 @@ get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
case ARRAY_REF:
{
tree offset = TREE_OPERAND (x, 1);
HOST_WIDE_INT idx = (tree_fits_uhwi_p (offset)
? tree_to_uhwi (offset) : HOST_WIDE_INT_MAX);
tree sub = TREE_OPERAND (x, 1);
unsigned HOST_WIDE_INT idx =
tree_fits_uhwi_p (sub) ? tree_to_uhwi (sub) : HOST_WIDE_INT_MAX;
tree eltype = TREE_TYPE (x);
if (TREE_CODE (eltype) == INTEGER_TYPE)
tree elsz = array_ref_element_size (x);
unsigned HOST_WIDE_INT elbytes =
tree_fits_shwi_p (elsz) ? tree_to_shwi (elsz) : HOST_WIDE_INT_MAX;
unsigned HOST_WIDE_INT byteoff = idx * elbytes;
if (byteoff < HOST_WIDE_INT_MAX
&& elbytes < HOST_WIDE_INT_MAX
&& byteoff / elbytes == idx)
{
/* For in-bounds constant offsets into constant-sized arrays
bump up *OFF, and for what's likely arrays or structs of
arrays, also *FLDOFF, as necessary. */
if (off)
*off = idx;
*off += byteoff;
if (elbytes > 1)
*fldoff += byteoff;
}
else if (idx < HOST_WIDE_INT_MAX)
*fldoff += idx * int_size_in_bytes (eltype);
else
*fldoff = idx;
*fldoff = HOST_WIDE_INT_MAX;
x = TREE_OPERAND (x, 0);
return get_origin_and_offset_r (x, fldoff, fldsize, nullptr);
return get_origin_and_offset_r (x, fldoff, fldsize, off);
}
case MEM_REF:
@ -2350,8 +2359,14 @@ get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
case COMPONENT_REF:
{
tree foff = component_ref_field_offset (x);
tree fld = TREE_OPERAND (x, 1);
*fldoff += int_byte_position (fld);
if (!tree_fits_shwi_p (foff)
|| !tree_fits_shwi_p (DECL_FIELD_BIT_OFFSET (fld)))
return x;
*fldoff += (tree_to_shwi (foff)
+ (tree_to_shwi (DECL_FIELD_BIT_OFFSET (fld))
/ BITS_PER_UNIT));
get_origin_and_offset_r (fld, fldoff, fldsize, off);
x = TREE_OPERAND (x, 0);
@ -2411,30 +2426,25 @@ get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
return x;
}
/* Nonrecursive version of the above. */
/* Nonrecursive version of the above.
The function never returns null unless X is null to begin with. */
static tree
get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off,
HOST_WIDE_INT *fldsize = nullptr)
{
if (!x)
return NULL_TREE;
HOST_WIDE_INT sizebuf;
if (!fldsize)
fldsize = &sizebuf;
/* Invalidate *FLDSIZE. */
*fldsize = -1;
*fldoff = *off = 0;
*fldoff = *off = *fldsize = 0;
tree orig = get_origin_and_offset_r (x, fldoff, fldsize, off);
if (!orig)
return NULL_TREE;
if (!*fldoff && *off == *fldsize)
{
*fldoff = *off;
*off = 0;
}
return orig;
return get_origin_and_offset_r (x, fldoff, fldsize, off);
}
/* If ARG refers to the same (sub)object or array element as described
@ -2454,7 +2464,8 @@ alias_offset (tree arg, HOST_WIDE_INT *arg_size,
return HOST_WIDE_INT_MIN;
/* The two arguments may refer to the same object. If they both refer
to a struct member, see if the members are one and the same. */
to a struct member, see if the members are one and the same. If so,
return the offset into the member. */
HOST_WIDE_INT arg_off = 0, arg_fld = 0;
tree arg_orig = get_origin_and_offset (arg, &arg_fld, &arg_off, arg_size);

View File

@ -2447,9 +2447,13 @@ field_at_offset (tree type, tree start_after, HOST_WIDE_INT off,
/* The offset of FLD within its immediately enclosing structure. */
HOST_WIDE_INT fldpos = next_pos < 0 ? int_byte_position (fld) : next_pos;
tree typesize = TYPE_SIZE_UNIT (fldtype);
if (typesize && TREE_CODE (typesize) != INTEGER_CST)
/* Bail if FLD is a variable length member. */
return NULL_TREE;
/* If the size is not available the field is a flexible array
member. Treat this case as success. */
tree typesize = TYPE_SIZE_UNIT (fldtype);
HOST_WIDE_INT fldsize = (tree_fits_uhwi_p (typesize)
? tree_to_uhwi (typesize)
: off);
@ -2463,7 +2467,11 @@ field_at_offset (tree type, tree start_after, HOST_WIDE_INT off,
{
/* If OFF is equal to the offset of the next field continue
to it and skip the array/struct business below. */
next_pos = int_byte_position (next_fld);
tree pos = byte_position (next_fld);
if (!tree_fits_shwi_p (pos))
/* Bail if NEXT_FLD is a variable length member. */
return NULL_TREE;
next_pos = tree_to_shwi (pos);
*nextoff = *fldoff + next_pos;
if (*nextoff == off && TREE_CODE (type) != UNION_TYPE)
continue;

View File

@ -0,0 +1,35 @@
/* PR tree-optimization/105604 - ICE: in tree_to_shwi with vla in struct
and sprintf
{ dg-do compile }
{ dg-options "-O2 -Wall -Wrestrict" } */
extern int sprintf (char*, const char*, ...);
extern void* sink (void*, ...);
struct {
long users;
long size;
char *data;
} * main_trans;
void *main___trans_tmp_1;
int users = 0;
void test (void)
{
struct {
long users;
long size;
char *data;
int links[users];
char buf[];
} *trans = sink (0);
trans->data = trans->buf;
main___trans_tmp_1 = trans;
main_trans = main___trans_tmp_1;
sprintf (main_trans->data, "test");
sink (main_trans->data);
}

View File

@ -0,0 +1,165 @@
/* PR tree-optimization/105604 - ICE: in tree_to_shwi with vla in struct
and sprintf
{ dg-do compile }
{ dg-options "-O2 -Wall -Wrestrict" } */
extern int sprintf (char*, const char*, ...);
void* sink (void*);
void sprintf_S_a8_an_bn (int n, int i, int j)
{
struct {
char a8[8], an[n], bn[n];
} *p = sink (0);
{
char *d = p->a8 + i;
char *s = p->a8;
sprintf (d, "%s", s); // { dg-warning "argument 3 may overlap" }
sink (p);
}
{
char *d = p->a8;
char *s = p->a8 + j;
sprintf (d, "%s", s); // { dg-warning "argument 3 may overlap" }
sink (p);
}
{
char *d = p->a8 + i;
char *s = p->a8 + j;
sprintf (d, "%s", s); // { dg-warning "argument 3 may overlap" }
sink (p);
}
{
char *d = p->a8 + i;
char *s = p->an;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->a8;
char *s = p->an + j;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->a8 + i;
char *s = p->an + j;
sprintf (d, "%s", s);
sink (p);
}
{
/* The IL makes it impossible to rule out an overlap between
p->a8 + i and p->bn + i so the "may overlap" warning triggers. */
char *d = p->a8 + i;
char *s = p->bn;
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
sink (p);
}
{
char *d = p->a8;
char *s = p->bn + j;
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
sink (p);
}
{
char *d = p->a8 + i;
char *s = p->bn + j;
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
sink (p);
}
{
char *d = p->an + i;
char *s = p->bn;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->an;
char *s = p->bn + j;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->an + i;
char *s = p->bn + j;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->an + i;
char *s = p->a8;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->an;
char *s = p->a8 + j;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->an + i;
char *s = p->a8 + j;
sprintf (d, "%s", s);
sink (p);
}
{
char *d = p->bn + i;
char *s = p->a8;
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
sink (p);
}
{
char *d = p->bn;
char *s = p->a8 + j;
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
sink (p);
}
{
char *d = p->bn + i;
char *s = p->a8 + j;
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
sink (p);
}
{
char *d = p->bn + i;
char *s = p->bn;
sprintf (d, "%s", s); // { dg-warning "may overlap" }
sink (p);
}
{
char *d = p->bn;
char *s = p->bn + j;
sprintf (d, "%s", s); // { dg-warning "may overlap" }
sink (p);
}
{
char *d = p->bn + i;
char *s = p->bn + j;
sprintf (d, "%s", s); // { dg-warning "may overlap" }
sink (p);
}
}

View File

@ -0,0 +1,114 @@
/* Verify that sprintf calls with arrays or struct of arrays don't
cause -Wrestrict false positives.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wrestrict -ftrack-macro-expansion=0" } */
#define sprintf(d, f, ...) (sprintf (d, f, __VA_ARGS__), sink (d))
extern void sink (void*, ...);
extern int (sprintf) (char*, const char*, ...);
extern char ca[][2][8];
void test_array_of_arrays (void)
{
sprintf (ca[0][0], "%s", ca[0][0]); // { dg-warning "-Wrestrict" }
sprintf (ca[0][0], "%s", ca[0][1]);
sprintf (ca[0][0], "%s", ca[1][0]);
sprintf (ca[0][0], "%s", ca[1][1]);
sprintf (ca[0][1], "%s", ca[0][0]);
sprintf (ca[0][1], "%s", ca[0][1]); // { dg-warning "-Wrestrict" }
sprintf (ca[0][1], "%s", ca[1][0]);
sprintf (ca[0][1], "%s", ca[1][1]);
sprintf (ca[1][0], "%s", ca[0][0]);
sprintf (ca[1][0], "%s", ca[0][1]);
sprintf (ca[1][0], "%s", ca[1][0]); // { dg-warning "-Wrestrict" }
sprintf (ca[1][0], "%s", ca[1][1]);
sprintf (ca[1][1], "%s", ca[0][0]);
sprintf (ca[1][1], "%s", ca[0][1]);
sprintf (ca[1][1], "%s", ca[1][0]);
sprintf (ca[1][1], "%s", ca[1][1]); // { dg-warning "-Wrestrict" }
}
struct A
{
char a[2][2][8];
char b[2][2][8];
char c[2][2][8];
};
extern struct A aa[][2];
void test_array_of_structs (void)
{
// Verify that calls with the same elements of the same array trigger
// warnings as expected.
sprintf (aa[0][0].a[0][0], "%s", aa[0][0].a[0][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][0].a[0][1], "%s", aa[0][0].a[0][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][0].a[1][0], "%s", aa[0][0].a[1][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][0].a[1][1], "%s", aa[0][0].a[1][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][1].a[0][0], "%s", aa[0][1].a[0][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][1].a[0][1], "%s", aa[0][1].a[0][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][1].a[1][0], "%s", aa[0][1].a[1][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[0][1].a[1][1], "%s", aa[0][1].a[1][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][0].a[0][0], "%s", aa[1][0].a[0][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][0].a[0][1], "%s", aa[1][0].a[0][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][0].a[1][0], "%s", aa[1][0].a[1][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][0].a[1][1], "%s", aa[1][0].a[1][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][1].a[0][0], "%s", aa[1][1].a[0][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][1].a[0][1], "%s", aa[1][1].a[0][1]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][1].a[1][0], "%s", aa[1][1].a[1][0]); // { dg-warning "-Wrestrict" }
sprintf (aa[1][1].a[1][1], "%s", aa[1][1].a[1][1]); // { dg-warning "-Wrestrict" }
#define NOWARN()
// Exhaustively verify that calls with different elements of the same
// array don't cause false positives.
#undef NOWARN
#define NOWARN(D, S) \
sprintf (D[0][0], "%s", S[0][0]); \
sprintf (D[0][0], "%s", S[0][1]); \
sprintf (D[0][0], "%s", S[1][0]); \
sprintf (D[0][0], "%s", S[1][1]); \
sprintf (D[0][1], "%s", S[0][0]); \
sprintf (D[0][1], "%s", S[0][1]); \
sprintf (D[0][1], "%s", S[1][0]); \
sprintf (D[0][1], "%s", S[1][1]); \
sprintf (D[1][0], "%s", S[0][0]); \
sprintf (D[1][0], "%s", S[0][1]); \
sprintf (D[1][0], "%s", S[1][0]); \
sprintf (D[1][0], "%s", S[1][1]); \
sprintf (D[1][1], "%s", S[0][0]); \
sprintf (D[1][1], "%s", S[0][1]); \
sprintf (D[1][1], "%s", S[1][0]); \
sprintf (D[1][1], "%s", S[1][1])
NOWARN (aa[0][0].a, aa[0][1].a);
NOWARN (aa[0][0].a, aa[1][0].a);
NOWARN (aa[0][0].a, aa[1][1].a);
NOWARN (aa[0][1].a, aa[0][0].a);
NOWARN (aa[0][1].a, aa[1][0].a);
NOWARN (aa[0][1].a, aa[1][1].a);
NOWARN (aa[1][0].a, aa[0][0].a);
NOWARN (aa[1][0].a, aa[0][1].a);
NOWARN (aa[1][0].a, aa[1][1].a);
#define NOWARN_MEM(M1, M2) \
NOWARN (aa[0][0].M1, aa[0][0].M2); \
NOWARN (aa[0][0].M1, aa[0][1].M2); \
NOWARN (aa[0][0].M1, aa[1][0].M2); \
NOWARN (aa[0][0].M1, aa[1][1].M2)
NOWARN_MEM (a, b);
NOWARN_MEM (a, c);
NOWARN_MEM (b, a);
NOWARN_MEM (b, c);
NOWARN_MEM (c, a);
NOWARN_MEM (c, b);
}