diff options
author | Martin Sebor <msebor@redhat.com> | 2019-11-01 21:09:20 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2019-11-01 15:09:20 -0600 |
commit | 49fb45c81f4ac068d9fb859968d8f223bc438251 (patch) | |
tree | 56f91e1dcae71841b97747d2342eacce4c35592e /gcc/tree.c | |
parent | 8dc56a2244cfe35cbdb53e0f586c16f90a2677e4 (diff) | |
download | gcc-49fb45c81f4ac068d9fb859968d8f223bc438251.zip gcc-49fb45c81f4ac068d9fb859968d8f223bc438251.tar.gz gcc-49fb45c81f4ac068d9fb859968d8f223bc438251.tar.bz2 |
PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local buffer
PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local buffer
PR middle-end/91647 - new FAILs for Warray-bounds-8 and Wstringop-overflow-3.C
PR middle-end/91463 - missing -Warray-bounds accessing past the end of a statically initialized flexible array member
PR middle-end/92312 - bogus -Wstringop-overflow storing into a trailing array backed by larger buffer
gcc/ChangeLog:
PR middle-end/91679
PR middle-end/91647
PR middle-end/91463
PR middle-end/92312
* c-family/c-pretty-print.c (direct_abstract_declarator): Print
bound in zero-length arrays.
* gcc/c-family/c.opt (-Wzero-length-bounds): New option.
* gcc/doc/invoke.texi (-Wzero-length-bounds): Document.
* gimple-match-head.c (try_conditional_simplification): Use memcpy
instead of a hand-rolled loop to avoid PR 92323.
* tree-vrp.c (vrp_prop::check_array_ref): Handle trailing arrays
with initializers.
(vrp_prop::check_mem_ref): Handle declared struct objects.
* tree.c (last_field): New function.
(array_at_struct_end_p): Handle MEM_REF.
(get_initializer_for): New helper.
(component_ref_size): Add argument. Rename locals. Call
get_initializer_for instead of fold_ctor_reference. Correct handling
of flexible array members.
* wide-int.h (generic_wide_int <storage>::sign_mask): Assert invariant.
gcc/testsuite/ChangeLog:
PR middle-end/91679
PR middle-end/91647
PR middle-end/91463
PR middle-end/92312
* c-c++-common/Warray-bounds-2.c: Disable VRP. Adjust expected messages.
* g++.dg/warn/Warray-bounds-8.C: Remove xfails.
* gcc.dg/Warray-bounds-48.c: New test.
* gcc.dg/Warray-bounds-49.c: New test.
* gcc.dg/Wstringop-overflow-16.c: Adjust text of expected messages.
* gcc.dg/Wstringop-overflow-21.c: New test.
* gcc.dg/Wzero-length-array-bounds.c: New test.
* gcc.dg/pr36902.c: Remove xfail.
* gcc.dg/strlenopt-57.c: Add an expected warning.
From-SVN: r277728
Diffstat (limited to 'gcc/tree.c')
-rw-r--r-- | gcc/tree.c | 225 |
1 files changed, 194 insertions, 31 deletions
@@ -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); } |