mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-01-31 23:40:28 +08:00
PR tree-optimization/91457 - inconsistent warning for writing past the end of an array member
gcc/ChangeLog: PR tree-optimization/91457 * builtins.c (component_size): New function. (compute_objsize): Add argument. Handle ARRAY_REF and COMPONENT_REF. * builtins.h (compute_objsize): Add argument. * tree-ssa-strlen.c (handle_store): Handle no-warning bit. * tree-vrp.c (vrp_prop::check_array_ref): Return warning result. (vrp_prop::check_mem_ref): Same. (vrp_prop::search_for_addr_array): Set no-warning bit. (check_array_bounds): Same. gcc/testsuite/ChangeLog: PR tree-optimization/91457 * c-c++-common/Wstringop-overflow-2.c: New test. * g++.dg/warn/Warray-bounds-8.C: New test. * g++.dg/warn/Wstringop-overflow-3.C: New test. * gcc.dg/Wstringop-overflow-15.c: New test. From-SVN: r274997
This commit is contained in:
parent
e2eee23981
commit
464969eb9b
@ -1,3 +1,15 @@
|
||||
2019-08-28 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR tree-optimization/91457
|
||||
* builtins.c (component_size): New function.
|
||||
(compute_objsize): Add argument. Handle ARRAY_REF and COMPONENT_REF.
|
||||
* builtins.h (compute_objsize): Add argument.
|
||||
* tree-ssa-strlen.c (handle_store): Handle no-warning bit.
|
||||
* tree-vrp.c (vrp_prop::check_array_ref): Return warning result.
|
||||
(vrp_prop::check_mem_ref): Same.
|
||||
(vrp_prop::search_for_addr_array): Set no-warning bit.
|
||||
(check_array_bounds): Same.
|
||||
|
||||
2019-08-28 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR driver/80545
|
||||
|
111
gcc/builtins.c
111
gcc/builtins.c
@ -72,6 +72,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "file-prefix-map.h" /* remap_macro_filename() */
|
||||
#include "gomp-constants.h"
|
||||
#include "omp-general.h"
|
||||
#include "tree-dfa.h"
|
||||
|
||||
struct target_builtins default_target_builtins;
|
||||
#if SWITCHABLE_TARGET
|
||||
@ -3561,6 +3562,54 @@ check_access (tree exp, tree, tree, tree dstwrite,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
Returns the size (which might be zero for an object with
|
||||
an uninitialized flexible array member) or null if the size
|
||||
cannot be determined. */
|
||||
|
||||
static tree
|
||||
component_size (tree ref)
|
||||
{
|
||||
gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
|
||||
|
||||
tree member = TREE_OPERAND (ref, 1);
|
||||
|
||||
/* If the member is not last or has a size greater than one, return
|
||||
it. Otherwise it's either a 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
|
||||
&& (!array_at_struct_end_p (ref)
|
||||
|| (!integer_zerop (size)
|
||||
&& !integer_onep (size))))
|
||||
return size;
|
||||
|
||||
/* 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);
|
||||
if (!base || !VAR_P (base))
|
||||
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 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));
|
||||
}
|
||||
|
||||
return DECL_EXTERNAL (base) ? NULL_TREE : integer_zero_node;
|
||||
}
|
||||
|
||||
/* Helper to compute the size of the object referenced by the DEST
|
||||
expression which must have pointer type, using Object Size type
|
||||
OSTYPE (only the least significant 2 bits are used). Return
|
||||
@ -3568,12 +3617,18 @@ check_access (tree exp, tree, tree, tree dstwrite,
|
||||
the size cannot be determined. When the referenced object involves
|
||||
a non-constant offset in some range the returned value represents
|
||||
the largest size given the smallest non-negative offset in the
|
||||
range. The function is intended for diagnostics and should not
|
||||
be used to influence code generation or optimization. */
|
||||
range. If nonnull, set *PDECL to the decl of the referenced
|
||||
subobject if it can be determined, or to null otherwise.
|
||||
The function is intended for diagnostics and should not be used
|
||||
to influence code generation or optimization. */
|
||||
|
||||
tree
|
||||
compute_objsize (tree dest, int ostype)
|
||||
compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
|
||||
{
|
||||
tree dummy = NULL_TREE;
|
||||
if (!pdecl)
|
||||
pdecl = &dummy;
|
||||
|
||||
unsigned HOST_WIDE_INT size;
|
||||
|
||||
/* Only the two least significant bits are meaningful. */
|
||||
@ -3600,7 +3655,7 @@ compute_objsize (tree dest, int ostype)
|
||||
tree off = gimple_assign_rhs2 (stmt);
|
||||
if (TREE_CODE (off) == INTEGER_CST)
|
||||
{
|
||||
if (tree size = compute_objsize (dest, ostype))
|
||||
if (tree size = compute_objsize (dest, ostype, pdecl))
|
||||
{
|
||||
wide_int wioff = wi::to_wide (off);
|
||||
wide_int wisiz = wi::to_wide (size);
|
||||
@ -3625,7 +3680,7 @@ compute_objsize (tree dest, int ostype)
|
||||
|
||||
if (rng == VR_RANGE)
|
||||
{
|
||||
if (tree size = compute_objsize (dest, ostype))
|
||||
if (tree size = compute_objsize (dest, ostype, pdecl))
|
||||
{
|
||||
wide_int wisiz = wi::to_wide (size);
|
||||
|
||||
@ -3653,12 +3708,31 @@ compute_objsize (tree dest, int ostype)
|
||||
if (!ostype)
|
||||
return NULL_TREE;
|
||||
|
||||
if (TREE_CODE (dest) == MEM_REF)
|
||||
if (TREE_CODE (dest) == ARRAY_REF
|
||||
|| TREE_CODE (dest) == MEM_REF)
|
||||
{
|
||||
tree ref = TREE_OPERAND (dest, 0);
|
||||
tree off = TREE_OPERAND (dest, 1);
|
||||
if (tree size = compute_objsize (ref, ostype))
|
||||
if (tree size = compute_objsize (ref, ostype, pdecl))
|
||||
{
|
||||
/* If the declaration of the destination object is known
|
||||
to have zero size, return zero. */
|
||||
if (integer_zerop (size))
|
||||
return integer_zero_node;
|
||||
|
||||
if (TREE_CODE (off) != INTEGER_CST
|
||||
|| TREE_CODE (size) != INTEGER_CST)
|
||||
return NULL_TREE;
|
||||
|
||||
if (TREE_CODE (dest) == ARRAY_REF)
|
||||
{
|
||||
tree eltype = TREE_TYPE (dest);
|
||||
if (tree tpsize = TYPE_SIZE_UNIT (eltype))
|
||||
off = fold_build2 (MULT_EXPR, size_type_node, off, tpsize);
|
||||
else
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (tree_int_cst_lt (off, size))
|
||||
return fold_build2 (MINUS_EXPR, size_type_node, size, off);
|
||||
return integer_zero_node;
|
||||
@ -3667,9 +3741,22 @@ compute_objsize (tree dest, int ostype)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (TREE_CODE (dest) == COMPONENT_REF)
|
||||
{
|
||||
*pdecl = TREE_OPERAND (dest, 1);
|
||||
return component_size (dest);
|
||||
}
|
||||
|
||||
if (TREE_CODE (dest) != ADDR_EXPR)
|
||||
return NULL_TREE;
|
||||
|
||||
tree ref = TREE_OPERAND (dest, 0);
|
||||
if (DECL_P (ref))
|
||||
{
|
||||
*pdecl = ref;
|
||||
return DECL_SIZE_UNIT (ref);
|
||||
}
|
||||
|
||||
tree type = TREE_TYPE (dest);
|
||||
if (TREE_CODE (type) == POINTER_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
@ -3677,14 +3764,10 @@ compute_objsize (tree dest, int ostype)
|
||||
type = TYPE_MAIN_VARIANT (type);
|
||||
|
||||
if (TREE_CODE (type) == ARRAY_TYPE
|
||||
&& !array_at_struct_end_p (TREE_OPERAND (dest, 0)))
|
||||
&& !array_at_struct_end_p (ref))
|
||||
{
|
||||
/* Return the constant size unless it's zero (that's a zero-length
|
||||
array likely at the end of a struct). */
|
||||
tree size = TYPE_SIZE_UNIT (type);
|
||||
if (size && TREE_CODE (size) == INTEGER_CST
|
||||
&& !integer_zerop (size))
|
||||
return size;
|
||||
if (tree size = TYPE_SIZE_UNIT (type))
|
||||
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
|
@ -134,7 +134,7 @@ extern tree fold_call_stmt (gcall *, bool);
|
||||
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
|
||||
extern bool is_simple_builtin (tree);
|
||||
extern bool is_inexpensive_builtin (tree);
|
||||
extern tree compute_objsize (tree, int);
|
||||
extern tree compute_objsize (tree, int, tree * = NULL);
|
||||
|
||||
extern bool readonly_data_expr (tree exp);
|
||||
extern bool init_target_chars (void);
|
||||
|
@ -1,3 +1,11 @@
|
||||
2019-08-28 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR tree-optimization/91457
|
||||
* c-c++-common/Wstringop-overflow-2.c: New test.
|
||||
* g++.dg/warn/Warray-bounds-8.C: New test.
|
||||
* g++.dg/warn/Wstringop-overflow-3.C: New test.
|
||||
* gcc.dg/Wstringop-overflow-15.c: New test.
|
||||
|
||||
2019-08-16 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR c++/90613
|
||||
|
348
gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
Normal file
348
gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
Normal file
@ -0,0 +1,348 @@
|
||||
/* PR middle-end/91458 - inconsistent warning for writing past the end
|
||||
of an array member
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
|
||||
|
||||
void sink (void*);
|
||||
|
||||
// Exercise flexible array members.
|
||||
|
||||
struct Ax
|
||||
{
|
||||
char n;
|
||||
char a[]; // { dg-message "destination object declared here" }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
struct Ax ax_;
|
||||
|
||||
void gax_ (void)
|
||||
{
|
||||
ax_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax_.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax_.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the flexible array member.
|
||||
struct Ax ax0 = { 0 };
|
||||
|
||||
void gax0 (void)
|
||||
{
|
||||
ax0.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the flexible array member to empty.
|
||||
struct Ax ax0_ = { 0, { } };
|
||||
|
||||
void gax0_ (void)
|
||||
{
|
||||
ax0_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0_.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0_.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for out-of-bounds accesses to a definition with
|
||||
// an initializer.
|
||||
struct Ax ax1 = { 1, { 0 } };
|
||||
|
||||
void gax1 (void)
|
||||
{
|
||||
ax1.a[0] = 0;
|
||||
ax1.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax1.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
struct Ax ax2 = { 2, { 1, 0 } };
|
||||
|
||||
void gax2 (void)
|
||||
{
|
||||
ax2.a[0] = 0;
|
||||
ax2.a[1] = 1;
|
||||
ax2.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void gaxp (struct Ax *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 3;
|
||||
p->a[9] = 9;
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an extern struct object whose array may be
|
||||
// initialized to any number of elements.
|
||||
extern struct Ax axx;
|
||||
|
||||
void gaxx (void)
|
||||
{
|
||||
axx.a[0] = 0;
|
||||
axx.a[3] = 3;
|
||||
axx.a[9] = 9;
|
||||
}
|
||||
|
||||
// Exercise zero-length array members.
|
||||
|
||||
struct A0
|
||||
{
|
||||
char n;
|
||||
char a[0]; // { dg-message "destination object declared here" }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
struct A0 a0_;
|
||||
|
||||
void ga0_ (void)
|
||||
{
|
||||
a0_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0_.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0_.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the flexible array member.
|
||||
struct A0 a00 = { 0 };
|
||||
|
||||
void ga00 (void)
|
||||
{
|
||||
a00.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the flexible array member to empty.
|
||||
struct A0 a00_ = { 0, { } };
|
||||
|
||||
void ga00_ (void)
|
||||
{
|
||||
a00_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00_.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00_.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// The following are rejected with
|
||||
// error: too many initializers for 'char [0]'
|
||||
// A0 a01 = { 1, { 0 } };
|
||||
// A0 a02 = { 2, { 1, 0 } };
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga0p (struct A0 *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 3;
|
||||
p->a[9] = 9;
|
||||
}
|
||||
|
||||
|
||||
// Verify warning for an extern struct object which (unlike a true
|
||||
// flexible array member) may not be initialized.
|
||||
extern struct A0 a0x;
|
||||
|
||||
void ga0x (void)
|
||||
{
|
||||
a0x.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0x.a[3] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0x.a[9] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Exercise trailing one-element array members.
|
||||
|
||||
struct A1
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "destination object declared here" }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
struct A1 a1_;
|
||||
|
||||
void ga1_ (void)
|
||||
{
|
||||
a1_.a[0] = 0;
|
||||
a1_.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1_.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1 a;
|
||||
a.a[0] = 0;
|
||||
a.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the one-element array member.
|
||||
struct A1 a1__ = { 0 };
|
||||
|
||||
void ga1__ (void)
|
||||
{
|
||||
a1__.a[0] = 0;
|
||||
a1__.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1__.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1 a = { 1 };
|
||||
a.a[0] = 0;
|
||||
a.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member to empty.
|
||||
struct A1 a1_0 = { 0, { } };
|
||||
|
||||
void ga1_0_ (void)
|
||||
{
|
||||
a1_0.a[0] = 0;
|
||||
a1_0.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1_0.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1 a = { 1, { } };
|
||||
a.a[0] = 0;
|
||||
a.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member.
|
||||
struct A1 a1_1 = { 0, { 1 } };
|
||||
|
||||
void ga1_1 (void)
|
||||
{
|
||||
a1_1.a[0] = 0;
|
||||
a1_1.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1_1.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1 a = { 0, { 1 } };
|
||||
a.a[0] = 0;
|
||||
a.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga1p (struct A1 *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 3;
|
||||
p->a[9] = 9;
|
||||
}
|
||||
|
||||
|
||||
// Verify warning for an extern struct object. Similar to the zero-length
|
||||
// array case, a one-element trailing array can be initialized to at most
|
||||
// a single element.
|
||||
extern struct A1 a1x;
|
||||
|
||||
void ga1x (void)
|
||||
{
|
||||
a1x.a[0] = 0;
|
||||
a1x.a[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1x.a[9] = 9; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Exercise interior one-element array members (verify they're not
|
||||
// treated as trailing.
|
||||
|
||||
struct A1i
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "destination object declared here" }
|
||||
char x;
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
struct A1i a1i_;
|
||||
|
||||
void ga1i_ (void)
|
||||
{
|
||||
a1i_.a[0] = 0;
|
||||
a1i_.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i_.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1i a;
|
||||
a.a[0] = 1;
|
||||
a.a[1] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the one-element array member.
|
||||
struct A1i a1i__ = { 0 };
|
||||
|
||||
void ga1i__ (void)
|
||||
{
|
||||
a1i__.a[0] = 0;
|
||||
a1i__.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i__.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1i a = { 0 };
|
||||
a.a[0] = 0;
|
||||
a.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member to empty.
|
||||
struct A1 a1i_0 = { 0, { } };
|
||||
|
||||
void ga1i_0_ (void)
|
||||
{
|
||||
a1i_0.a[0] = 0;
|
||||
a1i_0.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i_0.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1 a = { 0, { } };
|
||||
a.a[0] = 0;
|
||||
a.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member.
|
||||
struct A1 a1i_1 = { 0, { 1 } };
|
||||
|
||||
void ga1i_1 (void)
|
||||
{
|
||||
a1i_1.a[0] = 0;
|
||||
a1i_1.a[1] = 1; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i_1.a[2] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
struct A1 a = { 0, { 1 } };
|
||||
a.a[0] = 1;
|
||||
a.a[1] = 2; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a.a[2] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga1ip (struct A1i *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
p->a[9] = 9; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an extern struct object.
|
||||
extern struct A1i a1ix;
|
||||
|
||||
void ga1ix (void)
|
||||
{
|
||||
a1ix.a[0] = 0;
|
||||
a1ix.a[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1ix.a[9] = 9; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
388
gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
Normal file
388
gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
Normal file
@ -0,0 +1,388 @@
|
||||
/* PR middle-end/91458 - inconsistent warning for writing past the end
|
||||
of an array member
|
||||
See Wstringop-overflow-3.C for the same test that exercises the other
|
||||
warning.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wno-stringop-overflow" } */
|
||||
|
||||
void sink (void*);
|
||||
|
||||
// Exercise flexible array members.
|
||||
|
||||
struct Ax
|
||||
{
|
||||
char n;
|
||||
char a[]; // { dg-message "while referencing .Ax::a." "pr91463" { xfail *-*-* } }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
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 *-*-* } }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the flexible array member.
|
||||
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 *-*-* } }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the flexible array member to empty.
|
||||
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 *-*-* } }
|
||||
}
|
||||
|
||||
// Verify warning for out-of-bounds accesses to a definition with
|
||||
// an initializer.
|
||||
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 *-*-* } }
|
||||
}
|
||||
|
||||
Ax ax2 = { 2, { 1, 0 } };
|
||||
|
||||
void gax2 ()
|
||||
{
|
||||
ax2.a[0] = 0;
|
||||
ax2.a[1] = 0;
|
||||
ax2.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void gaxp (Ax *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0;
|
||||
p->a[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an extern struct object whose array may be
|
||||
// initialized to any number of elements.
|
||||
extern Ax axx;
|
||||
|
||||
void gaxx ()
|
||||
{
|
||||
axx.a[0] = 0;
|
||||
axx.a[3] = 0;
|
||||
axx.a[9] = 0;
|
||||
}
|
||||
|
||||
// Exercise zero-length array members.
|
||||
|
||||
struct A0
|
||||
{
|
||||
char n;
|
||||
char a[0]; // { dg-message "while referencing .A0::a." }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
A0 a0_;
|
||||
|
||||
void ga0_ ()
|
||||
{
|
||||
a0_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a0_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a0_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the flexible array member.
|
||||
A0 a00 = { 0 };
|
||||
|
||||
void ga00 ()
|
||||
{
|
||||
a00.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a00.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a00.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the flexible array member to empty.
|
||||
A0 a00_ = { 0, { } };
|
||||
|
||||
void ga00_ ()
|
||||
{
|
||||
a00_.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a00_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a00_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// The following are rejected with
|
||||
// error: too many initializers for 'char [0]'
|
||||
// A0 a01 = { 1, { 0 } };
|
||||
// A0 a02 = { 2, { 1, 0 } };
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga0p (A0 *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0;
|
||||
p->a[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Verify warning for an extern struct object which (unlike a true
|
||||
// flexible array member) may not be initialized.
|
||||
extern A0 a0x;
|
||||
|
||||
void ga0x ()
|
||||
{
|
||||
a0x.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a0x.a[3] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a0x.a[9] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
// Exercise trailing one-element array members.
|
||||
|
||||
struct A1
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "while referencing .A1::a." }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
A1 a1_;
|
||||
|
||||
void ga1_ ()
|
||||
{
|
||||
a1_.a[0] = 0;
|
||||
a1_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the one-element array member.
|
||||
A1 a1__ = { 0 };
|
||||
|
||||
void ga1__ ()
|
||||
{
|
||||
a1__.a[0] = 0;
|
||||
a1__.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1__.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member to empty.
|
||||
A1 a1_0 = { 0, { } };
|
||||
|
||||
void ga1_0_ ()
|
||||
{
|
||||
a1_0.a[0] = 0;
|
||||
a1_0.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1_0.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member.
|
||||
A1 a1_1 = { 0, { 1 } };
|
||||
|
||||
void ga1_1 ()
|
||||
{
|
||||
a1_1.a[0] = 0;
|
||||
a1_1.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1_1.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga1p (A1 *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0;
|
||||
p->a[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Verify warning for an extern struct object. Similar to the zero-length
|
||||
// array case, a one-element trailing array can be initialized to at most
|
||||
// a single element.
|
||||
extern A1 a1x;
|
||||
|
||||
void ga1x ()
|
||||
{
|
||||
a1x.a[0] = 0;
|
||||
a1x.a[3] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1x.a[9] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Exercise interior one-element array members (verify they're not
|
||||
// treated as trailing.
|
||||
|
||||
struct A1i
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "while referencing .A1i::a." }
|
||||
char x;
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
A1i a1i_;
|
||||
|
||||
void ga1i_ ()
|
||||
{
|
||||
a1i_.a[0] = 0;
|
||||
a1i_.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1i_.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the one-element array member.
|
||||
A1i a1i__ = { 0 };
|
||||
|
||||
void ga1i__ ()
|
||||
{
|
||||
a1i__.a[0] = 0;
|
||||
a1i__.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1i__.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member to empty.
|
||||
A1 a1i_0 = { 0, { } };
|
||||
|
||||
void ga1i_0_ ()
|
||||
{
|
||||
a1i_0.a[0] = 0;
|
||||
a1i_0.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1i_0.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member.
|
||||
A1 a1i_1 = { 0, { 1 } };
|
||||
|
||||
void ga1i_1 ()
|
||||
{
|
||||
a1i_1.a[0] = 0;
|
||||
a1i_1.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1i_1.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga1ip (A1i *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
p->a[9] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an extern struct object.
|
||||
extern A1i a1ix;
|
||||
|
||||
void ga1ix ()
|
||||
{
|
||||
a1ix.a[0] = 0;
|
||||
a1ix.a[3] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
a1ix.a[9] = 0; // { dg-warning "\\\[-Warray-bounds" }
|
||||
}
|
||||
|
||||
|
||||
// Verify non-POD classes with flexible array members.
|
||||
|
||||
struct Bx
|
||||
{
|
||||
char n;
|
||||
char a[]; // { dg-message "while referencing .Bx::a." "pr91463" { xfail *-*-* } }
|
||||
|
||||
// Verify the warning for a constant.
|
||||
Bx () { a[0] = 0; } // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
|
||||
|
||||
// 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 *-*-* } }
|
||||
};
|
||||
|
||||
void gbx (void)
|
||||
{
|
||||
struct Bx bx;
|
||||
sink (&bx);
|
||||
}
|
||||
|
||||
void gbxi (int i)
|
||||
{
|
||||
struct Bx bxi (i);
|
||||
sink (&bxi);
|
||||
}
|
||||
|
||||
struct B0
|
||||
{
|
||||
char n;
|
||||
char a[0]; // { dg-message "while referencing .B0::a." }
|
||||
|
||||
B0 () { a[0] = 0; } // { dg-warning "\\\[-Warray-bounds" }
|
||||
};
|
||||
|
||||
|
||||
void gb0 (void)
|
||||
{
|
||||
struct B0 b0;
|
||||
sink (&b0);
|
||||
}
|
||||
|
||||
|
||||
struct B1
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "while referencing .B1::a." }
|
||||
|
||||
B1 () { a[1] = 0; } // { dg-warning "\\\[-Warray-bounds" }
|
||||
};
|
||||
|
||||
void gb1 (void)
|
||||
{
|
||||
struct B1 b1;
|
||||
sink (&b1);
|
||||
}
|
||||
|
||||
|
||||
struct B123
|
||||
{
|
||||
char a[123]; // { dg-message "while referencing .B123::a." }
|
||||
|
||||
B123 () { a[123] = 0; } // { dg-warning "\\\[-Warray-bounds" }
|
||||
};
|
||||
|
||||
void gb123 (void)
|
||||
{
|
||||
struct B123 b123;
|
||||
sink (&b123);
|
||||
}
|
||||
|
||||
|
||||
struct B234
|
||||
{
|
||||
char a[234]; // { dg-message "while referencing .B234::a." }
|
||||
|
||||
B234 (int i) { a[i] = 0; } // { dg-warning "\\\[-Warray-bounds" }
|
||||
};
|
||||
|
||||
void g234 (void)
|
||||
{
|
||||
struct B234 b234 (234);
|
||||
sink (&b234);
|
||||
}
|
386
gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
Normal file
386
gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
Normal file
@ -0,0 +1,386 @@
|
||||
/* PR middle-end/91458 - inconsistent warning for writing past the end
|
||||
of an array member
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
|
||||
|
||||
void sink (void*);
|
||||
|
||||
// Exercise flexible array members.
|
||||
|
||||
struct Ax
|
||||
{
|
||||
char n;
|
||||
char a[]; // { dg-message "destination object declared here" }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
Ax ax_;
|
||||
|
||||
void gax_ ()
|
||||
{
|
||||
ax_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax_.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax_.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the flexible array member.
|
||||
Ax ax0 = { 0 };
|
||||
|
||||
void gax0 ()
|
||||
{
|
||||
ax0.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the flexible array member to empty.
|
||||
Ax ax0_ = { 0, { } };
|
||||
|
||||
void gax0_ ()
|
||||
{
|
||||
ax0_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0_.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax0_.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for out-of-bounds accesses to a definition with
|
||||
// an initializer.
|
||||
Ax ax1 = { 1, { 0 } };
|
||||
|
||||
void gax1 ()
|
||||
{
|
||||
ax1.a[0] = 0;
|
||||
ax1.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
ax1.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
Ax ax2 = { 2, { 1, 0 } };
|
||||
|
||||
void gax2 ()
|
||||
{
|
||||
ax2.a[0] = 0;
|
||||
ax2.a[1] = 0;
|
||||
ax2.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void gaxp (Ax *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0;
|
||||
p->a[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an extern struct object whose array may be
|
||||
// initialized to any number of elements.
|
||||
extern Ax axx;
|
||||
|
||||
void gaxx ()
|
||||
{
|
||||
axx.a[0] = 0;
|
||||
axx.a[3] = 0;
|
||||
axx.a[9] = 0;
|
||||
}
|
||||
|
||||
// Exercise zero-length array members.
|
||||
|
||||
struct A0
|
||||
{
|
||||
char n;
|
||||
char a[0]; // { dg-message "destination object declared here" }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
A0 a0_;
|
||||
|
||||
void ga0_ ()
|
||||
{
|
||||
a0_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0_.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0_.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the flexible array member.
|
||||
A0 a00 = { 0 };
|
||||
|
||||
void ga00 ()
|
||||
{
|
||||
a00.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the flexible array member to empty.
|
||||
A0 a00_ = { 0, { } };
|
||||
|
||||
void ga00_ ()
|
||||
{
|
||||
a00_.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00_.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a00_.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// The following are rejected with
|
||||
// error: too many initializers for 'char [0]'
|
||||
// A0 a01 = { 1, { 0 } };
|
||||
// A0 a02 = { 2, { 1, 0 } };
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga0p (A0 *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0;
|
||||
p->a[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Verify warning for an extern struct object which (unlike a true
|
||||
// flexible array member) may not be initialized.
|
||||
extern A0 a0x;
|
||||
|
||||
void ga0x ()
|
||||
{
|
||||
a0x.a[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0x.a[3] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a0x.a[9] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Exercise trailing one-element array members.
|
||||
|
||||
struct A1
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "destination object declared here" }
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
A1 a1_;
|
||||
|
||||
void ga1_ ()
|
||||
{
|
||||
a1_.a[0] = 0;
|
||||
a1_.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1_.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the one-element array member.
|
||||
A1 a1__ = { 0 };
|
||||
|
||||
void ga1__ ()
|
||||
{
|
||||
a1__.a[0] = 0;
|
||||
a1__.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1__.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member to empty.
|
||||
A1 a1_0 = { 0, { } };
|
||||
|
||||
void ga1_0_ ()
|
||||
{
|
||||
a1_0.a[0] = 0;
|
||||
a1_0.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1_0.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member.
|
||||
A1 a1_1 = { 0, { 1 } };
|
||||
|
||||
void ga1_1 ()
|
||||
{
|
||||
a1_1.a[0] = 0;
|
||||
a1_1.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1_1.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga1p (A1 *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0;
|
||||
p->a[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Verify warning for an extern struct object. Similar to the zero-length
|
||||
// array case, a one-element trailing array can be initialized to at most
|
||||
// a single element.
|
||||
extern A1 a1x;
|
||||
|
||||
void ga1x ()
|
||||
{
|
||||
a1x.a[0] = 0;
|
||||
a1x.a[3] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1x.a[9] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Exercise interior one-element array members (verify they're not
|
||||
// treated as trailing.
|
||||
|
||||
struct A1i
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "destination object declared here" }
|
||||
char x;
|
||||
};
|
||||
|
||||
// Verify warning for a definition with no initializer.
|
||||
A1i a1i_;
|
||||
|
||||
void ga1i_ ()
|
||||
{
|
||||
a1i_.a[0] = 0;
|
||||
a1i_.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i_.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that doesn't
|
||||
// initialize the one-element array member.
|
||||
A1i a1i__ = { 0 };
|
||||
|
||||
void ga1i__ ()
|
||||
{
|
||||
a1i__.a[0] = 0;
|
||||
a1i__.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i__.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member to empty.
|
||||
A1 a1i_0 = { 0, { } };
|
||||
|
||||
void ga1i_0_ ()
|
||||
{
|
||||
a1i_0.a[0] = 0;
|
||||
a1i_0.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i_0.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
// Verify warning for access to a definition with an initializer that
|
||||
// initializes the one-element array member.
|
||||
A1 a1i_1 = { 0, { 1 } };
|
||||
|
||||
void ga1i_1 ()
|
||||
{
|
||||
a1i_1.a[0] = 0;
|
||||
a1i_1.a[1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1i_1.a[2] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an unknown struct object.
|
||||
void ga1ip (A1i *p)
|
||||
{
|
||||
p->a[0] = 0;
|
||||
p->a[3] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
p->a[9] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify no warning for an extern struct object.
|
||||
extern A1i a1ix;
|
||||
|
||||
void ga1ix ()
|
||||
{
|
||||
a1ix.a[0] = 0;
|
||||
a1ix.a[3] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
a1ix.a[9] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
}
|
||||
|
||||
|
||||
// Verify non-POD classes with flexible array members.
|
||||
|
||||
struct Bx
|
||||
{
|
||||
char n;
|
||||
char a[]; // { dg-message "destination object declared here" }
|
||||
|
||||
// Verify the warning for a constant.
|
||||
Bx () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
|
||||
// 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 "\\\[-Wstringop-overflow" }
|
||||
};
|
||||
|
||||
void gbx (void)
|
||||
{
|
||||
struct Bx bx;
|
||||
sink (&bx);
|
||||
}
|
||||
|
||||
void gbxi (int i)
|
||||
{
|
||||
struct Bx bxi (i);
|
||||
sink (&bxi);
|
||||
}
|
||||
|
||||
struct B0
|
||||
{
|
||||
char n;
|
||||
char a[0]; // { dg-message "destination object declared here" }
|
||||
|
||||
B0 () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
};
|
||||
|
||||
|
||||
void gb0 (void)
|
||||
{
|
||||
struct B0 b0;
|
||||
sink (&b0);
|
||||
}
|
||||
|
||||
|
||||
struct B1
|
||||
{
|
||||
char n;
|
||||
char a[1]; // { dg-message "destination object declared here" }
|
||||
|
||||
B1 () { a[1] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
};
|
||||
|
||||
void gb1 (void)
|
||||
{
|
||||
struct B1 b1;
|
||||
sink (&b1);
|
||||
}
|
||||
|
||||
|
||||
struct B123
|
||||
{
|
||||
char a[123]; // { dg-message "destination object declared here" }
|
||||
|
||||
B123 () { a[123] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
};
|
||||
|
||||
void gb123 (void)
|
||||
{
|
||||
struct B123 b123;
|
||||
sink (&b123);
|
||||
}
|
||||
|
||||
|
||||
struct B234
|
||||
{
|
||||
char a[234]; // { dg-message "destination object declared here" }
|
||||
|
||||
B234 (int i) { a[i] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
|
||||
};
|
||||
|
||||
void g234 (void)
|
||||
{
|
||||
struct B234 b234 (234);
|
||||
sink (&b234);
|
||||
}
|
62
gcc/testsuite/gcc.dg/Wstringop-overflow-15.c
Normal file
62
gcc/testsuite/gcc.dg/Wstringop-overflow-15.c
Normal file
@ -0,0 +1,62 @@
|
||||
/* PR middle-end/91458 - inconsistent warning for writing past the end
|
||||
of an array member
|
||||
Verify that the -Wstringop-overflow detection doesn't cause an ICE
|
||||
for either kind of VLAs (member and non-member).
|
||||
Diagnosing the accesses is the subject of pr82608.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
|
||||
|
||||
void sink (void*);
|
||||
|
||||
void vla_unbounded (int n)
|
||||
{
|
||||
char a[n];
|
||||
|
||||
a[0] = 0;
|
||||
a[1] = 1;
|
||||
a[n] = n; // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
|
||||
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
void vla_bounded (int n)
|
||||
{
|
||||
if (n > 32)
|
||||
n = 32;
|
||||
|
||||
char a[n];
|
||||
|
||||
a[0] = 0;
|
||||
a[1] = 1;
|
||||
a[n] = n; // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
|
||||
a[69] = n; // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
|
||||
|
||||
sink (&a);
|
||||
}
|
||||
|
||||
|
||||
void member_vla_unbounded (int n)
|
||||
{
|
||||
struct S { char i, a[n]; } s;
|
||||
|
||||
s.a[0] = 0;
|
||||
s.a[1] = 1;
|
||||
s.a[n] = n; // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
|
||||
|
||||
sink (&s);
|
||||
}
|
||||
|
||||
void member_vla_bounded (int n)
|
||||
{
|
||||
if (n > 32)
|
||||
n = 32;
|
||||
|
||||
struct S { char i, a[n]; } s;
|
||||
|
||||
s.a[0] = 0;
|
||||
s.a[1] = 1;
|
||||
s.a[n] = n; // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
|
||||
s.a[69] = n; // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
|
||||
|
||||
sink (&s);
|
||||
}
|
@ -4026,16 +4026,30 @@ handle_store (gimple_stmt_iterator *gsi)
|
||||
rhs_minlen = lenrange[0];
|
||||
storing_nonzero_p = lenrange[1] > 0;
|
||||
|
||||
if (tree dstsize = compute_objsize (lhs, 1))
|
||||
if (compare_tree_int (dstsize, lenrange[2]) < 0)
|
||||
{
|
||||
location_t loc = gimple_nonartificial_location (stmt);
|
||||
warning_n (loc, OPT_Wstringop_overflow_,
|
||||
lenrange[2],
|
||||
"%Gwriting %u byte into a region of size %E",
|
||||
"%Gwriting %u bytes into a region of size %E",
|
||||
stmt, lenrange[2], dstsize);
|
||||
}
|
||||
/* Avoid issuing multiple warnings for the same LHS or statement.
|
||||
For example, -Warray-bounds may have already been issued for
|
||||
an out-of-bounds subscript. */
|
||||
if (!TREE_NO_WARNING (lhs) && !gimple_no_warning_p (stmt))
|
||||
{
|
||||
/* Set to the declaration referenced by LHS (if known). */
|
||||
tree decl = NULL_TREE;
|
||||
if (tree dstsize = compute_objsize (lhs, 1, &decl))
|
||||
if (compare_tree_int (dstsize, lenrange[2]) < 0)
|
||||
{
|
||||
location_t loc = gimple_nonartificial_location (stmt);
|
||||
if (warning_n (loc, OPT_Wstringop_overflow_,
|
||||
lenrange[2],
|
||||
"%Gwriting %u byte into a region of size %E",
|
||||
"%Gwriting %u bytes into a region of size %E",
|
||||
stmt, lenrange[2], dstsize))
|
||||
{
|
||||
if (decl)
|
||||
inform (DECL_SOURCE_LOCATION (decl),
|
||||
"destination object declared here");
|
||||
gimple_set_no_warning (stmt, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4388,8 +4388,8 @@ class vrp_prop : public ssa_propagation_engine
|
||||
void vrp_initialize (void);
|
||||
void vrp_finalize (bool);
|
||||
void check_all_array_refs (void);
|
||||
void check_array_ref (location_t, tree, bool);
|
||||
void check_mem_ref (location_t, tree, bool);
|
||||
bool check_array_ref (location_t, tree, bool);
|
||||
bool check_mem_ref (location_t, tree, bool);
|
||||
void search_for_addr_array (tree, location_t);
|
||||
|
||||
class vr_values vr_values;
|
||||
@ -4415,9 +4415,10 @@ class vrp_prop : public ssa_propagation_engine
|
||||
array subscript is a constant, check if it is outside valid
|
||||
range. If the array subscript is a RANGE, warn if it is
|
||||
non-overlapping with valid range.
|
||||
IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
|
||||
IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.
|
||||
Returns true if a warning has been issued. */
|
||||
|
||||
void
|
||||
bool
|
||||
vrp_prop::check_array_ref (location_t location, tree ref,
|
||||
bool ignore_off_by_one)
|
||||
{
|
||||
@ -4426,7 +4427,7 @@ vrp_prop::check_array_ref (location_t location, tree ref,
|
||||
tree low_bound, up_bound, up_bound_p1;
|
||||
|
||||
if (TREE_NO_WARNING (ref))
|
||||
return;
|
||||
return false;
|
||||
|
||||
low_sub = up_sub = TREE_OPERAND (ref, 1);
|
||||
up_bound = array_ref_up_bound (ref);
|
||||
@ -4541,12 +4542,16 @@ vrp_prop::check_array_ref (location_t location, tree ref,
|
||||
if (warned)
|
||||
{
|
||||
ref = TREE_OPERAND (ref, 0);
|
||||
if (TREE_CODE (ref) == COMPONENT_REF)
|
||||
ref = TREE_OPERAND (ref, 1);
|
||||
|
||||
if (DECL_P (ref))
|
||||
inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
|
||||
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
}
|
||||
|
||||
return warned;
|
||||
}
|
||||
|
||||
/* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
|
||||
@ -4556,14 +4561,15 @@ vrp_prop::check_array_ref (location_t location, tree ref,
|
||||
with valid range.
|
||||
IGNORE_OFF_BY_ONE is true if the MEM_REF is inside an ADDR_EXPR
|
||||
(used to allow one-past-the-end indices for code that takes
|
||||
the address of the just-past-the-end element of an array). */
|
||||
the address of the just-past-the-end element of an array).
|
||||
Returns true if a warning has been issued. */
|
||||
|
||||
void
|
||||
bool
|
||||
vrp_prop::check_mem_ref (location_t location, tree ref,
|
||||
bool ignore_off_by_one)
|
||||
{
|
||||
if (TREE_NO_WARNING (ref))
|
||||
return;
|
||||
return false;
|
||||
|
||||
tree arg = TREE_OPERAND (ref, 0);
|
||||
/* The constant and variable offset of the reference. */
|
||||
@ -4615,7 +4621,7 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
|
||||
continue;
|
||||
}
|
||||
else
|
||||
return;
|
||||
return false;
|
||||
|
||||
/* VAROFF should always be a SSA_NAME here (and not even
|
||||
INTEGER_CST) but there's no point in taking chances. */
|
||||
@ -4677,10 +4683,10 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
|
||||
arg = TREE_OPERAND (arg, 0);
|
||||
if (TREE_CODE (arg) != STRING_CST
|
||||
&& TREE_CODE (arg) != VAR_DECL)
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return;
|
||||
return false;
|
||||
|
||||
/* 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
|
||||
@ -4695,7 +4701,7 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
|
||||
|| !COMPLETE_TYPE_P (reftype)
|
||||
|| TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST
|
||||
|| RECORD_OR_UNION_TYPE_P (reftype))
|
||||
return;
|
||||
return false;
|
||||
|
||||
offset_int eltsize;
|
||||
if (TREE_CODE (reftype) == ARRAY_TYPE)
|
||||
@ -4797,11 +4803,11 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
|
||||
|
||||
if (warned)
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
return;
|
||||
return warned;
|
||||
}
|
||||
|
||||
if (warn_array_bounds < 2)
|
||||
return;
|
||||
return false;
|
||||
|
||||
/* At level 2 check also intermediate offsets. */
|
||||
int i = 0;
|
||||
@ -4812,8 +4818,13 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
|
||||
if (warning_at (location, OPT_Warray_bounds,
|
||||
"intermediate array offset %wi is outside array bounds "
|
||||
"of %qT", tmpidx, reftype))
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
{
|
||||
TREE_NO_WARNING (ref) = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Searches if the expr T, located at LOCATION computes
|
||||
@ -4825,10 +4836,14 @@ vrp_prop::search_for_addr_array (tree t, location_t location)
|
||||
/* Check each ARRAY_REF and MEM_REF in the reference chain. */
|
||||
do
|
||||
{
|
||||
bool warned = false;
|
||||
if (TREE_CODE (t) == ARRAY_REF)
|
||||
check_array_ref (location, t, true /*ignore_off_by_one*/);
|
||||
warned = check_array_ref (location, t, true /*ignore_off_by_one*/);
|
||||
else if (TREE_CODE (t) == MEM_REF)
|
||||
check_mem_ref (location, t, true /*ignore_off_by_one*/);
|
||||
warned = check_mem_ref (location, t, true /*ignore_off_by_one*/);
|
||||
|
||||
if (warned)
|
||||
TREE_NO_WARNING (t) = true;
|
||||
|
||||
t = TREE_OPERAND (t, 0);
|
||||
}
|
||||
@ -4920,16 +4935,20 @@ check_array_bounds (tree *tp, int *walk_subtree, void *data)
|
||||
|
||||
*walk_subtree = TRUE;
|
||||
|
||||
bool warned = false;
|
||||
vrp_prop *vrp_prop = (class vrp_prop *)wi->info;
|
||||
if (TREE_CODE (t) == ARRAY_REF)
|
||||
vrp_prop->check_array_ref (location, t, false /*ignore_off_by_one*/);
|
||||
warned = vrp_prop->check_array_ref (location, t, false/*ignore_off_by_one*/);
|
||||
else if (TREE_CODE (t) == MEM_REF)
|
||||
vrp_prop->check_mem_ref (location, t, false /*ignore_off_by_one*/);
|
||||
warned = vrp_prop->check_mem_ref (location, t, false /*ignore_off_by_one*/);
|
||||
else if (TREE_CODE (t) == ADDR_EXPR)
|
||||
{
|
||||
vrp_prop->search_for_addr_array (t, location);
|
||||
*walk_subtree = FALSE;
|
||||
}
|
||||
/* Propagate the no-warning bit to the outer expression. */
|
||||
if (warned)
|
||||
TREE_NO_WARNING (t) = true;
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user