PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local buffer

PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local buffer
PR middle-end/91647 - new FAILs for Warray-bounds-8 and Wstringop-overflow-3.C
PR middle-end/91463 - missing -Warray-bounds accessing past the end of a statically initialized flexible array member
PR middle-end/92312 - bogus -Wstringop-overflow storing into a trailing array backed by larger buffer

gcc/ChangeLog:

	PR middle-end/91679
	PR middle-end/91647
	PR middle-end/91463
	PR middle-end/92312
	* c-family/c-pretty-print.c (direct_abstract_declarator): Print
	bound in zero-length arrays.
	* gcc/c-family/c.opt (-Wzero-length-bounds): New option.
	* gcc/doc/invoke.texi (-Wzero-length-bounds): Document.
	* gimple-match-head.c (try_conditional_simplification): Use memcpy
	instead of a hand-rolled loop to avoid PR 92323.
	* tree-vrp.c (vrp_prop::check_array_ref): Handle trailing arrays
	with initializers.
	(vrp_prop::check_mem_ref): Handle declared struct objects.
	* tree.c (last_field): New function.
	(array_at_struct_end_p): Handle MEM_REF.
	(get_initializer_for): New helper.
	(component_ref_size): Add argument.  Rename locals.  Call
	get_initializer_for instead of fold_ctor_reference.  Correct handling
	of flexible array members.
	* wide-int.h (generic_wide_int <storage>::sign_mask): Assert invariant.

gcc/testsuite/ChangeLog:

	PR middle-end/91679
	PR middle-end/91647
	PR middle-end/91463
	PR middle-end/92312
	* c-c++-common/Warray-bounds-2.c: Disable VRP.  Adjust expected messages.
	* g++.dg/warn/Warray-bounds-8.C: Remove xfails.
	* gcc.dg/Warray-bounds-48.c: New test.
	* gcc.dg/Warray-bounds-49.c: New test.
	* gcc.dg/Wstringop-overflow-16.c: Adjust text of expected messages.
	* gcc.dg/Wstringop-overflow-21.c: New test.
	* gcc.dg/Wzero-length-array-bounds.c: New test.
	* gcc.dg/pr36902.c: Remove xfail.
	* gcc.dg/strlenopt-57.c: Add an expected warning.

From-SVN: r277728
This commit is contained in:
Martin Sebor 2019-11-01 21:09:20 +00:00 committed by Martin Sebor
parent 8dc56a2244
commit 49fb45c81f
20 changed files with 1025 additions and 113 deletions

View File

@ -1,3 +1,26 @@
2019-11-01 Martin Sebor <msebor@redhat.com>
PR middle-end/91679
PR middle-end/91647
PR middle-end/91463
PR middle-end/92312
* c-family/c-pretty-print.c (direct_abstract_declarator): Print
bound in zero-length arrays.
* gcc/c-family/c.opt (-Wzero-length-bounds): New option.
* gcc/doc/invoke.texi (-Wzero-length-bounds): Document.
* gimple-match-head.c (try_conditional_simplification): Use memcpy
instead of a hand-rolled loop to avoid PR 92323.
* tree-vrp.c (vrp_prop::check_array_ref): Handle trailing arrays
with initializers.
(vrp_prop::check_mem_ref): Handle declared struct objects.
* tree.c (last_field): New function.
(array_at_struct_end_p): Handle MEM_REF.
(get_initializer_for): New helper.
(component_ref_size): Add argument. Rename locals. Call
get_initializer_for instead of fold_ctor_reference. Correct handling
of flexible array members.
* wide-int.h (generic_wide_int <storage>::sign_mask): Assert invariant.
2019-11-01 Kewen Lin <linkw@gcc.gnu.org> 2019-11-01 Kewen Lin <linkw@gcc.gnu.org>
* config/rs6000/rs6000-modes.def (V2SF, V2SI): New modes. * config/rs6000/rs6000-modes.def (V2SF, V2SI): New modes.

View File

@ -581,16 +581,20 @@ c_pretty_printer::direct_abstract_declarator (tree t)
case ARRAY_TYPE: case ARRAY_TYPE:
pp_c_left_bracket (this); pp_c_left_bracket (this);
if (TYPE_DOMAIN (t) && TYPE_MAX_VALUE (TYPE_DOMAIN (t))) if (tree dom = TYPE_DOMAIN (t))
{ {
tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (t)); if (tree maxval = TYPE_MAX_VALUE (dom))
tree type = TREE_TYPE (maxval); {
tree type = TREE_TYPE (maxval);
if (tree_fits_shwi_p (maxval)) if (tree_fits_shwi_p (maxval))
pp_wide_integer (this, tree_to_shwi (maxval) + 1); pp_wide_integer (this, tree_to_shwi (maxval) + 1);
else
expression (fold_build2 (PLUS_EXPR, type, maxval,
build_int_cst (type, 1)));
}
else else
expression (fold_build2 (PLUS_EXPR, type, maxval, pp_string (this, "0");
build_int_cst (type, 1)));
} }
pp_c_right_bracket (this); pp_c_right_bracket (this);
direct_abstract_declarator (TREE_TYPE (t)); direct_abstract_declarator (TREE_TYPE (t));

View File

@ -338,6 +338,10 @@ Warray-bounds=
LangEnabledBy(C ObjC C++ LTO ObjC++,Wall,1,0) LangEnabledBy(C ObjC C++ LTO ObjC++,Wall,1,0)
; in common.opt ; in common.opt
Wzero-length-bounds
C ObjC C++ ObjC++ Var(warn_zero_length_bounds) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
Warn about accesses to interior zero-length array members.
Wassign-intercept Wassign-intercept
ObjC ObjC++ Var(warn_assign_intercept) Warning ObjC ObjC++ Var(warn_assign_intercept) Warning
Warn whenever an Objective-C assignment is being intercepted by the garbage collector. Warn whenever an Objective-C assignment is being intercepted by the garbage collector.

View File

