From 6889a3acfeed47265886676c6d43b04ef799fb82 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Thu, 19 Sep 2019 22:15:34 +0000 Subject: [PATCH] PR middle-end/91631 - buffer overflow into an array member of a declared object not detected gcc/ChangeLog: PR middle-end/91631 * builtins.c (component_size): Correct trailing array computation, rename to component_ref_size and move... (compute_objsize): Adjust. * gimple-ssa-warn-restrict.c (builtin_memref::refsize): New member. (builtin_access::strict): Do not consider mememmove. (builtin_access::write_off): New function. (builtin_memref::builtin_memref): Initialize refsize. (builtin_memref::set_base_and_offset): Adjust refoff and compute refsize. (builtin_memref::offset_out_of_bounds): Use ooboff input values. Handle refsize. (builtin_access::builtin_access): Intialize dstoff to destination refeence offset here instead of in maybe_diag_overlap. Adjust referencess even to unrelated objects. Adjust sizrange of bounded string functions to reflect bound. For strcat, adjust destination sizrange by that of source. (builtin_access::strcat_overlap): Adjust offsets and sizes to reflect the increase in destination sizrange above. (builtin_access::overlap): Do not set dstoff here but instead in builtin_access::builtin_access. (check_bounds_or_overlap): Use builtin_access::write_off. (maybe_diag_access_bounds): Add argument. Add informational notes. (dump_builtin_memref, dump_builtin_access): New functions. * tree.c (component_ref_size): ...to here. * tree.h (component_ref_size): Declare. * tree-ssa-strlen (handle_builtin_strcat): Include the terminating nul in the size of the source string. gcc/testsuite/ChangeLog: PR middle-end/91631 * /c-c++-common/Warray-bounds-3.c: Correct expected offsets. * /c-c++-common/Warray-bounds-4.c: Same. * gcc.dg/Warray-bounds-39.c: Remove xfails. * gcc.dg/Warray-bounds-45.c: New test. * gcc.dg/Warray-bounds-46.c: New test. From-SVN: r275981 --- gcc/ChangeLog | 31 ++ gcc/builtins.c | 50 +-- gcc/gimple-ssa-warn-restrict.c | 348 ++++++++++++++----- gcc/testsuite/ChangeLog | 9 + gcc/testsuite/c-c++-common/Warray-bounds-3.c | 26 +- gcc/testsuite/c-c++-common/Warray-bounds-4.c | 6 +- gcc/testsuite/gcc.dg/Warray-bounds-39.c | 6 +- gcc/testsuite/gcc.dg/Warray-bounds-45.c | 330 ++++++++++++++++++ gcc/testsuite/gcc.dg/Warray-bounds-46.c | 249 +++++++++++++ gcc/tree-ssa-strlen.c | 7 +- gcc/tree.c | 70 ++++ gcc/tree.h | 7 + 12 files changed, 983 insertions(+), 156 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Warray-bounds-45.c create mode 100644 gcc/testsuite/gcc.dg/Warray-bounds-46.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7f16c16e6a1..9dfb64643c1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,34 @@ +2019-09-19 Martin Sebor + + PR middle-end/91631 + * builtins.c (component_size): Correct trailing array computation, + rename to component_ref_size and move... + (compute_objsize): Adjust. + * gimple-ssa-warn-restrict.c (builtin_memref::refsize): New member. + (builtin_access::strict): Do not consider memmove. + (builtin_access::write_off): New function. + (builtin_memref::builtin_memref): Initialize refsize. + (builtin_memref::set_base_and_offset): Adjust refoff and compute + refsize. + (builtin_memref::offset_out_of_bounds): Use ooboff input values. + Handle refsize. + (builtin_access::builtin_access): Initialize dstoff to destination + refeence offset here instead of in maybe_diag_overlap. Adjust + referencess even to unrelated objects. Adjust sizrange of bounded + string functions to reflect bound. For strcat, adjust destination + sizrange by that of source. + (builtin_access::strcat_overlap): Adjust offsets and sizes + to reflect the increase in destination sizrange above. + (builtin_access::overlap): Do not set dstoff here but instead + in builtin_access::builtin_access. + (check_bounds_or_overlap): Use builtin_access::write_off. + (maybe_diag_access_bounds): Add argument. Add informational notes. + (dump_builtin_memref, dump_builtin_access): New functions. + * tree.c (component_ref_size): ...to here. + * tree.h (component_ref_size): Declare. + * tree-ssa-strlen (handle_builtin_strcat): Include the terminating + nul in the size of the source string. + 2019-09-19 Lewis Hyatt PR c/67224 diff --git a/gcc/builtins.c b/gcc/builtins.c index f8063c138a3..1fd4b88bcac 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3562,54 +3562,6 @@ 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 @@ -3744,7 +3696,7 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) if (TREE_CODE (dest) == COMPONENT_REF) { *pdecl = TREE_OPERAND (dest, 1); - return component_size (dest); + return component_ref_size (dest); } if (TREE_CODE (dest) != ADDR_EXPR) diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c index cbfc47894dc..4f6e535ff27 100644 --- a/gcc/gimple-ssa-warn-restrict.c +++ b/gcc/gimple-ssa-warn-restrict.c @@ -137,6 +137,8 @@ public: /* The size of the BASE object, PTRDIFF_MAX if indeterminate, and negative until (possibly lazily) initialized. */ offset_int basesize; + /* Same for the subobject. */ + offset_int refsize; /* The non-negative offset of the referenced subobject. Used to avoid warnings for (apparently) possibly but not definitively overlapping @@ -160,7 +162,7 @@ public: builtin_memref (tree, tree); - tree offset_out_of_bounds (int, offset_int[2]) const; + tree offset_out_of_bounds (int, offset_int[3]) const; private: @@ -192,7 +194,8 @@ class builtin_access and false for raw memory functions. */ bool strict () const { - return detect_overlap != &builtin_access::generic_overlap; + return (detect_overlap != &builtin_access::generic_overlap + && detect_overlap != &builtin_access::no_overlap); } builtin_access (gimple *, builtin_memref &, builtin_memref &); @@ -200,6 +203,10 @@ class builtin_access /* Entry point to determine overlap. */ bool overlap (); + offset_int write_off (tree) const; + + void dump (FILE *) const; + private: /* Implementation functions used to determine overlap. */ bool generic_overlap (); @@ -234,6 +241,7 @@ builtin_memref::builtin_memref (tree expr, tree size) ref (), base (), basesize (-1), + refsize (-1), refoff (HOST_WIDE_INT_MIN), offrange (), sizrange (), @@ -298,6 +306,19 @@ builtin_memref::builtin_memref (tree expr, tree size) } } +/* Based on the initial length of the destination STARTLEN, returns + the offset of the first write access from the beginning of + the destination. Nonzero only for strcat-type of calls. */ + +offset_int builtin_access::write_off (tree startlen) const +{ + if (detect_overlap != &builtin_access::strcat_overlap + || !startlen || TREE_CODE (startlen) != INTEGER_CST) + return 0; + + return wi::to_offset (startlen); +} + /* Ctor helper to set or extend OFFRANGE based on the OFFSET argument. Pointer offsets are represented as unsigned sizetype but must be treated as signed. */ @@ -483,33 +504,69 @@ builtin_memref::set_base_and_offset (tree expr) if (TREE_CODE (base) == MEM_REF) { - tree memrefoff = TREE_OPERAND (base, 1); + tree memrefoff = fold_convert (ptrdiff_type_node, TREE_OPERAND (base, 1)); extend_offset_range (memrefoff); base = TREE_OPERAND (base, 0); + + if (refoff != HOST_WIDE_INT_MIN + && TREE_CODE (expr) == COMPONENT_REF) + { + /* Bump up the offset of the referenced subobject to reflect + the offset to the enclosing object. For example, so that + in + struct S { char a, b[3]; } s[2]; + strcpy (s[1].b, "1234"); + REFOFF is set to s[1].b - (char*)s. */ + tree basetype = TREE_TYPE (TREE_TYPE (base)); + if (tree basesize = TYPE_SIZE_UNIT (basetype)) + if (TREE_CODE (basesize) == INTEGER_CST) + { + offset_int size = wi::to_offset (basesize); + offset_int off = tree_to_shwi (memrefoff); + refoff += size * (off / size); + } + } + + if (!integer_zerop (memrefoff)) + /* A non-zero offset into an array of struct with flexible array + members implies that the array is empty because there is no + way to initialize such a member when it belongs to an array. + This must be some sort of a bug. */ + refsize = 0; } + if (TREE_CODE (ref) == COMPONENT_REF) + if (tree size = component_ref_size (ref)) + if (TREE_CODE (size) == INTEGER_CST) + refsize = wi::to_offset (size); + if (TREE_CODE (base) == SSA_NAME) set_base_and_offset (base); } /* Return error_mark_node if the signed offset exceeds the bounds - of the address space (PTRDIFF_MAX). Otherwise, return either - BASE or REF when the offset exceeds the bounds of the BASE or - REF object, and set OOBOFF to the past-the-end offset formed - by the reference, including its size. When STRICT is non-zero - use REF size, when available, otherwise use BASE size. When - STRICT is greater than 1, use the size of the last array member - as the bound, otherwise treat such a member as a flexible array - member. Return NULL when the offset is in bounds. */ + of the address space (PTRDIFF_MAX). Otherwise, return either BASE + or REF when the offset exceeds the bounds of the BASE or REF object, + and set OOBOFF to the past-the-end offset formed by the reference, + including its size. OOBOFF is initially setto the range of offsets, + and OOBOFF[2] to the offset of the first write access (nonzero for + the strcat family). When STRICT is nonzero use REF size, when + available, otherwise use BASE size. When STRICT is greater than 1, + use the size of the last array member as the bound, otherwise treat + such a member as a flexible array member. Return NULL when the offset + is in bounds. */ tree -builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const +builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[3]) const { if (!ptr) return NULL_TREE; + /* The offset of the first write access or zero. */ + offset_int wroff = ooboff[2]; + /* A temporary, possibly adjusted, copy of the offset range. */ - offset_int offrng[2] = { offrange[0], offrange[1] }; + offset_int offrng[2] = { ooboff[0], ooboff[1] }; if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE) { @@ -527,9 +584,19 @@ builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const bool hib = wi::les_p (offrng[0], offrng[1]); bool lob = !hib; + /* Set to the size remaining in the object object after subtracting + REFOFF. It may become negative as a result of negative indices + into the enclosing object, such as in: + extern struct S { char a[4], b[3], c[1]; } *p; + strcpy (p[-3].b, "123"); */ + offset_int size = basesize; + tree obj = base; + + const bool decl_p = DECL_P (obj); + if (basesize < 0) { - endoff = offrng[lob] + sizrange[0]; + endoff = offrng[lob] + (sizrange[0] - wroff); /* For a reference through a pointer to an object of unknown size all initial offsets are considered valid, positive as well as @@ -539,41 +606,45 @@ builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const if (endoff > maxobjsize) return error_mark_node; - return NULL_TREE; + /* When the referenced subobject is known, the end offset must be + within its bounds. Otherwise there is nothing to do. */ + if (strict + && !decl_p + && ref + && refsize >= 0 + && TREE_CODE (ref) == COMPONENT_REF) + { + /* If REFOFF is negative, SIZE will become negative here. */ + size = refoff + refsize; + obj = ref; + } + else + return NULL_TREE; } /* A reference to an object of known size must be within the bounds - of the base object. */ - if (offrng[hib] < 0 || offrng[lob] > basesize) - return base; + of either the base object or the subobject (see above for when + a subobject can be used). */ + if ((decl_p && offrng[hib] < 0) || offrng[lob] > size) + return obj; /* The extent of the reference must also be within the bounds of - the base object (if known) or the maximum object size otherwise. */ - endoff = wi::smax (offrng[lob], 0) + sizrange[0]; + the base object (if known) or the subobject or the maximum object + size otherwise. */ + endoff = offrng[lob] + sizrange[0]; if (endoff > maxobjsize) return error_mark_node; - offset_int size = basesize; - tree obj = base; - if (strict - && DECL_P (obj) + && decl_p && ref - && refoff >= 0 - && TREE_CODE (ref) == COMPONENT_REF - && (strict > 1 - || !array_at_struct_end_p (ref))) + && refsize >= 0 + && TREE_CODE (ref) == COMPONENT_REF) { - /* If the reference is to a member subobject, the offset must - be within the bounds of the subobject. */ - tree field = TREE_OPERAND (ref, 1); - tree type = TREE_TYPE (field); - if (tree sz = TYPE_SIZE_UNIT (type)) - if (TREE_CODE (sz) == INTEGER_CST) - { - size = refoff + wi::to_offset (sz); - obj = ref; - } + /* If the reference is to a member subobject of a declared object, + the offset must be within the bounds of the subobject. */ + size = refoff + refsize; + obj = ref; } if (endoff <= size) @@ -581,12 +652,12 @@ builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const /* Set the out-of-bounds offset range to be one greater than that delimited by the reference including its size. */ - ooboff[lob] = size + 1; + ooboff[lob] = size; if (endoff > ooboff[lob]) - ooboff[hib] = endoff; + ooboff[hib] = endoff - 1; else - ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1]; + ooboff[hib] = offrng[lob] + sizrange[1]; return obj; } @@ -599,8 +670,10 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst, : dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (), dstoff (), srcoff (), dstsiz (), srcsiz () { + dstoff[0] = dst.offrange[0]; + dstoff[1] = dst.offrange[1]; + /* Zero out since the offset_int ctors invoked above are no-op. */ - dstoff[0] = dstoff[1] = 0; srcoff[0] = srcoff[1] = 0; dstsiz[0] = dstsiz[1] = 0; srcsiz[0] = srcsiz[1] = 0; @@ -716,15 +789,9 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst, src.basesize = maxobjsize; } - /* If there is no dependency between the references or the base - objects of the two references aren't the same there's nothing - else to do. */ - if (depends_p && dstref->base != srcref->base) - return; - - /* ...otherwise, make adjustments for references to the same object - by string built-in functions to reflect the constraints imposed - by the function. */ + /* Make adjustments for references to the same object by string + built-in functions to reflect the constraints imposed by + the function. */ /* For bounded string functions determine the range of the bound on the access. For others, the range stays unbounded. */ @@ -755,6 +822,7 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst, } } + bool dstsize_set = false; /* The size range of one reference involving the same base object can be determined from the size range of the other reference. This makes it possible to compute accurate offsets for warnings @@ -766,6 +834,7 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst, the source. */ dstref->sizrange[0] = srcref->sizrange[0]; dstref->sizrange[1] = srcref->sizrange[1]; + dstsize_set = true; } else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize) { @@ -778,11 +847,15 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst, { if (dstref->strbounded_p) { - /* Read access by strncpy is bounded. */ - if (bounds[0] < srcref->sizrange[0]) - srcref->sizrange[0] = bounds[0]; - if (bounds[1] < srcref->sizrange[1]) - srcref->sizrange[1] = bounds[1]; + /* Read access by strncpy is constrained by the third + argument but except for a zero bound is at least one. */ + offset_int size = wi::umax (srcref->basesize, 1); + offset_int bound = wi::umin (size, bounds[0]); + if (bound < srcref->sizrange[0]) + srcref->sizrange[0] = bound; + bound = wi::umin (srcref->basesize, bounds[1]); + if (bound < srcref->sizrange[1]) + srcref->sizrange[1] = bound; } /* For string functions, adjust the size range of the source @@ -834,6 +907,11 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst, } } } + else if (!dstsize_set && detect_overlap == &builtin_access::strcat_overlap) + { + dstref->sizrange[0] += srcref->sizrange[0] - 1; + dstref->sizrange[1] += srcref->sizrange[1] - 1; + } if (dstref->strbounded_p) { @@ -1108,10 +1186,11 @@ builtin_access::strcat_overlap () /* Adjust for strcat-like accesses. */ /* As a special case for strcat, set the DSTREF offsets to the length - of the source string since the function starts writing at the first - nul, and set the size to 1 for the length of the nul. */ - acs.dstoff[0] += acs.dstsiz[0]; - acs.dstoff[1] += acs.dstsiz[1]; + of the destination string since the function starts writing over + its terminating nul, and set the destination size to 1 for the length + of the nul. */ + acs.dstoff[0] += dstsiz[0] - srcref->sizrange[0]; + acs.dstoff[1] += dstsiz[1] - srcref->sizrange[1]; bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0; @@ -1189,7 +1268,8 @@ builtin_access::strcat_overlap () acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0; acs.ovlsiz[1] = 1; - offset_int endoff = dstref->offrange[0] + dstref->sizrange[0]; + offset_int endoff + = dstref->offrange[0] + (dstref->sizrange[0] - srcref->sizrange[0]); if (endoff <= srcref->offrange[0]) acs.ovloff[0] = wi::smin (maxobjsize, srcref->offrange[0]).to_shwi (); else @@ -1261,10 +1341,6 @@ builtin_access::overlap () if (!dstref->base || !srcref->base) return false; - /* Set the access offsets. */ - acs.dstoff[0] = dstref->offrange[0]; - acs.dstoff[1] = dstref->offrange[1]; - /* If the base object is an array adjust the bounds of the offset to be non-negative and within the bounds of the array if possible. */ if (dstref->base @@ -1626,7 +1702,8 @@ maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs) static bool maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict, - const builtin_memref &ref, bool do_warn) + const builtin_memref &ref, offset_int wroff, + bool do_warn) { const offset_int maxobjsize = ref.maxobjsize; @@ -1665,8 +1742,12 @@ maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict, } /* Check for out-bounds pointers regardless of warning options since - the result is used to make codegen decisions. */ - offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] }; + the result is used to make codegen decisions. An excessive WROFF + can only come up as a result of an invalid strncat bound and is + diagnosed separately using a more meaningful warning. */ + if (maxobjsize < wroff) + wroff = 0; + offset_int ooboff[] = { ref.offrange[0], ref.offrange[1], wroff }; tree oobref = ref.offset_out_of_bounds (strict, ooboff); if (!oobref) return false; @@ -1787,27 +1868,45 @@ maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict, } else if (TREE_CODE (ref.ref) == MEM_REF) { - tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0)); + tree refop = TREE_OPERAND (ref.ref, 0); + tree type = TREE_TYPE (refop); if (POINTER_TYPE_P (type)) type = TREE_TYPE (type); type = TYPE_MAIN_VARIANT (type); - warned = warning_at (loc, OPT_Warray_bounds, - "%G%qD offset %s from the object at %qE is out " - "of the bounds of %qT", - call, func, rangestr[0], ref.base, type); + if (warning_at (loc, OPT_Warray_bounds, + "%G%qD offset %s from the object at %qE is out " + "of the bounds of %qT", + call, func, rangestr[0], ref.base, type)) + { + if (TREE_CODE (ref.ref) == COMPONENT_REF) + refop = TREE_OPERAND (ref.ref, 1); + if (DECL_P (refop)) + inform (DECL_SOURCE_LOCATION (refop), + "subobject %qD declared here", refop); + warned = true; + } } else { + tree refop = TREE_OPERAND (ref.ref, 0); tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref)); - warned = warning_at (loc, OPT_Warray_bounds, - "%G%qD offset %s from the object at %qE is out " - "of the bounds of referenced subobject %qD with " - "type %qT at offset %wu", - call, func, rangestr[0], ref.base, - TREE_OPERAND (ref.ref, 1), type, - ref.refoff.to_uhwi ()); + if (warning_at (loc, OPT_Warray_bounds, + "%G%qD offset %s from the object at %qE is out " + "of the bounds of referenced subobject %qD with " + "type %qT at offset %wi", + call, func, rangestr[0], ref.base, + TREE_OPERAND (ref.ref, 1), type, + ref.refoff.to_shwi ())) + { + if (TREE_CODE (ref.ref) == COMPONENT_REF) + refop = TREE_OPERAND (ref.ref, 1); + if (DECL_P (refop)) + inform (DECL_SOURCE_LOCATION (refop), + "subobject %qD declared here", refop); + warned = true; + } } return warned; @@ -1936,17 +2035,23 @@ check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize, builtin_memref dstref (dst, dstsize); builtin_memref srcref (src, srcsize); + /* Create a descriptor of the access. This may adjust both DSTREF + and SRCREF based on one another and the kind of the access. */ builtin_access acs (call, dstref, srcref); /* Set STRICT to the value of the -Warray-bounds=N argument for string functions or when N > 1. */ int strict = (acs.strict () || warn_array_bounds > 1 ? warn_array_bounds : 0); - /* Validate offsets first to make sure they are within the bounds - of the destination object if its size is known, or PTRDIFF_MAX - otherwise. */ - if (maybe_diag_access_bounds (loc, call, func, strict, dstref, do_warn) - || maybe_diag_access_bounds (loc, call, func, strict, srcref, do_warn)) + /* The starting offset of the destination write access. Nonzero only + for the strcat family of functions. */ + offset_int wroff = acs.write_off (dstsize); + + /* Validate offsets to each reference before the access first to make + sure they are within the bounds of the destination object if its + size is known, or PTRDIFF_MAX otherwise. */ + if (maybe_diag_access_bounds (loc, call, func, strict, dstref, wroff, do_warn) + || maybe_diag_access_bounds (loc, call, func, strict, srcref, 0, do_warn)) { if (do_warn) gimple_set_no_warning (call, true); @@ -2003,3 +2108,76 @@ make_pass_warn_restrict (gcc::context *ctxt) { return new pass_wrestrict (ctxt); } + +DEBUG_FUNCTION void +dump_builtin_memref (FILE *fp, const builtin_memref &ref) +{ + fprintf (fp, "\n ptr = "); + print_generic_expr (fp, ref.ptr, TDF_LINENO); + fprintf (fp, "\n ref = "); + if (ref.ref) + print_generic_expr (fp, ref.ref, TDF_LINENO); + else + fputs ("null", fp); + fprintf (fp, "\n base = "); + print_generic_expr (fp, ref.base, TDF_LINENO); + fprintf (fp, + "\n basesize = %lli" + "\n refsize = %lli" + "\n refoff = %lli" + "\n offrange = [%lli, %lli]" + "\n sizrange = [%lli, %lli]" + "\n strbounded_p = %s\n", + (long long)ref.basesize.to_shwi (), + (long long)ref.refsize.to_shwi (), + (long long)ref.refoff.to_shwi (), + (long long)ref.offrange[0].to_shwi (), + (long long)ref.offrange[1].to_shwi (), + (long long)ref.sizrange[0].to_shwi (), + (long long)ref.sizrange[1].to_shwi (), + ref.strbounded_p ? "true" : "false"); +} + +void +builtin_access::dump (FILE *fp) const +{ + fprintf (fp, " dstref:"); + dump_builtin_memref (fp, *dstref); + fprintf (fp, "\n srcref:"); + dump_builtin_memref (fp, *srcref); + + fprintf (fp, + " sizrange = [%lli, %lli]\n" + " ovloff = [%lli, %lli]\n" + " ovlsiz = [%lli, %lli]\n" + " dstoff = [%lli, %lli]\n" + " dstsiz = [%lli, %lli]\n" + " srcoff = [%lli, %lli]\n" + " srcsiz = [%lli, %lli]\n", + (long long)sizrange[0], (long long)sizrange[1], + (long long)ovloff[0], (long long)ovloff[1], + (long long)ovlsiz[0], (long long)ovlsiz[1], + (long long)dstoff[0].to_shwi (), (long long)dstoff[1].to_shwi (), + (long long)dstsiz[0].to_shwi (), (long long)dstsiz[1].to_shwi (), + (long long)srcoff[0].to_shwi (), (long long)srcoff[1].to_shwi (), + (long long)srcsiz[0].to_shwi (), (long long)srcsiz[1].to_shwi ()); +} + +DEBUG_FUNCTION void +dump_builtin_access (FILE *fp, gimple *stmt, const builtin_access &acs) +{ + if (stmt) + { + fprintf (fp, "\nDumping builtin_access for "); + print_gimple_expr (fp, stmt, TDF_LINENO); + fputs (":\n", fp); + } + + acs.dump (fp); +} + +DEBUG_FUNCTION void +debug (gimple *stmt, const builtin_access &acs) +{ + dump_builtin_access (stdout, stmt, acs); +} diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 1f9b5ac567b..c230b2a545e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2019-09-19 Martin Sebor + + PR middle-end/91631 + * /c-c++-common/Warray-bounds-3.c: Correct expected offsets. + * /c-c++-common/Warray-bounds-4.c: Same. + * gcc.dg/Warray-bounds-39.c: Remove xfails. + * gcc.dg/Warray-bounds-45.c: New test. + * gcc.dg/Warray-bounds-46.c: New test. + 2019-09-19 Lewis Hyatt PR c/67224 diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-3.c b/gcc/testsuite/c-c++-common/Warray-bounds-3.c index 7f2b33b404a..ab84c602625 100644 --- a/gcc/testsuite/c-c++-common/Warray-bounds-3.c +++ b/gcc/testsuite/c-c++-common/Warray-bounds-3.c @@ -115,7 +115,7 @@ void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n) offset, i.e., 7 + 3. Including the whole final range because would be confusing (the upper bound would either be negative or a very large positive number) so only the lower bound is included. */ - T (char, 9, a, a + SAR ( 0, 6), 3); /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a, a + SAR ( 0, 6), 3); /* { dg-warning "forming offset 9 is out of the bounds \\\[0, 9] of object " "memcpy" } */ /* This fails because the offset isn't represented as an SSA_NAME but rather as a GIMPLE_PHI (offset, 0). With some effort it is @@ -129,18 +129,18 @@ void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n) T (char, 9, a, a + SAR ( 2, 6), 3); T (char, 9, a, a + SAR ( 3, 6), 3); - T (char, 9, a, a + SAR (-1, 7), 3); /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */ - T (char, 9, a, a + SAR (-2, 8), 3); /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */ - T (char, 9, a, a + SAR (-3, 7), 5); /* { dg-warning "forming offset \\\[10, 13] is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a, a + SAR (-1, 7), 3); /* { dg-warning "forming offset \\\[9, 10] is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a, a + SAR (-2, 8), 3); /* { dg-warning "offset \\\[9, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a, a + SAR (-3, 7), 5); /* { dg-warning "forming offset \\\[9, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */ T (char, 9, a + SAR (-2, -1), a, 3); T (char, 9, a + SAR (-1, 1), a, 3); T (char, 9, a + SAR ( 0, 1), a, 3); T (char, 9, a + SAR ( 0, 2), a, 3); T (char, 9, a + SAR ( 0, 3), a, 3); - T (char, 9, a + SAR ( 0, 6), a, 3); /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */ - T (char, 9, a + SAR (-1, 7), a, 3); /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */ - T (char, 9, a + SAR (-2, 8), a, 3); /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a + SAR ( 0, 6), a, 3); /* { dg-warning "forming offset 9 is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a + SAR (-1, 7), a, 3); /* { dg-warning "forming offset \\\[9, 10] is out of the bounds \\\[0, 9] of object " "memcpy" } */ + T (char, 9, a + SAR (-2, 8), a, 3); /* { dg-warning "offset \\\[9, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */ ptrdiff_t i = SAR (DIFF_MIN + 1, DIFF_MAX - 4); T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 1), 3); @@ -312,13 +312,13 @@ void test_strcpy_bounds (char *d, const char *s) it out of bounds (it isn't) but because the final source offset after the access has completed, is. It would be clearer if the warning mentioned the final offset. */ - TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s); /* { dg-warning "forming offset 3 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]." "strcpy" } */ + TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s); /* { dg-warning "offset 2 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]." "strcpy" } */ TI (char, 2, "", a + SR (3, DIFF_MAX - 1), s); /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]." "strcpy" } */ TI (char, 3, "", a + SR (0, DIFF_MAX - 1), s); TI (char, 3, "", a + SR (1, DIFF_MAX - 1), s); TI (char, 3, "", a + SR (2, DIFF_MAX - 1), s); - TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s); /* { dg-warning "forming offset 4 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]." "strcpy" } */ + TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s); /* { dg-warning "offset 3 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]." "strcpy" } */ TI (char, 3, "", a + SR (4, DIFF_MAX - 1), s); /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]." "strcpy" } */ TI (char, 4, "", a + SR (DIFF_MAX - 2, DIFF_MAX - 1), s); /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]." "strcpy" } */ @@ -364,15 +364,15 @@ void test_strcpy_bounds_memarray_range (void) TM (a5, "0", ma.a5 + i, ma.a5); TM (a5, "01", ma.a5 + i, ma.a5); TM (a5, "012", ma.a5 + i, ma.a5); - TM (a5, "0123", ma.a5 + i, ma.a5); /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 4" "strcpy" } */ + TM (a5, "0123", ma.a5 + i, ma.a5); /* { dg-warning "offset 9 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 4" "strcpy" } */ TM (a11, "0", ma.a5, ma.a11); TM (a11, "01", ma.a5, ma.a11); TM (a11, "012", ma.a5, ma.a11); TM (a11, "0123", ma.a5, ma.a11); - TM (a11, "01234", ma.a5, ma.a11); /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */ - TM (a11, "012345", ma.a5, ma.a11); /* { dg-warning "offset \\\[10, 11] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */ - TM (a11, "0123456", ma.a5, ma.a11); /* { dg-warning "offset \\\[10, 12] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */ + TM (a11, "01234", ma.a5, ma.a11); /* { dg-warning "offset 9 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */ + TM (a11, "012345", ma.a5, ma.a11); /* { dg-warning "offset \\\[9, 10] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */ + TM (a11, "0123456", ma.a5, ma.a11); /* { dg-warning "offset \\\[9, 11] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */ TM (a11, "0123456", ma.a11 + i, "789abcd"); } diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-4.c b/gcc/testsuite/c-c++-common/Warray-bounds-4.c index 7a39b23388a..961107a3472 100644 --- a/gcc/testsuite/c-c++-common/Warray-bounds-4.c +++ b/gcc/testsuite/c-c++-common/Warray-bounds-4.c @@ -44,8 +44,8 @@ void test_memcpy_bounds_memarray_range (void) TM (ma.a5, ma.a5 + j, ma.a5, 1); TM (ma.a5, ma.a5 + j, ma.a5, 3); TM (ma.a5, ma.a5 + j, ma.a5, 5); - TM (ma.a5, ma.a5 + j, ma.a5, 7); /* { dg-warning "offset \\\[6, 8] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */ - TM (ma.a5, ma.a5 + j, ma.a5, 9); /* { dg-warning "offset \\\[6, 10] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */ + TM (ma.a5, ma.a5 + j, ma.a5, 7); /* { dg-warning "offset \\\[5, 7] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */ + TM (ma.a5, ma.a5 + j, ma.a5, 9); /* { dg-warning "offset \\\[5, 9] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */ } void test_strcpy_bounds_memarray_range (void) @@ -67,7 +67,7 @@ void test_strcpy_bounds_memarray_range (void) #if __i386__ || __x86_64__ /* Disabled for non-x86 targets due to bug 83462. */ - TM ("", "012345", ma.a7 + i, ma.a7); /* { dg-warning "offset 13 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" "strcpy" { xfail { ! { i?86-*-* x86_64-*-* } } } } */ + TM ("", "012345", ma.a7 + i, ma.a7); /* { dg-warning "offset 12 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" "strcpy" { xfail { ! { i?86-*-* x86_64-*-* } } } } */ #endif } diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-39.c b/gcc/testsuite/gcc.dg/Warray-bounds-39.c index ca42af86cd4..f10ffaca5cb 100644 --- a/gcc/testsuite/gcc.dg/Warray-bounds-39.c +++ b/gcc/testsuite/gcc.dg/Warray-bounds-39.c @@ -123,7 +123,7 @@ char* test_strcpy_s0 (char *d) char* test_strcpy_s0_0 (char *d) { - return strcpy (d, s0_0[0]); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr88991" { xfail *-*-* } } */ + return strcpy (d, s0_0[0]); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */ } @@ -139,10 +139,10 @@ char* test_strncpy_s0_2 (char *d) char* test_strncpy_s0_0_1 (char *d) { - return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr88991" { xfail *-*-* } } */ + return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */ } char* test_strncpy_s0_0_2 (char *d) { - return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr88991" { xfail *-*-* } } */ + return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */ } diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-45.c b/gcc/testsuite/gcc.dg/Warray-bounds-45.c new file mode 100644 index 00000000000..e21452fdf24 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-45.c @@ -0,0 +1,330 @@ +/* PR middle-end/91631 - buffer overflow into an array member of a declared + object not detected + Test to verify that past-the-end accesses by string functions to member + arrays by-reference objects are diagnosed. + { dg-do compile } + { dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" } */ + +extern char* strcpy (char*, const char*); +extern char* strcat (char*, const char*); + +void sink (void*, ...); + +#define S36 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +#define S(N) (S36 + sizeof (S36) - N - 1) + +/* In the test macro, prevent the strcpy to memcpy transformation + by using a local array initialized with the string literal. Without + it, GCC transforms the strcpy call with memcpy which (unfortunately) + permits accesses that cross subobject boundaries. */ +#define T(dst, ncpy, ncat) \ + do { \ + const char a[] = S36; \ + strcpy (dst, a + sizeof a - ncpy - 1); \ + const char b[] = S36; \ + strcat (dst, b + sizeof b - ncat - 1); \ + sink (dst); \ + } while (0) + + +struct MemArrays +{ + char a7[7]; // { dg-message "'a7' declared here" } + char a4[4]; // { dg-message "'a4' declared here" } + char a3[3]; // { dg-message "'a3' declared here" } +}; + +struct MemArrays gma; + +void strcat_value (void) +{ + T (gma.a7, 1, 1); + T (gma.a7, 1, 5); + T (gma.a7, 1, 6); // { dg-warning "'strcat' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + T (gma.a7, 1, 7); // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + + T (gma.a7, 2, 1); + T (gma.a7, 2, 4); + T (gma.a7, 2, 5); // { dg-warning "'strcat' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + T (gma.a7, 2, 6); // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + + T (gma.a7, 5, 1); + T (gma.a7, 5, 2); // { dg-warning "'strcat' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + + T (gma.a4, 1, 1); + T (gma.a4, 1, 2); + T (gma.a4, 1, 3); // { dg-warning "'strcat' offset 11 from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" } + T (gma.a4, 1, 4); // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" } + + T (gma.a4, 2, 3); // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" } + + T (gma.a3, 1, 1); + T (gma.a3, 1, 2); // { dg-warning "'strcat' offset 14 from the object at 'gma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 11" } +} + + +void strcat_ref (struct MemArrays *pma) +{ + T (pma->a7, 1, 1); + T (pma->a7, 1, 5); + T (pma->a7, 1, 6); // { dg-warning "'strcat' offset 7 from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + T (pma->a7, 1, 7); // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + + T (pma->a7, 2, 1); + T (pma->a7, 2, 4); + T (pma->a7, 2, 5); // { dg-warning "'strcat' offset 7 from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + T (pma->a7, 2, 6); // { dg-warning "'strcat' offset \\\[7, 8] from the object at 'pma' is out of the bounds of referenced subobject 'a7' with type 'char\\\[7]' at offset 0" } + + T (pma->a4, 1, 1); + T (pma->a4, 1, 2); + T (pma->a4, 1, 3); // { dg-warning "'strcat' offset 11 from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" } + T (pma->a4, 1, 4); // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" } + + T (pma->a4, 2, 3); // { dg-warning "'strcat' offset \\\[11, 12] from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 7" } + + T (pma->a3, 1, 1); + T (pma->a3, 1, 2); // { dg-warning "'strcat' offset 14 from the object at 'pma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 11" } +} + + +#define T2(dst1, dst2, ncpy, ncat) \ + do { \ + const char a[] = S36; \ + strcpy (dst1, a + sizeof a - ncpy - 1); \ + const char b[] = S36; \ + strcat (dst2, b + sizeof b - ncat - 1); \ + sink (dst1, dst2); \ + } while (0) + +struct ArraysOfMemArrays +{ + struct MemArrays ma3[3]; +} a3[3]; + +void strcat_arrays_of_arrays_value (void) +{ + T2 (a3[0].ma3[0].a7, a3[0].ma3[0].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (a3[0].ma3[0].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[0].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[1].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[1].a7, a3[0].ma3[1].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (a3[0].ma3[1].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[2].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[2].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[2].a7, a3[0].ma3[2].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + + T2 (a3[0].ma3[0].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[0].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[0].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[1].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[1].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[1].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[2].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[2].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[2].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[0].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[0].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[0].a7, a3[2].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[1].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[1].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[1].a7, a3[2].ma3[2].a7, 6, 6); + + T2 (a3[0].ma3[2].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[0].ma3[2].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[0].ma3[2].a7, a3[2].ma3[2].a7, 6, 6); + + + T2 (a3[1].ma3[0].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[0].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[0].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[1].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[1].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[1].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[2].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[2].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[2].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[0].a7, a3[1].ma3[0].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (a3[1].ma3[0].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[0].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[1].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[1].a7, a3[1].ma3[1].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (a3[1].ma3[1].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[2].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[2].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[2].a7, a3[1].ma3[2].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + + T2 (a3[1].ma3[0].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[0].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[0].a7, a3[2].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[1].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[1].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[1].a7, a3[2].ma3[2].a7, 6, 6); + + T2 (a3[1].ma3[2].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[1].ma3[2].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[1].ma3[2].a7, a3[2].ma3[2].a7, 6, 6); + + + T2 (a3[2].ma3[0].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[0].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[0].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[1].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[1].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[1].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[2].a7, a3[0].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[2].a7, a3[0].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[2].a7, a3[0].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[0].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[0].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[0].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[1].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[1].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[1].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[2].a7, a3[1].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[2].a7, a3[1].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[2].a7, a3[1].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[0].a7, a3[2].ma3[0].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (a3[2].ma3[0].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[0].a7, a3[2].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[1].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[1].a7, a3[2].ma3[1].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (a3[2].ma3[1].a7, a3[2].ma3[2].a7, 6, 6); + + T2 (a3[2].ma3[2].a7, a3[2].ma3[0].a7, 6, 6); + T2 (a3[2].ma3[2].a7, a3[2].ma3[1].a7, 6, 6); + T2 (a3[2].ma3[2].a7, a3[2].ma3[2].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } +} + + +void strcat_arrays_of_arrays_ref (struct ArraysOfMemArrays *p) +{ + T2 (p[0].ma3[0].a7, p[0].ma3[0].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (p[0].ma3[0].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[0].ma3[0].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[1].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[0].ma3[1].a7, p[0].ma3[1].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (p[0].ma3[1].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[2].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[0].ma3[2].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[0].ma3[2].a7, p[0].ma3[2].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + + T2 (p[0].ma3[0].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[0].ma3[0].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[0].ma3[0].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[1].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[0].ma3[1].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[0].ma3[1].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[2].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[0].ma3[2].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[0].ma3[2].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[0].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[0].ma3[0].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[0].ma3[0].a7, p[2].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[1].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[0].ma3[1].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[0].ma3[1].a7, p[2].ma3[2].a7, 6, 6); + + T2 (p[0].ma3[2].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[0].ma3[2].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[0].ma3[2].a7, p[2].ma3[2].a7, 6, 6); + + + T2 (p[1].ma3[0].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[1].ma3[0].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[1].ma3[0].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[1].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[1].ma3[1].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[1].ma3[1].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[2].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[1].ma3[2].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[1].ma3[2].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[0].a7, p[1].ma3[0].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (p[1].ma3[0].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[1].ma3[0].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[1].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[1].ma3[1].a7, p[1].ma3[1].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (p[1].ma3[1].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[2].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[1].ma3[2].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[1].ma3[2].a7, p[1].ma3[2].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + + T2 (p[1].ma3[0].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[1].ma3[0].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[1].ma3[0].a7, p[2].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[1].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[1].ma3[1].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[1].ma3[1].a7, p[2].ma3[2].a7, 6, 6); + + T2 (p[1].ma3[2].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[1].ma3[2].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[1].ma3[2].a7, p[2].ma3[2].a7, 6, 6); + + + T2 (p[2].ma3[0].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[2].ma3[0].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[2].ma3[0].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[1].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[2].ma3[1].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[2].ma3[1].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[2].a7, p[0].ma3[0].a7, 6, 6); + T2 (p[2].ma3[2].a7, p[0].ma3[1].a7, 6, 6); + T2 (p[2].ma3[2].a7, p[0].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[0].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[2].ma3[0].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[2].ma3[0].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[1].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[2].ma3[1].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[2].ma3[1].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[2].a7, p[1].ma3[0].a7, 6, 6); + T2 (p[2].ma3[2].a7, p[1].ma3[1].a7, 6, 6); + T2 (p[2].ma3[2].a7, p[1].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[0].a7, p[2].ma3[0].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (p[2].ma3[0].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[2].ma3[0].a7, p[2].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[1].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[2].ma3[1].a7, p[2].ma3[1].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } + T2 (p[2].ma3[1].a7, p[2].ma3[2].a7, 6, 6); + + T2 (p[2].ma3[2].a7, p[2].ma3[0].a7, 6, 6); + T2 (p[2].ma3[2].a7, p[2].ma3[1].a7, 6, 6); + T2 (p[2].ma3[2].a7, p[2].ma3[2].a7, 6, 6); // { dg-warning "\\\[-Warray-bounds" } +} diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-46.c b/gcc/testsuite/gcc.dg/Warray-bounds-46.c new file mode 100644 index 00000000000..09b577e6363 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-46.c @@ -0,0 +1,249 @@ +/* PR middle-end/91631 - buffer overflow into an array member of a declared + object not detected + Test to verify that past-the-end accesses by string functions to member + arrays by-reference objects are diagnosed. + { dg-do compile } + { dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" } */ + +#define SA(expr) typedef int StaticAssert [2 * !!(expr) - 1] + +typedef __SIZE_TYPE__ size_t; + +extern char* strcpy (char*, const char*); +extern char* strncpy (char*, const char*, size_t); + +void sink (void*); + +struct MA17 +{ + char pad[4]; + char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9], a10[10]; + char a11[11], a12[12], a13[13], a14[14], a15[15], a16[16], a17[17], ax[]; +}; + +extern struct MA17 gma; +extern struct MA17 gma2[2]; + +struct MA17 igma_3 = { .ax = { 1, 2, 3 } }; +struct MA17 igma2_[2]; + +#define S36 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +#define S(N) (S36 + sizeof (S36) - N - 1) + +/* In the test macro, prevent the strcpy to memcpy transformation + by using a local array initialized with the string literal. Without + it, GCC transforms the strcpy call with memcpy which (unfortunately) + permits accesses that cross subobject boundaries. */ +#define T(dst, n) \ + do { \ + const char a[] = S36; \ + strcpy (dst, a + sizeof a - n - 1); \ + sink (dst); \ + } while (0) + +void strcpy_global (void) +{ + T (gma.a1, 0); + T (gma.a1, 1); // { dg-warning "'strcpy' offset 5 from the object at 'gma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + T (gma.a1, 4); // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'gma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + + T (gma.a2, 1); + T (gma.a2, 2); // { dg-warning "'strcpy' offset 7 from the object at 'gma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" } + + T (gma.a3, 2); + T (gma.a3, 3); // { dg-warning "'strcpy' offset 10 from the object at 'gma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" } + + T (gma.a4, 3); + T (gma.a4, 4); // { dg-warning "'strcpy' offset 14 from the object at 'gma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" } + + T (gma.a5, 4); + T (gma.a5, 5); // { dg-warning "'strcpy' offset 19 from the object at 'gma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" } + + SA (__builtin_offsetof (struct MA17, a17) == 140); + + T (gma.a17, 16); + T (gma.a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" } + + 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'" } +} + + +void strcpy_global_array (void) +{ + T (gma2[0].a1, 0); + T (gma2[0].a1, 1); // { dg-warning "'strcpy' offset 5 from the object at 'gma2' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + T (gma2[0].a1, 4); // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'gma2' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + + T (gma2[0].a2, 1); + T (gma2[0].a2, 2); // { dg-warning "'strcpy' offset 7 from the object at 'gma2' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" } + + T (gma2[0].a3, 2); + T (gma2[0].a3, 3); // { dg-warning "'strcpy' offset 10 from the object at 'gma2' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" } + + T (gma2[0].a4, 3); + T (gma2[0].a4, 4); // { dg-warning "'strcpy' offset 14 from the object at 'gma2' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" } + + T (gma2[0].a5, 4); + T (gma2[0].a5, 5); // { dg-warning "'strcpy' offset 19 from the object at 'gma2' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" } + + 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 + 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" } + + /* 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 (igma_3.ax, 0); + T (igma_3.ax, 1); + T (igma_3.ax, 1); + T (igma_3.ax, 3); // { dg-warning " offset 160 " } + T (igma_3.ax, 9); // { dg-warning " offset \\\[160, 166] " } +} + + +void strcpy_local (void) +{ + struct MA17 lma; + + T (lma.a1, 0); + T (lma.a1, 1); // { dg-warning "'strcpy' offset 5 from the object at 'lma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + T (lma.a1, 4); // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'lma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + + T (lma.a2, 1); + T (lma.a2, 2); // { dg-warning "'strcpy' offset 7 from the object at 'lma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" } + + T (lma.a3, 2); + T (lma.a3, 3); // { dg-warning "'strcpy' offset 10 from the object at 'lma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" } + + T (lma.a4, 3); + T (lma.a4, 4); // { dg-warning "'strcpy' offset 14 from the object at 'lma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" } + + T (lma.a5, 4); + T (lma.a5, 5); // { dg-warning "'strcpy' offset 19 from the object at 'lma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" } + + 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" } +} + + +void strcpy_ref (struct MA17 *pma) +{ + T (pma->a1, 0); + T (pma->a1, 1); // { dg-warning "'strcpy' offset 5 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + T (pma->a1, 4); // { dg-warning "'strcpy' offset \\\[5, 8] from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 4" } + + T (pma->a2, 1); + T (pma->a2, 2); // { dg-warning "'strcpy' offset 7 from the object at 'pma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 5" } + + T (pma->a3, 2); + T (pma->a3, 3); // { dg-warning "'strcpy' offset 10 from the object at 'pma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 7" } + + T (pma->a4, 3); + T (pma->a4, 4); // { dg-warning "'strcpy' offset 14 from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 10" } + + T (pma->a5, 4); + T (pma->a5, 5); // { dg-warning "'strcpy' offset 19 from the object at 'pma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 14" } + + T (pma->a17, 16); + T (pma->a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'pma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" } + + T (pma->ax, 0); + T ((*pma).ax, 8); + T (pma[0].ax, 9); + + SA (__builtin_offsetof (struct MA17, a1) == 4 + && sizeof (struct MA17) == 157); + + T (pma[1].a1, 0); + T (pma[1].a1, 1); // { dg-warning "'strcpy' offset 162 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 161" } + T (pma[1].a1, 4); // { dg-warning "'strcpy' offset \\\[162, 165] from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset 161" } + + T (pma[1].a2, 1); + T (pma[1].a2, 2); // { dg-warning "'strcpy' offset 164 from the object at 'pma' is out of the bounds of referenced subobject 'a2' with type 'char\\\[2]' at offset 162" } + + T (pma[1].a3, 2); + T (pma[1].a3, 3); // { dg-warning "'strcpy' offset 167 from the object at 'pma' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset 164" } + + T (pma[1].a4, 3); + T (pma[1].a4, 4); // { dg-warning "'strcpy' offset 171 from the object at 'pma' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset 167" } + + T (pma[1].a5, 4); + T (pma[1].a5, 5); // { dg-warning "'strcpy' offset 176 from the object at 'pma' is out of the bounds of referenced subobject 'a5' with type 'char\\\[5]' at offset 171" } + + T (pma[1].a17, 16); + T (pma[1].a17, 17); // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 297" } + + /* Since PMA points to an array of structs, accessing the flexible + member of any of the elements of the array except for the last one + would necessarily access a part of the next element of the enclosing + 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].a1, 0); + T (pma[-1].a1, 1); // { dg-warning "'strcpy' offset -152 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" } + T (pma[-1].a1, 4); // { dg-warning "'strcpy' offset \\\[-152, -149] from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" } +} + +struct MA3 +{ + char a4[4]; // { dg-message "'a4' declared here" } + char a3[3]; // { dg-message "'a3' declared here" } + char c; +}; + +void strcpy_ref_note (struct MA17 *pma, struct MA3 *pma3) +{ + T (pma3[-1].a4, 0); + T (pma3[-1].a4, 1); + T (pma3[-1].a4, 2); + T (pma3[-1].a4, 3); + T (pma3[-1].a4, 4); // { dg-warning "'strcpy' offset -4 from the object at 'pma3' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset -8" } + T (pma3[-1].a4, 5); // { dg-warning "'strcpy' offset \\\[-4, -3] from the object at 'pma3' is out of the bounds of referenced subobject 'a4' with type 'char\\\[4]' at offset -8" } + + T (pma3[-1].a3, 0); + T (pma3[-1].a3, 1); + T (pma3[-1].a3, 2); + T (pma3[-1].a3, 3); // { dg-warning "'strcpy' offset -1 from the object at 'pma3' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset -4" } + T (pma3[-1].a3, 4); // { dg-warning "'strcpy' offset \\\[-1, 0] from the object at 'pma3' is out of the bounds of referenced subobject 'a3' with type 'char\\\[3]' at offset -4" } +} + + +void strncpy_vla_member (unsigned n) +{ + struct VarLenStruct { + char a4[4], an[n], bn[n]; + } x; + + sink (&x); + + strncpy (x.bn, x.a4, sizeof x.bn); + sink (&x); + + strncpy (x.a4, x.bn, sizeof x.a4); + x.a4[sizeof x.a4 - 1] = '\0'; + sink (&x); + + strncpy (x.a4, x.bn, n); + sink (&x); + + strncpy (x.an, x.bn, sizeof x.bn); /* { dg-bogus "\\\[-Warray-bounds" } */ + sink (&x); +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index b9793207a71..5e1054be48e 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -3057,11 +3057,12 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) /* Compute the size of the source sequence, including the nul. */ tree srcsize = srclen ? srclen : size_zero_node; - srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1)); - + tree one = build_int_cst (type, 1); + srcsize = fold_build2 (PLUS_EXPR, type, srcsize, one); + tree dstsize = fold_build2 (PLUS_EXPR, type, dstlen, one); tree sptr = si && si->ptr ? si->ptr : src; - if (check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize)) + if (check_bounds_or_overlap (stmt, dst, sptr, dstsize, srcsize)) { gimple_set_no_warning (stmt, true); set_no_warning = true; diff --git a/gcc/tree.c b/gcc/tree.c index 6be756c19ee..59ea6b93454 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -67,6 +67,7 @@ along with GCC; see the file COPYING3. If not see #include "rtl.h" #include "regs.h" #include "tree-vector-builder.h" +#include "gimple-fold.h" /* Tree code classes. */ @@ -13850,6 +13851,75 @@ component_ref_field_offset (tree exp) return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp); } +/* 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. */ + +tree +component_ref_size (tree ref) +{ + gcc_assert (TREE_CODE (ref) == COMPONENT_REF); + + 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 memtype = TREE_TYPE (member); + if (TREE_CODE (memtype) != ARRAY_TYPE + || !array_at_struct_end_p (ref)) + return size; + + if (!integer_zerop (size)) + if (tree dom = TYPE_DOMAIN (memtype)) + if (tree min = TYPE_MIN_VALUE (dom)) + if (tree max = TYPE_MAX_VALUE (dom)) + if (TREE_CODE (min) == INTEGER_CST + && TREE_CODE (max) == INTEGER_CST) + { + offset_int minidx = wi::to_offset (min); + offset_int maxidx = wi::to_offset (max); + if (maxidx - minidx > 1) + 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 "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 + ? NULL_TREE : integer_zero_node); +} + /* Return the machine mode of T. For vectors, returns the mode of the inner type. The main use case is to feed the result to HONOR_NANS, avoiding the BLKmode that a direct TYPE_MODE (T) might return. */ diff --git a/gcc/tree.h b/gcc/tree.h index 3fc36a4d087..c825109b5f7 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5263,6 +5263,13 @@ extern bool array_at_struct_end_p (tree); by EXP. This does not include any offset in DECL_FIELD_BIT_OFFSET. */ extern tree component_ref_field_offset (tree); +/* Return the size of the member referenced by the COMPONENT_REF, using + its initializer expression if necessary in order to determine the size + 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 int tree_map_base_eq (const void *, const void *); extern unsigned int tree_map_base_hash (const void *); extern int tree_map_base_marked_p (const void *); -- 2.30.2