From: Martin Sebor Date: Fri, 1 Nov 2019 21:09:20 +0000 (+0000) Subject: PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=49fb45c81f4ac068d9fb859968d8f223bc438251;p=gcc.git 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 ::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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bb7819e1fea..796f5e9ec91 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2019-11-01 Martin Sebor + + 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 ::sign_mask): Assert invariant. + 2019-11-01 Kewen Lin * config/rs6000/rs6000-modes.def (V2SF, V2SI): New modes. diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c index 1b06cc20f68..bc7354559ba 100644 --- a/gcc/c-family/c-pretty-print.c +++ b/gcc/c-family/c-pretty-print.c @@ -581,16 +581,20 @@ c_pretty_printer::direct_abstract_declarator (tree t) case ARRAY_TYPE: 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)); - tree type = TREE_TYPE (maxval); + if (tree maxval = TYPE_MAX_VALUE (dom)) + { + tree type = TREE_TYPE (maxval); - if (tree_fits_shwi_p (maxval)) - pp_wide_integer (this, tree_to_shwi (maxval) + 1); + if (tree_fits_shwi_p (maxval)) + pp_wide_integer (this, tree_to_shwi (maxval) + 1); + else + expression (fold_build2 (PLUS_EXPR, type, maxval, + build_int_cst (type, 1))); + } else - expression (fold_build2 (PLUS_EXPR, type, maxval, - build_int_cst (type, 1))); + pp_string (this, "0"); } pp_c_right_bracket (this); direct_abstract_declarator (TREE_TYPE (t)); diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 495eb16a58a..bb6eeaf1523 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -338,6 +338,10 @@ Warray-bounds= LangEnabledBy(C ObjC C++ LTO ObjC++,Wall,1,0) ; 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 ObjC ObjC++ Var(warn_assign_intercept) Warning Warn whenever an Objective-C assignment is being intercepted by the garbage collector. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d1eb317f43c..faa7fa95a0e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -325,6 +325,7 @@ Objective-C and Objective-C++ Dialects}. -Winaccessible-base @gol -Winit-self -Winline -Wno-int-conversion -Wint-in-bool-context @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 -Wlogical-op -Wlogical-not-parentheses -Wlong-long @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-function-declaration @r{(C and Objective-C only)} @gol -Winit-self @r{(only for C++)} @gol +-Wzero-length-bounds @gol -Wlogical-not-parentheses @gol -Wmain @r{(only for C/ObjC and unless} @option{-ffreestanding}@r{)} @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 @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 @opindex Wno-div-by-zero @opindex Wdiv-by-zero diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c index 53278168a59..d7c74a1865a 100644 --- a/gcc/gimple-match-head.c +++ b/gcc/gimple-match-head.c @@ -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], res_op->ops[num_ops - 1]), 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) { case 2: diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 8b03bd9913e..f4e18db6111 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,19 @@ +2019-11-01 Martin Sebor + + 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 * gfortran.dg/byte_3.f: New test. diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-2.c b/gcc/testsuite/c-c++-common/Warray-bounds-2.c index ca2d1c98218..7f925c34bae 100644 --- a/gcc/testsuite/c-c++-common/Warray-bounds-2.c +++ b/gcc/testsuite/c-c++-common/Warray-bounds-2.c @@ -6,7 +6,7 @@ source of the excessive array bound is in a different function than the call. { 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 () # include @@ -216,13 +216,13 @@ void call_strncpy_dst_diff_max (const char *s, size_t n) static void 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" } */ -} /* { dg-warning "array subscript -1 is outside array bounds" "" { target *-*-* } .-1 } */ + 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" } */ +} void call_strncpy_dstarray_diff_neg (const char *s, size_t n) { - struct Array ar10[2]; /* { dg-bogus ".ar10. declared here" } */ - sink (&ar10); /* { dg-message "while referencing" "" { target *-*-* } .-1 } */ + struct Array ar10[2]; /* { dg-message ".ar10. declared here" } */ + sink (&ar10); int off = (char*)ar10[1].a17 - (char*)ar10 + 1; wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n); diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C index 69226fa632a..6db20135668 100644 --- a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C @@ -13,7 +13,7 @@ void sink (void*); struct Ax { 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. @@ -21,9 +21,9 @@ Ax ax_; void gax_ () { - ax_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } + ax_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } } // Verify warning for access to a definition with an initializer that doesn't @@ -32,9 +32,9 @@ Ax ax0 = { 0 }; void gax0 () { - ax0.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax0.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax0.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } + ax0.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax0.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax0.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } } // Verify warning for access to a definition with an initializer that @@ -43,9 +43,9 @@ Ax ax0_ = { 0, { } }; void gax0_ () { - ax0_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax0_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax0_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } + ax0_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax0_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax0_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } } // Verify warning for out-of-bounds accesses to a definition with @@ -55,8 +55,8 @@ Ax ax1 = { 1, { 0 } }; void gax1 () { ax1.a[0] = 0; - ax1.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } - ax1.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } } + ax1.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } + ax1.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } } Ax ax2 = { 2, { 1, 0 } }; @@ -65,7 +65,7 @@ void gax2 () { ax2.a[0] = 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 { 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. - 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 // 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) diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-46.c b/gcc/testsuite/gcc.dg/Warray-bounds-46.c index 09b577e6363..4980f93a470 100644 --- a/gcc/testsuite/gcc.dg/Warray-bounds-46.c +++ b/gcc/testsuite/gcc.dg/Warray-bounds-46.c @@ -67,7 +67,7 @@ void strcpy_global (void) 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, 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 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, 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, 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\\\[0]' at offset 157" } /* IGMA_ is internal and provides on definition for the flexible array member. Verify that a warnin is issued for out-of-bounds accesses 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, 1); @@ -134,7 +134,7 @@ void strcpy_local (void) 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.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 of the array which could in theory have nonzero elements without 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, 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)[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 + 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[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[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\\\[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\\\[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\\\[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\\\[0]' at offset 628" } 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" } diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-48.c b/gcc/testsuite/gcc.dg/Warray-bounds-48.c new file mode 100644 index 00000000000..d6a327e3689 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-48.c @@ -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); +} diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-49.c b/gcc/testsuite/gcc.dg/Warray-bounds-49.c new file mode 100644 index 00000000000..7a847acf4d2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-49.c @@ -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]'" } +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-16.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-16.c index 74548a4f0fa..11fb05e730a 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-16.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-16.c @@ -3,7 +3,7 @@ { dg-options "-O2 -Wall" } */ 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 { @@ -15,7 +15,7 @@ void ctype_finish (struct locale_ctype_t *ctype) long unsigned int cnt; for (cnt = 0; cnt < 20; ++cnt) { 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]; } } diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-21.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-21.c new file mode 100644 index 00000000000..3a27460220c --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-21.c @@ -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); +} diff --git a/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c new file mode 100644 index 00000000000..8e880d92dea --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c @@ -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" } +} diff --git a/gcc/testsuite/gcc.dg/pr36902.c b/gcc/testsuite/gcc.dg/pr36902.c index a065124ae71..cc8650ccb3b 100644 --- a/gcc/testsuite/gcc.dg/pr36902.c +++ b/gcc/testsuite/gcc.dg/pr36902.c @@ -44,7 +44,7 @@ foo2(unsigned char * to, const unsigned char * from, int n) *to = *from; break; case 5: - to[4] = from [4]; /* { dg-warning "array subscript is above array bounds" "" { xfail *-*-* } } */ + to[4] = from [4]; /* { dg-warning "\\\[-Warray-bounds } */ break; } return to; diff --git a/gcc/testsuite/gcc.dg/strlenopt-57.c b/gcc/testsuite/gcc.dg/strlenopt-57.c index 49dc8cd6fbb..b7212bcf795 100644 --- a/gcc/testsuite/gcc.dg/strlenopt-57.c +++ b/gcc/testsuite/gcc.dg/strlenopt-57.c @@ -21,7 +21,7 @@ void test_var_flexarray_cst_off (void) { /* Use arbitrary constants greater than 16 in case GCC ever starts 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[2] = 23 < strlen (a9.a + 9); a[3] = 29 < strlen (ax.a + 3); diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index ad9be74daf0..31258615247 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -4122,7 +4122,6 @@ bool vrp_prop::check_array_ref (location_t location, tree ref, bool ignore_off_by_one) { - const value_range *vr = NULL; tree low_sub, up_sub; 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); up_bound = array_ref_up_bound (ref); + /* Set for accesses to interior zero-length arrays. */ + bool interior_zero_len = false; + if (!up_bound || TREE_CODE (up_bound) != INTEGER_CST || (warn_array_bounds < 2 @@ -4152,11 +4154,22 @@ vrp_prop::check_array_ref (location_t location, tree ref, } 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); 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, wi::sub (wi::to_wide (maxbound), off)); @@ -4185,6 +4198,7 @@ vrp_prop::check_array_ref (location_t location, tree ref, "array subscript %E is above array bounds of %qT", low_bound, artype); + const value_range *vr = NULL; if (TREE_CODE (low_sub) == SSA_NAME) { 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 && TREE_CODE (up_sub) == INTEGER_CST @@ -4214,19 +4230,25 @@ vrp_prop::check_array_ref (location_t location, tree ref, && (ignore_off_by_one ? !tree_int_cst_le (up_sub, up_bound_p1) : !tree_int_cst_le (up_sub, up_bound))) - { - 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"); - } - warned = warning_at (location, OPT_Warray_bounds, - "array subscript %E is above array bounds of %qT", - up_sub, artype); - } + 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 && tree_int_cst_lt (low_sub, low_bound)) + warned = warning_at (location, OPT_Warray_bounds, + "array subscript %E is below array bounds of %qT", + low_sub, artype); + + if (!warned && interior_zero_len) + warned = warning_at (location, OPT_Wzero_length_bounds, + (TREE_CODE (low_sub) == INTEGER_CST + ? G_("array subscript %E is outside the bounds " + "of an interior zero-length array %qT") + : G_("array subscript %qE is outside the bounds " + "of an interior zero-length array %qT")), + low_sub, artype); + + if (warned) { if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -4234,19 +4256,25 @@ vrp_prop::check_array_ref (location_t location, tree ref, dump_generic_expr (MSG_NOTE, TDF_SLIM, ref); fprintf (dump_file, "\n"); } - warned = warning_at (location, OPT_Warray_bounds, - "array subscript %E is below array bounds of %qT", - low_sub, artype); - } - if (warned) - { ref = TREE_OPERAND (ref, 0); + + tree rec = NULL_TREE; 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)) 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; } @@ -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, string literal, or a non-array type when the MEM_REF represents a reference/subscript via a pointer to an object that is not - an element of an array. References to members of structs and - unions are excluded because MEM_REF doesn't make it possible - to identify the member where the reference originated. - Incomplete types are excluded as well because their size is - not known. */ + an element of an array. Incomplete types are excluded as well + because their size is not known. */ tree reftype = TREE_TYPE (arg); if (POINTER_TYPE_P (reftype) || !COMPLETE_TYPE_P (reftype) - || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST - || RECORD_OR_UNION_TYPE_P (reftype)) + || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST) + 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; arrbounds[0] = 0; @@ -4412,7 +4445,14 @@ vrp_prop::check_mem_ref (location_t location, tree ref, if (tree dom = TYPE_DOMAIN (reftype)) { 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)); else 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 { 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; diff --git a/gcc/tree.c b/gcc/tree.c index 741f7a2ce65..3299b344ea6 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -3089,6 +3089,25 @@ first_field (const_tree type) 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) by modifying the last node in chain 1 to point to chain 2. This is the Lisp primitive `nconc'. */ @@ -13363,8 +13382,8 @@ array_ref_up_bound (tree exp) return NULL_TREE; } -/* Returns true if REF is an array reference or a component reference - to an array at the end of a structure. +/* Returns true if REF is an array reference, component reference, + or memory reference to an array at the end of a structure. If this is the case, the array may be allocated larger than its upper bound implies. */ @@ -13382,6 +13401,28 @@ array_at_struct_end_p (tree ref) else if (TREE_CODE (ref) == COMPONENT_REF && TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 1))) == ARRAY_TYPE) 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 return false; @@ -13498,33 +13539,72 @@ component_ref_field_offset (tree 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 REF, using its initializer expression if necessary in order to 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 an uninitialized flexible array member) or null if the size cannot be determined. */ tree -component_ref_size (tree ref) +component_ref_size (tree ref, bool *interior_zero_length /* = NULL */) { 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); - /* If the member is not an array, or is not last, or is an array with - more than one element, return its size. Otherwise it's either - 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 memsize = DECL_SIZE_UNIT (member); + if (memsize) { tree memtype = TREE_TYPE (member); - if (TREE_CODE (memtype) != ARRAY_TYPE - || !array_at_struct_end_p (ref)) - return size; + if (TREE_CODE (memtype) != ARRAY_TYPE) + return memsize; + + 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 (!integer_zerop (size)) + if (!zero_length) if (tree dom = TYPE_DOMAIN (memtype)) if (tree min = TYPE_MIN_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 maxidx = wi::to_offset (max); - if (maxidx - minidx > 1) - return size; + if (maxidx - minidx > 0) + /* 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 flexible array, try to determine its size from its initializer. */ - poly_int64 off = 0; - tree base = get_addr_base_and_unit_offset (ref, &off); + poly_int64 baseoff = 0; + tree base = get_addr_base_and_unit_offset (ref, &baseoff); 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 - array member is that obtained above. */ - if (size) - return size; + if (TREE_CODE (TREE_OPERAND (ref, 0)) != COMPONENT_REF) + return NULL_TREE; - if (tree init = DECL_INITIAL (base)) - if (TREE_CODE (init) == CONSTRUCTOR) - { - off <<= LOG2_BITS_PER_UNIT; - init = fold_ctor_reference (NULL_TREE, init, off, 0, base); - if (init) - return TYPE_SIZE_UNIT (TREE_TYPE (init)); - } + base = TREE_OPERAND (ref, 0); + baseoff = tree_to_poly_int64 (byte_position (TREE_OPERAND (ref, 1))); + } + + /* BASE is the declared object of which MEMBER is either a member + or that is is cast to REFTYPE (e.g., a char buffer used to store + 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 flexible array member can be initialized to have any number of elements. Otherwise, return zero because the flexible array 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); } diff --git a/gcc/tree.h b/gcc/tree.h index 81351548ecd..2ce001d5468 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5262,7 +5262,7 @@ extern tree component_ref_field_offset (tree); of an initialized flexible array member. The size might be zero for an object with an uninitialized flexible array member or null if it 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 unsigned int tree_map_base_hash (const void *); diff --git a/gcc/wide-int.h b/gcc/wide-int.h index 862079a8549..5e0f444f9a6 100644 --- a/gcc/wide-int.h +++ b/gcc/wide-int.h @@ -852,6 +852,8 @@ inline HOST_WIDE_INT generic_wide_int ::sign_mask () const { unsigned int len = this->get_len (); + gcc_assert (len > 0); + unsigned HOST_WIDE_INT high = this->get_val ()[len - 1]; if (!is_sign_extended) {