@ -325,6 +325,7 @@ Objective-C and Objective-C++ Dialects}.
-Winaccessible-base @gol -Winaccessible-base @gol
-Winit-self -Winline -Wno-int-conversion -Wint-in-bool-context @gol -Winit-self -Winline -Wno-int-conversion -Wint-in-bool-context @gol
-Wno-int-to-pointer-cast -Winvalid-memory-model -Wno-invalid-offsetof @gol -Wno-int-to-pointer-cast -Winvalid-memory-model -Wno-invalid-offsetof @gol
-Wzero-length-bounds @gol
-Winvalid-pch -Wlarger-than=@var{byte-size} @gol -Winvalid-pch -Wlarger-than=@var{byte-size} @gol
-Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol -Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
-Wmain -Wmaybe-uninitialized -Wmemset-elt-size -Wmemset-transposed-args @gol -Wmain -Wmaybe-uninitialized -Wmemset-elt-size -Wmemset-transposed-args @gol
@ -4438,6 +4439,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
-Wimplicit-int @r{(C and Objective-C only)} @gol -Wimplicit-int @r{(C and Objective-C only)} @gol
-Wimplicit-function-declaration @r{(C and Objective-C only)} @gol -Wimplicit-function-declaration @r{(C and Objective-C only)} @gol
-Winit-self @r{(only for C++)} @gol -Winit-self @r{(only for C++)} @gol
-Wzero-length-bounds @gol
-Wlogical-not-parentheses @gol -Wlogical-not-parentheses @gol
-Wmain @r{(only for C/ObjC and unless} @option{-ffreestanding}@r{)} @gol -Wmain @r{(only for C/ObjC and unless} @option{-ffreestanding}@r{)} @gol
-Wmaybe-uninitialized @gol -Wmaybe-uninitialized @gol
@ -6330,6 +6332,33 @@ conversions. This warning is about implicit conversions; for explicit
conversions the warnings @option{-Wno-int-to-pointer-cast} and conversions the warnings @option{-Wno-int-to-pointer-cast} and
@option{-Wno-pointer-to-int-cast} may be used. @option{-Wno-pointer-to-int-cast} may be used.
@item -Wzero-length-bounds
@opindex Wzero-length-bounds
@opindex Wzero-length-bounds
Warn about accesses to elements of zero-length array members that might
overlap other members of the same object. Declaring interior zero-length
arrays is discouraged because accesses to them are undefined. See
@xref{Zero Length}.
For example, the first two stores in function @code{bad} are diagnosed
because the array elements overlap the subsequent members @code{b} and
@code{c}. The third store is diagnosed by @option{-Warray-bounds}
because it is beyond the bounds of the enclosing object.
@smallexample
struct X @{ int a[0]; int b, c; @};
struct X x;
void bad (void)
@{
x.a[0] = 0; // -Wzero-length-bounds
x.a[1] = 1; // -Wzero-length-bounds
x.a[2] = 2; // -Warray-bounds
@}
@end smallexample
Option @option{-Wzero-length-bounds} is enabled by @option{-Warray-bounds}.
@item -Wno-div-by-zero @item -Wno-div-by-zero
@opindex Wno-div-by-zero @opindex Wno-div-by-zero
@opindex Wdiv-by-zero @opindex Wdiv-by-zero

View File

@ -837,8 +837,8 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
gimple_match_op cond_op (gimple_match_cond (res_op->ops[0], gimple_match_op cond_op (gimple_match_cond (res_op->ops[0],
res_op->ops[num_ops - 1]), res_op->ops[num_ops - 1]),
op, res_op->type, num_ops - 2); op, res_op->type, num_ops - 2);
for (unsigned int i = 1; i < num_ops - 1; ++i)
cond_op.ops[i - 1] = res_op->ops[i]; memcpy (cond_op.ops, res_op->ops + 1, (num_ops - 1) * sizeof *cond_op.ops);
switch (num_ops - 2) switch (num_ops - 2)
{ {
case 2: case 2:

View File

@ -1,3 +1,19 @@
2019-11-01 Martin Sebor <msebor@redhat.com>
PR middle-end/91679
PR middle-end/91647
PR middle-end/91463
PR middle-end/92312
* c-c++-common/Warray-bounds-2.c: Disable VRP. Adjust expected messages.
* g++.dg/warn/Warray-bounds-8.C: Remove xfails.
* gcc.dg/Warray-bounds-48.c: New test.
* gcc.dg/Warray-bounds-49.c: New test.
* gcc.dg/Wstringop-overflow-16.c: Adjust text of expected messages.
* gcc.dg/Wstringop-overflow-21.c: New test.
* gcc.dg/Wzero-length-array-bounds.c: New test.
* gcc.dg/pr36902.c: Remove xfail.
* gcc.dg/strlenopt-57.c: Add an expected warning.
2019-11-01 Steven G. Kargl <kargl@gcc.gnu.org> 2019-11-01 Steven G. Kargl <kargl@gcc.gnu.org>
* gfortran.dg/byte_3.f: New test. * gfortran.dg/byte_3.f: New test.

View File

@ -6,7 +6,7 @@
source of the excessive array bound is in a different function than source of the excessive array bound is in a different function than
the call. the call.
{ dg-do compile } { dg-do compile }
{ dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */ { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow -fno-tree-vrp" } */
#if __has_include (<stddef.h>) #if __has_include (<stddef.h>)
# include <stddef.h> # include <stddef.h>
@ -216,13 +216,13 @@ void call_strncpy_dst_diff_max (const char *s, size_t n)
static void static void
wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i, size_t n) wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i, size_t n)
{ {
strncpy (d + i, s, n); /* { dg-bogus "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */ strncpy (d + i, s, n); /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
} /* { dg-warning "array subscript -1 is outside array bounds" "" { target *-*-* } .-1 } */ }
void call_strncpy_dstarray_diff_neg (const char *s, size_t n) void call_strncpy_dstarray_diff_neg (const char *s, size_t n)
{ {
struct Array ar10[2]; /* { dg-bogus ".ar10. declared here" } */ struct Array ar10[2]; /* { dg-message ".ar10. declared here" } */
sink (&ar10); /* { dg-message "while referencing" "" { target *-*-* } .-1 } */ sink (&ar10);
int off = (char*)ar10[1].a17 - (char*)ar10 + 1; int off = (char*)ar10[1].a17 - (char*)ar10 + 1;
wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n); wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n);

View File

@ -13,7 +13,7 @@ void sink (void*);
struct Ax struct Ax
{ {
char n; char n;
char a[]; // { dg-message "while referencing .Ax::a." "pr91463" { xfail *-*-* } } char a[]; // { dg-message "while referencing .Ax::a." }
}; };
// Verify warning for a definition with no initializer. // Verify warning for a definition with no initializer.
@ -21,9 +21,9 @@ Ax ax_;
void gax_ () void gax_ ()
{ {
ax_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
} }
// Verify warning for access to a definition with an initializer that doesn't // Verify warning for access to a definition with an initializer that doesn't
@ -32,9 +32,9 @@ Ax ax0 = { 0 };
void gax0 () void gax0 ()
{ {
ax0.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax0.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax0.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax0.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax0.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax0.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
} }
// Verify warning for access to a definition with an initializer that // Verify warning for access to a definition with an initializer that
@ -43,9 +43,9 @@ Ax ax0_ = { 0, { } };
void gax0_ () void gax0_ ()
{ {
ax0_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax0_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax0_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax0_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax0_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax0_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
} }
// Verify warning for out-of-bounds accesses to a definition with // Verify warning for out-of-bounds accesses to a definition with
@ -55,8 +55,8 @@ Ax ax1 = { 1, { 0 } };
void gax1 () void gax1 ()
{ {
ax1.a[0] = 0; ax1.a[0] = 0;
ax1.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax1.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
ax1.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax1.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
} }
Ax ax2 = { 2, { 1, 0 } }; Ax ax2 = { 2, { 1, 0 } };
@ -65,7 +65,7 @@ void gax2 ()
{ {
ax2.a[0] = 0; ax2.a[0] = 0;
ax2.a[1] = 0; ax2.a[1] = 0;
ax2.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } ax2.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
} }
@ -308,14 +308,14 @@ void ga1ix ()
struct Bx struct Bx
{ {
char n; char n;
char a[]; // { dg-message "while referencing .Bx::a." "pr91463" { xfail *-*-* } } char a[]; // { dg-message "while referencing .Bx::a." }
// Verify the warning for a constant. // Verify the warning for a constant.
Bx () { a[0] = 0; } // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } Bx () { a[0] = 0; } // { dg-warning "\\\[-Warray-bounds" }
// And also for a non-constant. Regardless of the subscript, the array // And also for a non-constant. Regardless of the subscript, the array
// of the object in function gxi() below has a zero size. // of the object in function gxi() below has a zero size.
Bx (int i) { a[i] = 0; } // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } Bx (int i) { a[i] = 0; } // { dg-warning "\\\[-Warray-bounds" }
}; };
void gbx (void) void gbx (void)

View File

@ -67,7 +67,7 @@ void strcpy_global (void)
SA (__builtin_offsetof (struct MA17, ax) == 157); SA (__builtin_offsetof (struct MA17, ax) == 157);
T (gma.ax, 0); // { dg-warning "'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" } T (gma.ax, 0); // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'ax' with type 'char[]' at offset 157|'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
} }
@ -92,16 +92,16 @@ void strcpy_global_array (void)
T (gma2[0].a17, 16); T (gma2[0].a17, 16);
T (gma2[0].a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'gma2' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" } T (gma2[0].a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'gma2' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
/* GMA2 is external buts because it's an array its definition in another /* GMA2 is external but because it's an array its definition in another
translation unit may not provide an initializer for the flexible array translation unit may not provide an initializer for the flexible array
member. Verify that a warning is issued for access to it. */ member. Verify that a warning is issued for access to it. */
T (gma2[0].ax, 1); // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" } T (gma2[0].ax, 1); // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
T (gma2[0].ax, 7); // { dg-warning "'strcpy' offset \\\[157, 164] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" } T (gma2[0].ax, 7); // { dg-warning "'strcpy' offset \\\[157, 164] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
/* IGMA_ is internal and provides on definition for the flexible array /* IGMA_ is internal and provides on definition for the flexible array
member. Verify that a warnin is issued for out-of-bounds accesses member. Verify that a warnin is issued for out-of-bounds accesses
to it. */ to it. */
T (igma2_[0].ax, 1); // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'igma2_' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" } T (igma2_[0].ax, 1); // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'igma2_' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
T (igma_3.ax, 0); T (igma_3.ax, 0);
T (igma_3.ax, 1); T (igma_3.ax, 1);
@ -134,7 +134,7 @@ void strcpy_local (void)
T (lma.a17, 16); T (lma.a17, 16);
T (lma.a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" } T (lma.a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
T (lma.ax, 0); // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" } T (lma.ax, 0); // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
} }
@ -191,11 +191,11 @@ void strcpy_ref (struct MA17 *pma)
array. The warning assumes that PMA doesn't point to the last element array. The warning assumes that PMA doesn't point to the last element
of the array which could in theory have nonzero elements without of the array which could in theory have nonzero elements without
overlapping other objects. */ overlapping other objects. */
T (pma[1].ax, 0); // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 314" } T (pma[1].ax, 0); // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 314" }
T ((pma + 1)->ax, 1); // { dg-warning "'strcpy' offset \\\[314, 315] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 314" } T ((pma + 1)->ax, 1); // { dg-warning "'strcpy' offset \\\[314, 315] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 314" }
T ((pma + 1)[1].ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 471" } T ((pma + 1)[1].ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 471" }
T ((*(pma + 2)).ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 471" } T ((*(pma + 2)).ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 471" }
T (pma[3].ax, 9); // { dg-warning "'strcpy' offset \\\[628, 637] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 628" } T (pma[3].ax, 9); // { dg-warning "'strcpy' offset \\\[628, 637] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 628" }
T (pma[-1].a1, 0); T (pma[-1].a1, 0);
T (pma[-1].a1, 1); // { dg-warning "'strcpy' offset -152 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" } T (pma[-1].a1, 1); // { dg-warning "'strcpy' offset -152 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" }

View File

@ -0,0 +1,363 @@
/* PR middle-end/91647 - missing -Warray-bounds accessing a zero-length array
of a declared object
{ dg-do "compile" }
{ dg-options "-O2 -Wall" } */
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
void sink (void*);
/* Exercise a true flexible member. */
struct AX
{
int32_t n;
int16_t ax[]; // { dg-message "while referencing 'ax'" "member" }
};
static void warn_ax_local (struct AX *p)
{
p->ax[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
p->ax[1] = 1; // { dg-warning "\\\[-Warray-bounds" }
}
static void nowarn_ax_extern (struct AX *p)
{
p->ax[0] = 0; p->ax[99] = 99; p->ax[999] = 999; p->ax[9999] = 9999;
}
static void warn_ax_local_buf (struct AX *p)
{
p->ax[0] = 4; p->ax[1] = 5;
p->ax[2] = 6; // { dg-warning "\\\[-Warray-bounds" }
p->ax[3] = 7; // { dg-warning "\\\[-Warray-bounds" }
p->ax[4] = 8; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_ax_extern_buf (struct AX *p)
{
p->ax[0] = 9; p->ax[1] = 10; p->ax[2] = 11;
p->ax[3] = 12; // { dg-warning "\\\[-Warray-bounds" }
p->ax[4] = 13; // { dg-warning "\\\[-Warray-bounds" }
p->ax[5] = 14; // { dg-warning "\\\[-Warray-bounds" }
}
static void nowarn_ax_extern_bufx (struct AX *p)
{
p->ax[0] = 0; p->ax[99] = 99; p->ax[999] = 999; p->ax[9999] = 9999;
}
static void nowarn_ax_ref (struct AX *p)
{
p->ax[0] = 0; p->ax[99] = 99; p->ax[999] = 999; p->ax[9999] = 9999;
}
void test_ax (struct AX *p, unsigned n)
{
{
struct AX sax; // { dg-message "defined here" "struct definition" }
warn_ax_local (&sax);
sink (&sax);
}
{
extern
struct AX xsax;
nowarn_ax_extern (&xsax);
sink (&xsax);
}
{
/* Verify out-of-bounds access to the local BUF is diagnosed. */
char ax_buf_p2[sizeof (struct AX) + 2 * sizeof (int16_t)];
warn_ax_local_buf ((struct AX*) ax_buf_p2);
sink (ax_buf_p2);
}
{
/* Verify out-of-bounds access to the extern BUF with a known
bound is diagnosed. */
extern char ax_buf_p3[sizeof (struct AX) + 3 * sizeof (int16_t)];
warn_ax_extern_buf ((struct AX*) ax_buf_p3);
sink (ax_buf_p3);
}
{
/* Verify that accesses to BUFX with an unknown bound are not
diagnosed. */
extern char bufx[];
nowarn_ax_extern_bufx ((struct AX*) bufx);
sink (bufx);
}
{
/* Verify that accesses to BUFN with a runtime bound are not
diagnosed. */
char bufn[n];
nowarn_ax_extern_bufx ((struct AX*) bufn);
sink (bufn);
}
nowarn_ax_ref (p);
}
/* Exercise a zero-length trailing member array. It's the same as above
except that extern declarations with no definitions are considered to
have zero elements (they can't be initialized to have any). */
struct A0
{
int32_t n;
int16_t a0[0]; // { dg-message "while referencing 'a0'" "member" }
};
static void warn_a0_local (struct A0 *p)
{
p->a0[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
p->a0[1] = 1; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a0_extern (struct A0 *p)
{
p->a0[0] = 2; // { dg-warning "\\\[-Warray-bounds" }
p->a0[1] = 3; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a0_local_buf (struct A0 *p)
{
p->a0[0] = 4; p->a0[1] = 5;
p->a0[2] = 6; // { dg-warning "\\\[-Warray-bounds" }
p->a0[3] = 7; // { dg-warning "\\\[-Warray-bounds" }
p->a0[4] = 8; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a0_extern_buf (struct A0 *p)
{
p->a0[0] = 9; p->a0[1] = 10; p->a0[2] = 11;
p->a0[3] = 12; // { dg-warning "\\\[-Warray-bounds" }
p->a0[4] = 13; // { dg-warning "\\\[-Warray-bounds" }
p->a0[5] = 14; // { dg-warning "\\\[-Warray-bounds" }
}
static void nowarn_a0_extern_bufx (struct A0 *p)
{
p->a0[0] = 0; p->a0[99] = 99; p->a0[999] = 999; p->a0[9999] = 9999;
}
static void nowarn_a0_ref (struct A0 *p)
{
p->a0[0] = 0; p->a0[99] = 99; p->a0[999] = 999; p->a0[9999] = 9999;
}
void test_a0 (struct A0 *p, unsigned n)
{
{
struct A0 sa0; // { dg-message "defined here" "struct definition" }
warn_a0_local (&sa0);
sink (&sa0);
}
{
extern
struct A0 xsa0; // { dg-message "defined here" "struct definition" }
warn_a0_extern (&xsa0);
sink (&xsa0);
}
{
/* Verify out-of-bounds access to the local BUF is diagnosed. */
char a0_buf_p2[sizeof (struct A0) + 2 * sizeof (int16_t)];
warn_a0_local_buf ((struct A0*) a0_buf_p2);
sink (a0_buf_p2);
}
{
/* Verify out-of-bounds access to the extern BUF with a known
bound is diagnosed. */
extern char a0_buf_p3[sizeof (struct A0) + 3 * sizeof (int16_t)];
warn_a0_extern_buf ((struct A0*) a0_buf_p3);
sink (a0_buf_p3);
}
{
/* Verify that accesses to BUFX with an unknown bound are not
diagnosed. */
extern char bufx[];
nowarn_a0_extern_bufx ((struct A0*) bufx);
sink (bufx);
}
{
/* Verify that accesses to BUFN with a runtime bound are not
diagnosed. */
char bufn[n];
nowarn_a0_extern_bufx ((struct A0*) bufn);
sink (bufn);
}
nowarn_a0_ref (p);
}
/* Exercise a one-element trailing member array. It's the same as above
except that it has exactly one element. */
struct A1
{
int32_t n;
int16_t a1[1]; // { dg-message "while referencing 'a1'" }
};
static void warn_a1_local_noinit (struct A1 *p)
{
p->a1[0] = 0;
p->a1[1] = 1; // { dg-warning "\\\[-Warray-bounds" }
p->a1[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a1_extern (struct A1 *p)
{
p->a1[0] = 0;
p->a1[1] = 1; // { dg-warning "\\\[-Warray-bounds" }
p->a1[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a1_init (struct A1 *p)
{
p->a1[0] = 0;
p->a1[1] = 1; // { dg-warning "\\\[-Warray-bounds" }
p->a1[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a1_local_buf (struct A1 *p)
{
p->a1[0] = 0; p->a1[1] = 1; p->a1[2] = 2; p->a1[3] = 3;
p->a1[4] = 4; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a1_extern_buf (struct A1 *p)
{
p->a1[0] = 0; p->a1[1] = 1; p->a1[2] = 2; p->a1[3] = 3; p->a1[4] = 4;
p->a1[5] = 5; // { dg-warning "\\\[-Warray-bounds" }
}
static void nowarn_a1_extern_bufx (struct A1 *p)
{
p->a1[0] = 0; p->a1[99] = 99; p->a1[999] = 999; p->a1[9999] = 9999;
}
static void nowarn_a1_ref (struct A1 *p)
{
p->a1[0] = 0; p->a1[99] = 99; p->a1[999] = 999; p->a1[9999] = 9999;
}
void test_a1 (struct A1 *p, unsigned n)
{
{
struct A1 a1;
warn_a1_local_noinit (&a1);
sink (&a1);
}
{
extern struct A1 a1x;
warn_a1_extern (&a1x);
sink (&a1x);
}
{
struct A1 a1 = { 0, { 1 } };
warn_a1_init (&a1);
sink (&a1);
}
{
/* Verify out-of-bounds access to the local BUF is diagnosed. */
char buf_p2[sizeof (struct A1) + 2 * sizeof (int16_t)];
warn_a1_local_buf ((struct A1*) buf_p2);
sink (buf_p2);
}
{
/* Verify out-of-bounds access to the extern BUF with a known
bound is diagnosed. */
extern char a1_buf_p3[sizeof (struct A1) + 3 * sizeof (int16_t)];
warn_a1_extern_buf ((struct A1*) a1_buf_p3);
sink (a1_buf_p3);
}
{
/* Verify that accesses to BUFX with an unknown bound are not
diagnosed. */
extern char bufx[];
nowarn_a1_extern_bufx ((struct A1*) bufx);
sink (bufx);
}
{
/* Verify that accesses to BUFN with a runtime bound are not
diagnosed. */
char bufn[n];
nowarn_a1_extern_bufx ((struct A1*) bufn);
sink (bufn);
}
nowarn_a1_ref (p);
}
/* Exercise a two-element trailing member array. It's treated
the same as an interior array member. */
struct A2
{
int32_t n;
int16_t a2[2]; // { dg-message "while referencing 'a2'" }
};
static void warn_a2_noinit (struct A2 *p)
{
p->a2[0] = 0; p->a2[1] = 1;
p->a2[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a2_init (struct A2 *p)
{
p->a2[0] = 0; p->a2[1] = 1;
p->a2[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
p->a2[9] = 9; // { dg-warning "\\\[-Warray-bounds" }
}
static void warn_a2_ref (struct A2 *p)
{
p->a2[0] = 0; p->a2[1] = 1;
p->a2[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
p->a2[9] = 9; // { dg-warning "\\\[-Warray-bounds" }
}
void test_a2 (struct A2 *p)
{
{
struct A2 a2;
warn_a2_noinit (&a2);
sink (&a2);
}
{
struct A2 a2 = { 0, { 1, 2 } };
warn_a2_init (&a2);
sink (&a2);
}
warn_a2_ref (p);
}

View File

@ -0,0 +1,115 @@
/* PR middle-end/91647 - missing -Warray-bounds accessing a zero-length array
of a declared object
{ dg-do "compile" }
{ dg-options "-O2 -Wall" } */
struct __attribute__ ((aligned (16))) A16
{
__INT64_TYPE__ i8;
__INT16_TYPE__ i2;
__INT16_TYPE__ a2[];
};
struct A16 a0 = { };
void test_a0 (void)
{
// The first three elements fit in the tail padding.
a0.a2[0] = 0; a0.a2[1] = 1; a0.a2[2] = 2;
a0.a2[3] = 3; // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a1 = { .a2 = { 1 } };
void test_a1 (void)
{
a1.a2[0] = 0; a1.a2[1] = 1; a1.a2[2] = 2;
a1.a2[3] = 3; // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a2 = { .a2 = { 1, 2 } };
void test_a2 (void)
{
a2.a2[0] = 0; a2.a2[1] = 1; a2.a2[2] = 2;
a2.a2[3] = 3; // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a3 = { .a2 = { 1, 2, 3 } };
void test_a3 (void)
{
a3.a2[0] = 0; a3.a2[1] = 1; a3.a2[2] = 2;
a3.a2[3] = 3; // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a4 = { .a2 = { 1, 2, 3, 4 } };
void test_a4 (void)
{
a4.a2[0] = 0; a4.a2[1] = 1; a4.a2[2] = 2; a4.a2[3] = 3;
a4.a2[4] = 4; // { dg-warning "array subscript 4 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a5 = { .a2 = { 1, 2, 3, 4, 5 } };
void test_a5 (void)
{
a5.a2[0] = 0; a5.a2[1] = 1; a5.a2[2] = 2; a5.a2[3] = 3; a5.a2[4] = 4;
a5.a2[5] = 5; // { dg-warning "array subscript 5 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a6 = { .a2 = { 1, 2, 3, 4, 5, 6 } };
void test_a6 (void)
{
a6.a2[0] = 0; a6.a2[1] = 1; a6.a2[2] = 2; a6.a2[3] = 3; a6.a2[4] = 4;
a6.a2[5] = 5;
a6.a2[6] = 6; // { dg-warning "array subscript 6 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a7 = { .a2 = { 1, 2, 3, 4, 5, 6, 7 } };
void test_a7 (void)
{
a7.a2[0] = 0; a7.a2[1] = 1; a7.a2[2] = 2; a7.a2[3] = 3; a7.a2[4] = 4;
a7.a2[5] = 5; a7.a2[5] = 5; a7.a2[6] = 6;
a7.a2[7] = 7; // { dg-warning "array subscript 7 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a8 = { .a2 = { 1, 2, 3, 4, 5, 6, 7, 8 } };
void test_a8 (void)
{
a8.a2[0] = 0; a8.a2[1] = 1; a8.a2[2] = 2; a8.a2[3] = 3; a8.a2[4] = 4;
a8.a2[5] = 5; a8.a2[5] = 5; a8.a2[6] = 6; a8.a2[7] = 7;
a8.a2[8] = 8; // { dg-warning "array subscript 8 is above array bounds of 'short int\\\[0]'" }
}
struct A16 a9 = { .a2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 } };
void test_a9 (void)
{
a8.a2[0] = 8; a8.a2[1] = 7; a8.a2[2] = 6; a8.a2[3] = 5; a8.a2[4] = 4;
a8.a2[5] = 3; a8.a2[5] = 2; a8.a2[6] = 1; a8.a2[7] = 0;
a8.a2[9] = 8; // { dg-warning "array subscript 9 is above array bounds of 'short int\\\[0]'" }
}

View File

@ -3,7 +3,7 @@
{ dg-options "-O2 -Wall" } */ { dg-options "-O2 -Wall" } */
struct charseq { struct charseq {
unsigned char bytes[0]; // { dg-message "object declared here" } unsigned char bytes[0]; // { dg-message "while referencing|object declared here" }
}; };
struct locale_ctype_t { struct locale_ctype_t {
@ -15,7 +15,7 @@ void ctype_finish (struct locale_ctype_t *ctype)
long unsigned int cnt; long unsigned int cnt;
for (cnt = 0; cnt < 20; ++cnt) { for (cnt = 0; cnt < 20; ++cnt) {
static struct charseq replace[2]; static struct charseq replace[2];
replace[0].bytes[1] = '\0'; // { dg-warning "\\\[-Wstringop-overflow" } replace[0].bytes[1] = '\0'; // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
ctype->mboutdigits[cnt] = &replace[0]; ctype->mboutdigits[cnt] = &replace[0];
} }
} }

View File

@ -0,0 +1,59 @@
/* PR middle-end/92312 - bogus -Wstringop-overflow storing into a trailing
array backed by larger buffer
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
struct S0 { char a, b[0]; };
void sink (void*);
void test_memset_zero_length (void)
{
char a[3];
struct S0 *p = (struct S0*)a;
p->a = 0;
__builtin_memset (p->b, 0, 2);
sink (p);
__builtin_memset (p->b, 0, 3); // { dg-warning "\\\[-Wstringop-overflow" }
sink (p);
}
void test_store_zero_length (int i)
{
char a[3];
struct S0 *p = (struct S0*)a;
p->a = 0;
p->b[0] = 0;
p->b[1] = 1; // { dg-bogus "\\\[-Wstringop-overflow" }
p->b[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
p->b[i] = 2;
sink (p);
}
struct Sx { char a, b[]; };
void test_memset_flexarray (int i)
{
char a[3];
struct Sx *p = (struct Sx*)a;
p->a = 0;
__builtin_memset (p->b, 0, 2);
sink (p);
__builtin_memset (p->b, 0, 3); // { dg-warning "\\\[-Wstringop-overflow" }
sink (p);
}
void test_store_flexarray (int i)
{
char a[3];
struct Sx *p = (struct Sx*)a;
p->a = 0;
p->b[0] = 0;
p->b[1] = 1; // { dg-bogus "\\\[-Wstringop-overflow" }
p->b[2] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
p->b[i] = 2;
sink (p);
}

View File

@ -0,0 +1,88 @@
/* PR middle-end/91647 - missing -Warray-bounds accessing a zero-length array
of a declared object
Test to exercise -Wzero-length-bounds.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
void sink (void*);
struct X { int a[0]; int b, c; };
extern struct X x;
void bad (int i, int j)
{
x.a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
x.a[1] = 1; // { dg-warning "\\\[-Wzero-length-bounds" }
x.a[2] = 2; // { dg-warning "\\\[-Warray-bounds" }
x.a[i] = 3; // { dg-warning "\\\[-Wzero-length-bounds" }
x.a[j] = 4; // { dg-warning "array subscript 'j' is outside the bounds of an interior zero-length array" }
}
void access_by_reference (struct X *p, int i)
{
p->a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
p->a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
p->a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
p->a[i] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
}
extern struct X a[2];
void access_to_array (int i)
{
a[0].a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
a[0].a[1] = 1; // { dg-warning "\\\[-Wzero-length-bounds" }
/* Accesses to a subsequent element of the enclosing array seem like
a more sever problem than those to the next member of the same
struct and so might perhaps be better diagnosed by -Warray-bounds.
Then again, code that does this sort of crap might as well get what
it deserves if it disables -Wzero-length-bounds. */
a[0].a[2] = 2; // { dg-warning "\\\[-Wzero-length-bounds" }
a[0].a[i] = 3; // { dg-warning "\\\[-Wzero-length-bounds" }
sink (a);
a[1].a[0] = 4; // { dg-warning "\\\[-Wzero-length-bounds" }
a[1].a[1] = 5; // { dg-warning "\\\[-Wzero-length-bounds" }
a[1].a[2] = 6; // { dg-warning "\\\[-Warray-bounds" }
a[1].a[i] = 7; // { dg-warning "\\\[-Wzero-length-bounds" }
sink (a);
a[i].a[0] = 8; // { dg-warning "\\\[-Wzero-length-bounds" }
a[i].a[1] = 9; // { dg-warning "\\\[-Wzero-length-bounds" }
a[i].a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
}
struct Y
{
struct X a[2], b;
int c;
};
extern struct Y y;
void access_to_member (int i)
{
y.a[0].a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
y.a[0].a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
y.a[0].a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
sink (a);
y.a[1].a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
y.a[1].a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
/* Similar to the array case above, accesses to a subsequent member
of the "parent" struct seem like a more severe problem than those
to the next member of the same struct. */
y.a[1].a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
sink (a);
y.b.a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
y.b.a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
y.b.a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" }
y.b.a[3] = 0; // { dg-warning "\\\[-Warray-bounds" }
}

View File

@ -44,7 +44,7 @@ foo2(unsigned char * to, const unsigned char * from, int n)
*to = *from; *to = *from;
break; break;
case 5: case 5:
to[4] = from [4]; /* { dg-warning "array subscript is above array bounds" "" { xfail *-*-* } } */ to[4] = from [4]; /* { dg-warning "\\\[-Warray-bounds } */
break; break;
} }
return to; return to;

View File

@ -21,7 +21,7 @@ void test_var_flexarray_cst_off (void)
{ {
/* Use arbitrary constants greater than 16 in case GCC ever starts /* Use arbitrary constants greater than 16 in case GCC ever starts
unrolling strlen() calls with small array arguments. */ unrolling strlen() calls with small array arguments. */
a[0] = 17 < strlen (a0.a + 1); a[0] = 17 < strlen (a0.a + 1); // { dg-warning "\\\[-Warray-bounds" }
a[1] = 19 < strlen (a1.a + 1); a[1] = 19 < strlen (a1.a + 1);
a[2] = 23 < strlen (a9.a + 9); a[2] = 23 < strlen (a9.a + 9);
a[3] = 29 < strlen (ax.a + 3); a[3] = 29 < strlen (ax.a + 3);

View File

@ -4122,7 +4122,6 @@ bool
vrp_prop::check_array_ref (location_t location, tree ref, vrp_prop::check_array_ref (location_t location, tree ref,
bool ignore_off_by_one) bool ignore_off_by_one)
{ {
const value_range *vr = NULL;
tree low_sub, up_sub; tree low_sub, up_sub;
tree low_bound, up_bound, up_bound_p1; tree low_bound, up_bound, up_bound_p1;
@ -4132,6 +4131,9 @@ vrp_prop::check_array_ref (location_t location, tree ref,
low_sub = up_sub = TREE_OPERAND (ref, 1); low_sub = up_sub = TREE_OPERAND (ref, 1);
up_bound = array_ref_up_bound (ref); up_bound = array_ref_up_bound (ref);
/* Set for accesses to interior zero-length arrays. */
bool interior_zero_len = false;
if (!up_bound if (!up_bound
|| TREE_CODE (up_bound) != INTEGER_CST || TREE_CODE (up_bound) != INTEGER_CST
|| (warn_array_bounds < 2 || (warn_array_bounds < 2
@ -4152,11 +4154,22 @@ vrp_prop::check_array_ref (location_t location, tree ref,
} }
else else
{ {
tree maxbound = TYPE_MAX_VALUE (ptrdiff_type_node); tree ptrdiff_max = TYPE_MAX_VALUE (ptrdiff_type_node);
tree maxbound = ptrdiff_max;
tree arg = TREE_OPERAND (ref, 0); tree arg = TREE_OPERAND (ref, 0);
poly_int64 off; poly_int64 off;
if (get_addr_base_and_unit_offset (arg, &off) && known_gt (off, 0)) if (TREE_CODE (arg) == COMPONENT_REF)
{
/* Try to determine the size of the trailing array from
its initializer (if it has one). */
if (tree refsize = component_ref_size (arg, &interior_zero_len))
maxbound = refsize;
}
if (maxbound == ptrdiff_max
&& get_addr_base_and_unit_offset (arg, &off)
&& known_gt (off, 0))
maxbound = wide_int_to_tree (sizetype, maxbound = wide_int_to_tree (sizetype,
wi::sub (wi::to_wide (maxbound), wi::sub (wi::to_wide (maxbound),
off)); off));
@ -4185,6 +4198,7 @@ vrp_prop::check_array_ref (location_t location, tree ref,
"array subscript %E is above array bounds of %qT", "array subscript %E is above array bounds of %qT",
low_bound, artype); low_bound, artype);
const value_range *vr = NULL;
if (TREE_CODE (low_sub) == SSA_NAME) if (TREE_CODE (low_sub) == SSA_NAME)
{ {
vr = get_value_range (low_sub); vr = get_value_range (low_sub);
@ -4195,7 +4209,9 @@ vrp_prop::check_array_ref (location_t location, tree ref,
} }
} }
if (vr && vr->kind () == VR_ANTI_RANGE) if (warned)
; /* Do nothing. */
else if (vr && vr->kind () == VR_ANTI_RANGE)
{ {
if (up_bound if (up_bound
&& TREE_CODE (up_sub) == INTEGER_CST && TREE_CODE (up_sub) == INTEGER_CST
@ -4214,39 +4230,51 @@ vrp_prop::check_array_ref (location_t location, tree ref,
&& (ignore_off_by_one && (ignore_off_by_one
? !tree_int_cst_le (up_sub, up_bound_p1) ? !tree_int_cst_le (up_sub, up_bound_p1)
: !tree_int_cst_le (up_sub, up_bound))) : !tree_int_cst_le (up_sub, up_bound)))
{ warned = warning_at (location, OPT_Warray_bounds,
if (dump_file && (dump_flags & TDF_DETAILS)) "array subscript %E is above array bounds of %qT",
{ up_sub, artype);
fprintf (dump_file, "Array bound warning for ");
dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
fprintf (dump_file, "\n");
}
warned = warning_at (location, OPT_Warray_bounds,
"array subscript %E is above array bounds of %qT",
up_sub, artype);
}
else if (TREE_CODE (low_sub) == INTEGER_CST else if (TREE_CODE (low_sub) == INTEGER_CST
&& tree_int_cst_lt (low_sub, low_bound)) && tree_int_cst_lt (low_sub, low_bound))
{ warned = warning_at (location, OPT_Warray_bounds,
if (dump_file && (dump_flags & TDF_DETAILS)) "array subscript %E is below array bounds of %qT",
{ low_sub, artype);
fprintf (dump_file, "Array bound warning for ");
dump_generic_expr (MSG_NOTE, TDF_SLIM, ref); if (!warned && interior_zero_len)
fprintf (dump_file, "\n"); warned = warning_at (location, OPT_Wzero_length_bounds,
} (TREE_CODE (low_sub) == INTEGER_CST
warned = warning_at (location, OPT_Warray_bounds, ? G_("array subscript %E is outside the bounds "
"array subscript %E is below array bounds of %qT", "of an interior zero-length array %qT")
low_sub, artype); : G_("array subscript %qE is outside the bounds "
} "of an interior zero-length array %qT")),
low_sub, artype);
if (warned) if (warned)
{ {
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Array bound warning for ");
dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
fprintf (dump_file, "\n");
}
ref = TREE_OPERAND (ref, 0); ref = TREE_OPERAND (ref, 0);
tree rec = NULL_TREE;
if (TREE_CODE (ref) == COMPONENT_REF) if (TREE_CODE (ref) == COMPONENT_REF)
ref = TREE_OPERAND (ref, 1); {
/* For a reference to a member of a struct object also mention
the object if it's known. It may be defined in a different
function than the out-of-bounds access. */
rec = TREE_OPERAND (ref, 0);
if (!VAR_P (rec))
rec = NULL_TREE;
ref = TREE_OPERAND (ref, 1);
}
if (DECL_P (ref)) if (DECL_P (ref))
inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref); inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
if (rec && DECL_P (rec))
inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec);
TREE_NO_WARNING (ref) = 1; TREE_NO_WARNING (ref) = 1;
} }
@ -4391,16 +4419,21 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
/* The type of the object being referred to. It can be an array, /* The type of the object being referred to. It can be an array,
string literal, or a non-array type when the MEM_REF represents string literal, or a non-array type when the MEM_REF represents
a reference/subscript via a pointer to an object that is not a reference/subscript via a pointer to an object that is not
an element of an array. References to members of structs and an element of an array. Incomplete types are excluded as well
unions are excluded because MEM_REF doesn't make it possible because their size is not known. */
to identify the member where the reference originated.
Incomplete types are excluded as well because their size is
not known. */
tree reftype = TREE_TYPE (arg); tree reftype = TREE_TYPE (arg);
if (POINTER_TYPE_P (reftype) if (POINTER_TYPE_P (reftype)
|| !COMPLETE_TYPE_P (reftype) || !COMPLETE_TYPE_P (reftype)
|| TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
|| RECORD_OR_UNION_TYPE_P (reftype)) return false;
/* Except in declared objects, references to trailing array members
of structs and union objects are excluded because MEM_REF doesn't
make it possible to identify the member where the reference
originated. */
if (RECORD_OR_UNION_TYPE_P (reftype)
&& (!VAR_P (arg)
|| (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref))))
return false; return false;
arrbounds[0] = 0; arrbounds[0] = 0;
@ -4412,7 +4445,14 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
if (tree dom = TYPE_DOMAIN (reftype)) if (tree dom = TYPE_DOMAIN (reftype))
{ {
tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) }; tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1]) if (TREE_CODE (arg) == COMPONENT_REF)
{
offset_int size = maxobjsize;
if (tree fldsize = component_ref_size (arg))
size = wi::to_offset (fldsize);
arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize));
}
else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize)); arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
else else
arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0]) arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
@ -4434,7 +4474,13 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
else else
{ {
eltsize = 1; eltsize = 1;
arrbounds[1] = wi::to_offset (TYPE_SIZE_UNIT (reftype)); tree size = TYPE_SIZE_UNIT (reftype);
if (VAR_P (arg))
if (tree initsize = DECL_SIZE_UNIT (arg))
if (tree_int_cst_lt (size, initsize))
size = initsize;
arrbounds[1] = wi::to_offset (size);
} }
offrange[0] += ioff; offrange[0] += ioff;

View File

@ -3089,6 +3089,25 @@ first_field (const_tree type)
return t; return t;
} }
/* Returns the last FIELD_DECL in the TYPE_FIELDS of the RECORD_TYPE or
UNION_TYPE TYPE, or NULL_TREE if none. */
tree
last_field (const_tree type)
{
tree last = NULL_TREE;
for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
{
if (TREE_CODE (fld) != FIELD_DECL)
continue;
last = fld;
}
return last;
}
/* Concatenate two chains of nodes (chained through TREE_CHAIN) /* Concatenate two chains of nodes (chained through TREE_CHAIN)
by modifying the last node in chain 1 to point to chain 2. by modifying the last node in chain 1 to point to chain 2.
This is the Lisp primitive `nconc'. */ This is the Lisp primitive `nconc'. */
@ -13363,8 +13382,8 @@ array_ref_up_bound (tree exp)
return NULL_TREE; return NULL_TREE;
} }
/* Returns true if REF is an array reference or a component reference /* Returns true if REF is an array reference, component reference,
to an array at the end of a structure. or memory reference to an array at the end of a structure.
If this is the case, the array may be allocated larger If this is the case, the array may be allocated larger
than its upper bound implies. */ than its upper bound implies. */
@ -13382,6 +13401,28 @@ array_at_struct_end_p (tree ref)
else if (TREE_CODE (ref) == COMPONENT_REF else if (TREE_CODE (ref) == COMPONENT_REF
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 1))) == ARRAY_TYPE) && TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 1))) == ARRAY_TYPE)
atype = TREE_TYPE (TREE_OPERAND (ref, 1)); atype = TREE_TYPE (TREE_OPERAND (ref, 1));
else if (TREE_CODE (ref) == MEM_REF)
{
tree arg = TREE_OPERAND (ref, 0);
if (TREE_CODE (arg) == ADDR_EXPR)
arg = TREE_OPERAND (arg, 0);
tree argtype = TREE_TYPE (arg);
if (TREE_CODE (argtype) == RECORD_TYPE)
{
if (tree fld = last_field (argtype))
{
atype = TREE_TYPE (fld);
if (TREE_CODE (atype) != ARRAY_TYPE)
return false;
if (VAR_P (arg) && DECL_SIZE (fld))
return false;
}
else
return false;
}
else
return false;
}
else else
return false; return false;
@ -13498,33 +13539,72 @@ component_ref_field_offset (tree exp)
return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp); return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp);
} }
/* Given the initializer INIT, return the initializer for the field
DECL if it exists, otherwise null. Used to obtain the initializer
for a flexible array member and determine its size. */
static tree
get_initializer_for (tree init, tree decl)
{
STRIP_NOPS (init);
tree fld, fld_init;
unsigned HOST_WIDE_INT i;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), i, fld, fld_init)
{
if (decl == fld)
return fld_init;
if (TREE_CODE (fld) == CONSTRUCTOR)
{
fld_init = get_initializer_for (fld_init, decl);
if (fld_init)
return fld_init;
}
}
return NULL_TREE;
}
/* Determines the size of the member referenced by the COMPONENT_REF /* Determines the size of the member referenced by the COMPONENT_REF
REF, using its initializer expression if necessary in order to REF, using its initializer expression if necessary in order to
determine the size of an initialized flexible array member. determine the size of an initialized flexible array member.
If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
an interior zero-length array.
Returns the size (which might be zero for an object with Returns the size (which might be zero for an object with
an uninitialized flexible array member) or null if the size an uninitialized flexible array member) or null if the size
cannot be determined. */ cannot be determined. */
tree tree
component_ref_size (tree ref) component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
{ {
gcc_assert (TREE_CODE (ref) == COMPONENT_REF); gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
bool int_0_len = false;
if (!interior_zero_length)
interior_zero_length = &int_0_len;
tree member = TREE_OPERAND (ref, 1); tree member = TREE_OPERAND (ref, 1);
/* If the member is not an array, or is not last, or is an array with tree memsize = DECL_SIZE_UNIT (member);
more than one element, return its size. Otherwise it's either if (memsize)
a bona fide flexible array member, or a zero-length array member,
or an array of length one treated as such. */
tree size = DECL_SIZE_UNIT (member);
if (size)
{ {
tree memtype = TREE_TYPE (member); tree memtype = TREE_TYPE (member);
if (TREE_CODE (memtype) != ARRAY_TYPE if (TREE_CODE (memtype) != ARRAY_TYPE)
|| !array_at_struct_end_p (ref)) return memsize;
return size;
if (!integer_zerop (size)) bool trailing = array_at_struct_end_p (ref);
bool zero_length = integer_zerop (memsize);
if (!trailing && (!interior_zero_length || !zero_length))
/* MEMBER is either an interior array or is an array with
more than one element. */
return memsize;
*interior_zero_length = zero_length && !trailing;
if (*interior_zero_length)
memsize = NULL_TREE;
if (!zero_length)
if (tree dom = TYPE_DOMAIN (memtype)) if (tree dom = TYPE_DOMAIN (memtype))
if (tree min = TYPE_MIN_VALUE (dom)) if (tree min = TYPE_MIN_VALUE (dom))
if (tree max = TYPE_MAX_VALUE (dom)) if (tree max = TYPE_MAX_VALUE (dom))
@ -13533,37 +13613,120 @@ component_ref_size (tree ref)
{ {
offset_int minidx = wi::to_offset (min); offset_int minidx = wi::to_offset (min);
offset_int maxidx = wi::to_offset (max); offset_int maxidx = wi::to_offset (max);
if (maxidx - minidx > 1) if (maxidx - minidx > 0)
return size; /* MEMBER is an array with more than 1 element. */
return memsize;
} }
} }
/* MEMBER is either a bona fide flexible array member, or a zero-length
array member, or an array of length one treated as such. */
/* If the reference is to a declared object and the member a true /* If the reference is to a declared object and the member a true
flexible array, try to determine its size from its initializer. */ flexible array, try to determine its size from its initializer. */
poly_int64 off = 0; poly_int64 baseoff = 0;
tree base = get_addr_base_and_unit_offset (ref, &off); tree base = get_addr_base_and_unit_offset (ref, &baseoff);
if (!base || !VAR_P (base)) if (!base || !VAR_P (base))
return NULL_TREE; {
if (!*interior_zero_length)
return NULL_TREE;
/* The size of any member of a declared object other than a flexible if (TREE_CODE (TREE_OPERAND (ref, 0)) != COMPONENT_REF)
array member is that obtained above. */ return NULL_TREE;
if (size)
return size;
if (tree init = DECL_INITIAL (base)) base = TREE_OPERAND (ref, 0);
if (TREE_CODE (init) == CONSTRUCTOR) baseoff = tree_to_poly_int64 (byte_position (TREE_OPERAND (ref, 1)));
{ }
off <<= LOG2_BITS_PER_UNIT;
init = fold_ctor_reference (NULL_TREE, init, off, 0, base); /* BASE is the declared object of which MEMBER is either a member
if (init) or that is is cast to REFTYPE (e.g., a char buffer used to store
return TYPE_SIZE_UNIT (TREE_TYPE (init)); a REFTYPE object). */
} tree reftype = TREE_TYPE (TREE_OPERAND (ref, 0));
tree basetype = TREE_TYPE (base);
/* Determine the base type of the referenced object. If it's
the same as REFTYPE and MEMBER has a known size, return it. */
tree bt = basetype;
if (!*interior_zero_length)
while (TREE_CODE (bt) == ARRAY_TYPE)
bt = TREE_TYPE (bt);
bool typematch = useless_type_conversion_p (reftype, bt);
if (memsize && typematch)
return memsize;
memsize = NULL_TREE;
/* MEMBER is a true flexible array member. Compute its size from
the initializer of the BASE object if it has one. */
if (tree init = DECL_P (base) ? DECL_INITIAL (base) : NULL_TREE)
{
init = get_initializer_for (init, member);
if (init)
{
memsize = TYPE_SIZE_UNIT (TREE_TYPE (init));
if (tree refsize = TYPE_SIZE_UNIT (reftype))
{
/* Use the larger of the initializer size and the tail
padding in the enclosing struct. */
poly_int64 rsz = tree_to_poly_int64 (refsize);
rsz -= baseoff;
if (known_lt (tree_to_poly_int64 (memsize), rsz))
memsize = wide_int_to_tree (TREE_TYPE (memsize), rsz);
}
baseoff = 0;
}
}
if (!memsize)
{
if (typematch)
{
if (DECL_P (base)
&& DECL_EXTERNAL (base)
&& bt == basetype
&& !*interior_zero_length)
/* The size of a flexible array member of an extern struct
with no initializer cannot be determined (it's defined
in another translation unit and can have an initializer
witth an arbitrary number of elements). */
return NULL_TREE;
/* Use the size of the base struct or, for interior zero-length
arrays, the size of the enclosing type. */
memsize = TYPE_SIZE_UNIT (bt);
}
else
/* Use the size of the BASE object (possibly an array of some
other type such as char used to store the struct). */
memsize = DECL_SIZE_UNIT (base);
}
/* If the flexible array member has a known size use the greater
of it and the tail padding in the enclosing struct.
Otherwise, when the size of the flexible array member is unknown
and the referenced object is not a struct, use the size of its
type when known. This detects sizes of array buffers when cast
to struct types with flexible array members. */
if (memsize)
{
poly_int64 memsz64 = memsize ? tree_to_poly_int64 (memsize) : 0;
if (known_lt (baseoff, memsz64))
{
memsz64 -= baseoff;
return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
}
return integer_zero_node;
}
/* Return "don't know" for an external non-array object since its /* Return "don't know" for an external non-array object since its
flexible array member can be initialized to have any number of flexible array member can be initialized to have any number of
elements. Otherwise, return zero because the flexible array elements. Otherwise, return zero because the flexible array
member has no elements. */ member has no elements. */
return (DECL_EXTERNAL (base) && TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE return (DECL_P (base)
&& DECL_EXTERNAL (base)
&& (!typematch
|| TREE_CODE (basetype) != ARRAY_TYPE)
? NULL_TREE : integer_zero_node); ? NULL_TREE : integer_zero_node);
} }

View File

@ -5262,7 +5262,7 @@ extern tree component_ref_field_offset (tree);
of an initialized flexible array member. The size might be zero for of an initialized flexible array member. The size might be zero for
an object with an uninitialized flexible array member or null if it an object with an uninitialized flexible array member or null if it
cannot be determined. */ cannot be determined. */
extern tree component_ref_size (tree); extern tree component_ref_size (tree, bool * = NULL);
extern int tree_map_base_eq (const void *, const void *); extern int tree_map_base_eq (const void *, const void *);
extern unsigned int tree_map_base_hash (const void *); extern unsigned int tree_map_base_hash (const void *);

View File

@ -852,6 +852,8 @@ inline HOST_WIDE_INT
generic_wide_int <storage>::sign_mask () const generic_wide_int <storage>::sign_mask () const
{ {
unsigned int len = this->get_len (); unsigned int len = this->get_len ();
gcc_assert (len > 0);
unsigned HOST_WIDE_INT high = this->get_val ()[len - 1]; unsigned HOST_WIDE_INT high = this->get_val ()[len - 1];
if (!is_sign_extended) if (!is_sign_extended)
{ {