From: Martin Sebor Date: Wed, 10 Jun 2020 18:00:08 +0000 (-0600) Subject: PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing array plus... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a2c2cee92e5defff9bf23d3b1184ee96e57e5fdd;p=gcc.git PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing array plus offset Also resolves: PR middle-end/92939 - missing -Wstringop-overflow on negative index from the end of array gcc/ChangeLog: PR middle-end/95353 PR middle-end/92939 * builtins.c (inform_access): New function. (check_access): Call it. Add argument. (addr_decl_size): Remove. (get_range): New function. (compute_objsize): New overload. Only use compute_builtin_object_size with raw memory function. (check_memop_access): Pass new argument to compute_objsize and check_access. (expand_builtin_memchr, expand_builtin_strcat): Same. (expand_builtin_strcpy, expand_builtin_stpcpy_1): Same. (expand_builtin_stpncpy, check_strncat_sizes): Same. (expand_builtin_strncat, expand_builtin_strncpy): Same. (expand_builtin_memcmp): Same. * builtins.h (check_nul_terminated_array): Declare extern. (check_access): Add argument. (struct access_ref, struct access_data): New structs. * gimple-ssa-warn-restrict.c (clamp_offset): New helper. (builtin_access::overlap): Call it. * tree-object-size.c (decl_init_size): Declare extern. (addr_object_size): Correct offset computation. * tree-object-size.h (decl_init_size): Declare. * tree-ssa-strlen.c (handle_integral_assign): Remove a call to maybe_warn_overflow when assigning to an SSA_NAME. gcc/testsuite/ChangeLog: PR middle-end/95353 PR middle-end/92939 * c-c++-common/Wstringop-truncation.c: Remove an xfail. * gcc.dg/Warray-bounds-46.c: Remove a bogus warning. * gcc.dg/Wrestrict-9.c: Disable -Wstringop-overflow. * gcc.dg/Wstringop-overflow-12.c: Remove xfails. * gcc.dg/Wstringop-overflow-28.c: Same. * gcc.dg/builtin-stringop-chk-4.c: Same. * gcc.dg/builtin-stringop-chk-5.c: Same. * gcc.dg/builtin-stringop-chk-8.c: Same. * gcc.dg/strlenopt-74.c: Avoid buffer overflow. * gcc.dg/Wstringop-overflow-34.c: New test. * gcc.dg/Wstringop-overflow-35.c: New test. * gcc.dg/Wstringop-overflow-36.c: New test. * gcc.dg/Wstringop-overflow-37.c: New test. * gcc.dg/Wstringop-overflow-38.c: New test. --- diff --git a/gcc/builtins.c b/gcc/builtins.c index 667d36c940b..caab188e81c 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3394,6 +3394,130 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], return warned; } +/* Issue an inform message describing the target of an access REF. + WRITE is set for a write access and clear for a read access. */ + +static void +inform_access (const access_ref &ref, bool write) +{ + if (!ref.ref) + return; + + /* Convert offset range and avoid including a zero range since it isn't + necessarily meaningful. */ + long long minoff = 0, maxoff = 0; + if (wi::fits_shwi_p (ref.offrng[0]) + && wi::fits_shwi_p (ref.offrng[1])) + { + minoff = ref.offrng[0].to_shwi (); + maxoff = ref.offrng[1].to_shwi (); + } + + /* Convert size range and always include it since all sizes are + meaningful. */ + unsigned long long minsize = 0, maxsize = 0; + if (wi::fits_shwi_p (ref.sizrng[0]) + && wi::fits_shwi_p (ref.sizrng[1])) + { + minsize = ref.sizrng[0].to_shwi (); + maxsize = ref.sizrng[1].to_shwi (); + } + + char sizestr[80]; + location_t loc; + tree allocfn = NULL_TREE; + if (TREE_CODE (ref.ref) == SSA_NAME) + { + gimple *stmt = SSA_NAME_DEF_STMT (ref.ref); + gcc_assert (is_gimple_call (stmt)); + loc = gimple_location (stmt); + allocfn = gimple_call_fndecl (stmt); + if (!allocfn) + /* Handle calls through pointers to functions. */ + allocfn = gimple_call_fn (stmt); + + /* SIZRNG doesn't necessarily have the same range as the allocation + size determined by gimple_call_alloc_size (). */ + + if (minsize == maxsize) + sprintf (sizestr, "%llu", minsize); + else + sprintf (sizestr, "[%llu, %llu]", minsize, maxsize); + + } + else + loc = DECL_SOURCE_LOCATION (ref.ref); + + if (write) + { + if (DECL_P (ref.ref)) + { + if (minoff == maxoff) + { + if (minoff == 0) + inform (loc, "destination object %qD", ref.ref); + else + inform (loc, "at offset %lli into destination object %qD", + minoff, ref.ref); + } + else + inform (loc, "at offset [%lli, %lli] into destination object %qD", + minoff, maxoff, ref.ref); + return; + } + + if (minoff == maxoff) + { + if (minoff == 0) + inform (loc, "destination object of size %s allocated by %qE", + sizestr, allocfn); + else + inform (loc, + "at offset %lli into destination object of size %s " + "allocated by %qE", minoff, sizestr, allocfn); + } + else + inform (loc, + "at offset [%lli, %lli] into destination object of size %s " + "allocated by %qE", + minoff, maxoff, sizestr, allocfn); + + return; + } + + if (DECL_P (ref.ref)) + { + if (minoff == maxoff) + { + if (minoff == 0) + inform (loc, "source object %qD", ref.ref); + else + inform (loc, "at offset %lli into source object %qD", + minoff, ref.ref); + } + else + inform (loc, "at offset [%lli, %lli] into source object %qD", + minoff, maxoff, ref.ref); + return; + } + + if (minoff == maxoff) + { + if (minoff == 0) + inform (loc, "source object of size %s allocated by %qE", + sizestr, allocfn); + else + inform (loc, + "at offset %lli into source object of size %s " + "allocated by %qE", minoff, sizestr, allocfn); + } + else + inform (loc, + "at offset [%lli, %lli] into source object of size %s " + "allocated by %qE", + minoff, maxoff, sizestr, allocfn); +} + /* Try to verify that the sizes and lengths of the arguments to a string manipulation function given by EXP are within valid bounds and that the operation does not lead to buffer overflow or read past the end. @@ -3423,13 +3547,16 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], ACCESS is true for accesses, false for simple size checks in calls to functions that neither read from nor write to the region. + When nonnull, PAD points to a more detailed description of the access. + If the call is successfully verified as safe return true, otherwise return false. */ bool check_access (tree exp, tree, tree, tree dstwrite, tree maxread, tree srcstr, tree dstsize, - bool access /* = true */) + bool access /* = true */, + const access_data *pad /* = NULL */) { int opt = OPT_Wstringop_overflow_; @@ -3633,7 +3760,11 @@ check_access (tree exp, tree, tree, tree dstwrite, exp, range[0], range[1], dstsize)); if (warned) - TREE_NO_WARNING (exp) = true; + { + TREE_NO_WARNING (exp) = true; + if (pad) + inform_access (pad->dst, true); + } /* Return error when an overflow has been detected. */ return false; @@ -3741,8 +3872,11 @@ check_access (tree exp, tree, tree, tree dstwrite, loc = expansion_point_location_if_in_system_header (loc); if (warn_for_access (loc, func, exp, opt, range, slen, access)) - TREE_NO_WARNING (exp) = true; - + { + TREE_NO_WARNING (exp) = true; + if (pad) + inform_access (pad->src, false); + } return false; } @@ -3841,185 +3975,135 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, return wide_int_to_tree (sizetype, rng1[1]); } -/* Helper for compute_objsize. Returns the constant size of the DEST - if it refers to a variable or field and sets *PDECL to the DECL and - *POFF to zero. Otherwise returns null for other nodes. */ +/* Wrapper around the wide_int overload of get_range. Returns the same + result but accepts offset_int instead. */ -static tree -addr_decl_size (tree dest, tree *pdecl, tree *poff) +static bool +get_range (tree x, signop sgn, offset_int r[2], + const vr_values *rvals /* = NULL */) { - if (TREE_CODE (dest) == ADDR_EXPR) - dest = TREE_OPERAND (dest, 0); - - if (DECL_P (dest)) - { - *pdecl = dest; - *poff = integer_zero_node; - if (tree size = DECL_SIZE_UNIT (dest)) - return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE; - } - - if (TREE_CODE (dest) == COMPONENT_REF) - { - *pdecl = TREE_OPERAND (dest, 1); - *poff = integer_zero_node; - /* Only return constant sizes for now while callers depend on it. */ - if (tree size = component_ref_size (dest)) - return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE; - } + wide_int wr[2]; + if (!get_range (x, wr, rvals)) + return false; - return NULL_TREE; + r[0] = offset_int::from (wr[0], sgn); + r[1] = offset_int::from (wr[1], sgn); + return true; } -/* Helper to compute the size of the object referenced by the DEST +/* Helper to compute the size of the object referenced by the PTR expression which must have pointer type, using Object Size type OSTYPE (only the least significant 2 bits are used). - Returns an estimate of the size of the object represented as - a sizetype constant if successful or NULL when 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. - If nonnull, sets *PDECL to the decl of the referenced subobject - if it can be determined, or to null otherwise. Likewise, when - POFF is nonnull *POFF is set to the offset into *PDECL. + On success, sets PREF->REF to the DECL of the referenced object + if it's unique, otherwise to null, PREF->OFFRNG to the range of + offsets into it, and PREF->SIZRNG to the range of sizes of + the object(s). + VISITED is used to avoid visiting the same PHI operand multiple + times, and, when nonnull, RVALS to determine range information. + Returns true on success, false when the size cannot be determined. The function is intended for diagnostics and should not be used to influence code generation or optimization. */ -tree -compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */, - tree *poff /* = NULL */, const vr_values *rvals /* = NULL */) +static bool +compute_objsize (tree ptr, int ostype, access_ref *pref, + bitmap *visited, const vr_values *rvals /* = NULL */) { - tree dummy_decl = NULL_TREE; - if (!pdecl) - pdecl = &dummy_decl; + if (ostype == 0) + { + /* Use BOS only for raw memory functions like memcpy to get + the size of the largest enclosing object. */ + tree off = NULL_TREE; + unsigned HOST_WIDE_INT size; + if (compute_builtin_object_size (ptr, ostype, &size, &pref->ref, &off)) + { + if (off) + { + offset_int offset = wi::to_offset (off); + pref->offrng[0] += offset; + pref->offrng[1] += offset; + + /* compute_builtin_object_size() returns the remaining + size in PTR. Add the offset to it to get the full + size. */ + pref->sizrng[0] = pref->sizrng[1] = size + offset; + } + else + pref->sizrng[0] = pref->sizrng[1] = size; + return true; + } + } - tree dummy_off = NULL_TREE; - if (!poff) - poff = &dummy_off; + const bool addr = TREE_CODE (ptr) == ADDR_EXPR; + if (addr) + ptr = TREE_OPERAND (ptr, 0); - /* Only the two least significant bits are meaningful. */ - ostype &= 3; + if (DECL_P (ptr)) + { + /* Bail if the reference is to the pointer itself (as opposed + to what it points to). */ + if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr))) + return false; + + tree size = decl_init_size (ptr, false); + if (!size || TREE_CODE (size) != INTEGER_CST) + return false; - if (ostype) - /* Except for overly permissive calls to memcpy and other raw - memory functions with zero OSTYPE, detect the size from simple - DECLs first to more reliably than compute_builtin_object_size - set *PDECL and *POFF. */ - if (tree size = addr_decl_size (dest, pdecl, poff)) - return size; + pref->ref = ptr; + pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + return true; + } - unsigned HOST_WIDE_INT size; - if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff)) - return build_int_cst (sizetype, size); + const tree_code code = TREE_CODE (ptr); - if (TREE_CODE (dest) == SSA_NAME) + if (code == COMPONENT_REF) { - gimple *stmt = SSA_NAME_DEF_STMT (dest); - if (is_gimple_call (stmt)) + if (ostype == 0) { - /* If STMT is a call to an allocation function get the size - from its argument(s). If successful, also set *PDECL to - DEST for the caller to include in diagnostics. */ - if (tree size = gimple_call_alloc_size (stmt)) - { - *pdecl = dest; - *poff = integer_zero_node; - return size; - } - return NULL_TREE; + /* For raw memory functions like memcpy bail if the size + of the enclosing object cannot be determined. */ + access_ref tmpref; + tree ref = TREE_OPERAND (ptr, 0); + if (!compute_objsize (ref, ostype, &tmpref, visited, rvals) + || !tmpref.ref) + return false; } - if (!is_gimple_assign (stmt)) - return NULL_TREE; - - dest = gimple_assign_rhs1 (stmt); + tree field = TREE_OPERAND (ptr, 1); + /* Bail if the reference is to the pointer itself (as opposed + to what it points to). */ + if (!addr && POINTER_TYPE_P (TREE_TYPE (field))) + return false; - tree_code code = gimple_assign_rhs_code (stmt); - if (code == POINTER_PLUS_EXPR) + pref->ref = field; + /* Only return constant sizes for now while callers depend + on it. INT0LEN is true for interior zero-length arrays. */ + bool int0len = false; + tree size = component_ref_size (ptr, &int0len); + if (int0len) { - /* compute_builtin_object_size fails for addresses with - non-constant offsets. Try to determine the range of - such an offset here and use it to adjust the constant - size. */ - tree off = gimple_assign_rhs2 (stmt); - if (TREE_CODE (off) == INTEGER_CST) - { - if (tree size = compute_objsize (dest, ostype, pdecl, poff)) - { - wide_int wioff = wi::to_wide (off); - wide_int wisiz = wi::to_wide (size); - - /* Ignore negative offsets for now. For others, - use the lower bound as the most optimistic - estimate of the (remaining) size. */ - if (wi::neg_p (wioff)) - ; - else - { - if (*poff) - { - *poff = fold_convert (ptrdiff_type_node, *poff); - off = fold_convert (ptrdiff_type_node, *poff); - *poff = size_binop (PLUS_EXPR, *poff, off); - } - else - *poff = off; - if (wi::ltu_p (wioff, wisiz)) - return wide_int_to_tree (TREE_TYPE (size), - wi::sub (wisiz, wioff)); - return size_zero_node; - } - } - } - else if (TREE_CODE (off) == SSA_NAME - && INTEGRAL_TYPE_P (TREE_TYPE (off))) - { - wide_int min, max; - enum value_range_kind rng = get_range_info (off, &min, &max); - - if (rng == VR_RANGE) - if (tree size = compute_objsize (dest, ostype, pdecl, poff)) - { - wide_int wisiz = wi::to_wide (size); - - /* Ignore negative offsets for now. For others, - use the lower bound as the most optimistic - estimate of the (remaining)size. */ - if (wi::neg_p (min) || wi::neg_p (max)) - ; - else - { - /* FIXME: For now, since the offset is non-constant, - clear *POFF to keep it from being "misused." - Eventually *POFF will need to become a range that - can be properly added to the outer offset if it - too is one. */ - *poff = NULL_TREE; - if (wi::ltu_p (min, wisiz)) - return wide_int_to_tree (TREE_TYPE (size), - wi::sub (wisiz, min)); - return size_zero_node; - } - } - } + pref->sizrng[0] = pref->sizrng[1] = 0; + return true; } - else if (code != ADDR_EXPR) - return NULL_TREE; - } - /* Unless computing the largest size (for memcpy and other raw memory - functions), try to determine the size of the object from its type. */ - if (!ostype) - return NULL_TREE; + if (!size || TREE_CODE (size) != INTEGER_CST) + return false; + + pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + return true; + } - if (TREE_CODE (dest) == ARRAY_REF - || TREE_CODE (dest) == MEM_REF) + if (code == ARRAY_REF || code == MEM_REF) { - tree ref = TREE_OPERAND (dest, 0); + tree ref = TREE_OPERAND (ptr, 0); tree reftype = TREE_TYPE (ref); - if (TREE_CODE (dest) == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE) + if (code == ARRAY_REF + && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE) + /* Avoid arrays of pointers. FIXME: Hande pointers to arrays + of known bound. */ + return false; + + if (code == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE) { /* Give up for MEM_REFs of vector types; those may be synthesized from multiple assignments to consecutive data members. See PR @@ -4028,158 +4112,175 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */, MEM_REFs at the time they're created. */ reftype = TREE_TYPE (reftype); if (TREE_CODE (reftype) == VECTOR_TYPE) - return NULL_TREE; + return false; } - tree off = TREE_OPERAND (dest, 1); - if (tree size = compute_objsize (ref, ostype, pdecl, poff)) - { - /* If the declaration of the destination object is known - to have zero size, return zero. */ - if (integer_zerop (size) - && *pdecl && DECL_P (*pdecl) - && *poff && integer_zerop (*poff)) - return size_zero_node; - - /* A valid offset into a declared object cannot be negative. - A zero size with a zero "inner" offset is still zero size - regardless of the "other" offset OFF. */ - if (*poff - && ((integer_zerop (*poff) && integer_zerop (size)) - || (TREE_CODE (*poff) == INTEGER_CST - && tree_int_cst_sgn (*poff) < 0))) - return size_zero_node; - - wide_int offrng[2]; - if (!get_range (off, offrng, rvals)) - return NULL_TREE; - /* Convert to the same precision to keep wide_int from "helpfully" - crashing whenever it sees other arguments. */ - const unsigned sizprec = TYPE_PRECISION (sizetype); - offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED); - offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED); + if (!compute_objsize (ref, ostype, pref, visited, rvals)) + return false; - /* Adjust SIZE either up or down by the sum of *POFF and OFF - above. */ - if (TREE_CODE (dest) == ARRAY_REF) - { - tree lowbnd = array_ref_low_bound (dest); - if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd)) - { - /* Adjust the offset by the low bound of the array - domain (normally zero but 1 in Fortran). */ - unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd); - offrng[0] -= lb; - offrng[1] -= lb; - } + offset_int orng[2]; + tree off = TREE_OPERAND (ptr, 1); + if (!get_range (off, SIGNED, orng, rvals)) + /* Fail unless the size of the object is zero. */ + return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1]; - /* Convert the array index into a byte offset. */ - tree eltype = TREE_TYPE (dest); - tree tpsize = TYPE_SIZE_UNIT (eltype); - if (tpsize && TREE_CODE (tpsize) == INTEGER_CST) - { - wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ()); - offrng[0] *= wsz; - offrng[1] *= wsz; - } - else - return NULL_TREE; + if (TREE_CODE (ptr) == ARRAY_REF) + { + /* Convert the array index range determined above to a byte + offset. */ + tree lowbnd = array_ref_low_bound (ptr); + if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd)) + { + /* Adjust the index by the low bound of the array domain + (normally zero but 1 in Fortran). */ + unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd); + orng[0] -= lb; + orng[1] -= lb; } - wide_int wisize = wi::to_wide (size); + tree eltype = TREE_TYPE (ptr); + tree tpsize = TYPE_SIZE_UNIT (eltype); + if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST) + return false; + + offset_int sz = wi::to_offset (tpsize); + orng[0] *= sz; + orng[1] *= sz; - if (!*poff) + if (TREE_CODE (eltype) == ARRAY_TYPE) { - /* If the "inner" offset is unknown and the "outer" offset - is either negative or less than SIZE, return the size - minus the offset. This may be overly optimistic in - the first case if the inner offset happens to be less - than the absolute value of the outer offset. */ - if (wi::neg_p (offrng[0])) - return size; - if (wi::ltu_p (offrng[0], wisize)) - return build_int_cst (sizetype, (wisize - offrng[0]).to_uhwi ()); - return size_zero_node; + pref->sizrng[0] = pref->offrng[0] + orng[0] + sz; + pref->sizrng[1] = pref->offrng[1] + orng[1] + sz; } + } + + pref->offrng[0] += orng[0]; + pref->offrng[1] += orng[1]; - /* Convert to the same precision to keep wide_int from "helpfuly" - crashing whenever it sees other argumments. */ - offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED); - offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED); + return true; + } - tree dstoff = *poff; - if (integer_zerop (*poff)) - *poff = off; - else if (!integer_zerop (off)) + if (TREE_CODE (ptr) == SSA_NAME) + { + gimple *stmt = SSA_NAME_DEF_STMT (ptr); + if (is_gimple_call (stmt)) + { + /* If STMT is a call to an allocation function get the size + from its argument(s). If successful, also set *PDECL to + PTR for the caller to include in diagnostics. */ + wide_int wr[2]; + if (gimple_call_alloc_size (stmt, wr, rvals)) { - *poff = fold_convert (ptrdiff_type_node, *poff); - off = fold_convert (ptrdiff_type_node, off); - *poff = size_binop (PLUS_EXPR, *poff, off); + pref->ref = ptr; + pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED); + pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED); + return true; } + return false; + } - if (!wi::neg_p (offrng[0])) - { - if (TREE_CODE (size) != INTEGER_CST) - return NULL_TREE; + /* TODO: Handle PHI. */ - /* Return the difference between the size and the offset - or zero if the offset is greater. */ - wide_int wisize = wi::to_wide (size, sizprec); - if (wi::ltu_p (wisize, offrng[0])) - return size_zero_node; + if (!is_gimple_assign (stmt)) + return false; - return wide_int_to_tree (sizetype, wisize - offrng[0]); - } + ptr = gimple_assign_rhs1 (stmt); + + tree_code code = gimple_assign_rhs_code (stmt); + if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE) + /* Avoid conversions from non-pointers. */ + return false; - wide_int dstoffrng[2]; - if (TREE_CODE (dstoff) == INTEGER_CST) - dstoffrng[0] = dstoffrng[1] = wi::to_wide (dstoff); - else if (TREE_CODE (dstoff) == SSA_NAME) + if (code == POINTER_PLUS_EXPR) + { + /* If the the offset in the expression can be determined use + it to adjust the overall offset. Otherwise, set the overall + offset to the maximum. */ + offset_int orng[2]; + tree off = gimple_assign_rhs2 (stmt); + if (!get_range (off, SIGNED, orng, rvals) + || !wi::les_p (orng[0], orng[1])) { - enum value_range_kind rng - = get_range_info (dstoff, dstoffrng, dstoffrng + 1); - if (rng != VR_RANGE) - return NULL_TREE; + orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node)); + orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); } - else - return NULL_TREE; - - dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED); - dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED); - if (!wi::neg_p (dstoffrng[0])) - wisize += dstoffrng[0]; - - offrng[1] += dstoffrng[1]; - if (wi::neg_p (offrng[1])) - return size_zero_node; - - return wide_int_to_tree (sizetype, wisize); + pref->offrng[0] += orng[0]; + pref->offrng[1] += orng[1]; } + else if (code != ADDR_EXPR) + return false; - return NULL_TREE; + return compute_objsize (ptr, ostype, pref, visited, rvals); } - /* Try simple DECLs not handled above. */ - if (tree size = addr_decl_size (dest, pdecl, poff)) - return size; - - tree type = TREE_TYPE (dest); - if (TREE_CODE (type) == POINTER_TYPE) - type = TREE_TYPE (type); - + tree type = TREE_TYPE (ptr); type = TYPE_MAIN_VARIANT (type); - if (TREE_CODE (dest) == ADDR_EXPR) - dest = TREE_OPERAND (dest, 0); + if (TREE_CODE (ptr) == ADDR_EXPR) + ptr = TREE_OPERAND (ptr, 0); if (TREE_CODE (type) == ARRAY_TYPE - && !array_at_struct_end_p (dest)) + && !array_at_struct_end_p (ptr)) { if (tree size = TYPE_SIZE_UNIT (type)) - return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE; + return get_range (size, UNSIGNED, pref->sizrng, rvals); } - return NULL_TREE; + return false; +} + +/* Convenience wrapper around the above. */ + +static tree +compute_objsize (tree ptr, int ostype, access_ref *pref, + const vr_values *rvals = NULL) +{ + bitmap visited = NULL; + + bool success + = compute_objsize (ptr, ostype, pref, &visited, rvals); + + if (visited) + BITMAP_FREE (visited); + + if (!success) + return NULL_TREE; + + if (pref->offrng[0] < 0) + { + if (pref->offrng[1] < 0) + return size_zero_node; + + pref->offrng[0] = 0; + } + + if (pref->sizrng[1] < pref->offrng[0]) + return size_zero_node; + + return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]); +} + +/* Transitional wrapper around the above. The function should be removed + once callers transition to one of the two above. */ + +tree +compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */, + tree *poff /* = NULL */, const vr_values *rvals /* = NULL */) +{ + /* Set the initial offsets to zero and size to negative to indicate + none has been computed yet. */ + access_ref ref; + tree size = compute_objsize (ptr, ostype, &ref, rvals); + if (!size) + return NULL_TREE; + + if (pdecl) + *pdecl = ref.ref; + + if (poff) + *poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]); + + return size; } /* Helper to determine and check the sizes of the source and the destination @@ -4196,11 +4297,12 @@ check_memop_access (tree exp, tree dest, tree src, tree size) try to determine the size of the largest source and destination object using type-0 Object Size regardless of the object size type specified by the option. */ - tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE; - tree dstsize = compute_objsize (dest, 0); + access_data data; + tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE; + tree dstsize = compute_objsize (dest, 0, &data.dst); return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE, - srcsize, dstsize); + srcsize, dstsize, true, &data); } /* Validate memchr arguments without performing any expansion. @@ -4220,9 +4322,11 @@ expand_builtin_memchr (tree exp, rtx) of the object. */ if (warn_stringop_overflow) { - tree size = compute_objsize (arg1, 0); + access_data data; + tree size = compute_objsize (arg1, 0, &data.src); check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, - /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE); + /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE, + true, &data); } return NULL_RTX; @@ -4497,10 +4601,11 @@ expand_builtin_strcat (tree exp) just diagnose cases when the souce string is longer than the destination object. */ - tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); + access_data data; + tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src, - destsize); + destsize, true, &data); return NULL_RTX; } @@ -4521,9 +4626,11 @@ expand_builtin_strcpy (tree exp, rtx target) if (warn_stringop_overflow) { - tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); + access_data data; + tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, + &data.dst); check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, - src, destsize); + src, destsize, true, &data); } if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target)) @@ -4579,9 +4686,11 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode) if (warn_stringop_overflow) { - tree destsize = compute_objsize (dst, warn_stringop_overflow - 1); + access_data data; + tree destsize = compute_objsize (dst, warn_stringop_overflow - 1, + &data.dst); check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, - src, destsize); + src, destsize, true, &data); } /* If return value is ignored, transform stpcpy into strcpy. */ @@ -4690,10 +4799,12 @@ expand_builtin_stpncpy (tree exp, rtx) if (!check_nul_terminated_array (exp, src, len)) return NULL_RTX; + access_data data; /* The size of the destination object. */ - tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); + tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); - check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize); + check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize, + true, &data); return NULL_RTX; } @@ -4733,12 +4844,13 @@ check_strncat_sizes (tree exp, tree objsize) /* Try to verify that the destination is big enough for the shortest string. */ + access_data data; if (!objsize && warn_stringop_overflow) { /* If it hasn't been provided by __strncat_chk, try to determine the size of the destination object into which the source is being copied. */ - objsize = compute_objsize (dest, warn_stringop_overflow - 1); + objsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); } /* Add one for the terminating nul. */ @@ -4769,10 +4881,10 @@ check_strncat_sizes (tree exp, tree objsize) && tree_int_cst_lt (maxread, srclen))) srclen = maxread; - /* The number of bytes to write is LEN but check_access will also + /* The number of bytes to write is LEN but check_access will alsoa check SRCLEN if LEN's value isn't known. */ return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen, - objsize); + objsize, true, &data); } /* Similar to expand_builtin_strcat, do some very basic size validation @@ -4810,10 +4922,11 @@ expand_builtin_strncat (tree exp, rtx) maxlen = lendata.maxbound; } + access_data data; /* Try to verify that the destination is big enough for the shortest string. First try to determine the size of the destination object into which the source is being copied. */ - tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); + tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); /* Add one for the terminating nul. */ tree srclen = (maxlen @@ -4843,8 +4956,8 @@ expand_builtin_strncat (tree exp, rtx) && tree_int_cst_lt (maxread, srclen))) srclen = maxread; - /* The number of bytes to write is SRCLEN. */ - check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize); + check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize, + true, &data); return NULL_RTX; } @@ -4873,13 +4986,14 @@ expand_builtin_strncpy (tree exp, rtx target) if (warn_stringop_overflow) { - tree destsize = compute_objsize (dest, - warn_stringop_overflow - 1); + access_data data; + tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, + &data.dst); /* The number of bytes to write is LEN but check_access will also check SLEN if LEN's value isn't known. */ check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, - destsize); + destsize, true, &data); } /* We must be passed a constant len and src parameter. */ @@ -5192,16 +5306,18 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq) /* Diagnose calls where the specified length exceeds the size of either object. */ - tree size = compute_objsize (arg1, 0); + access_data data; + tree size = compute_objsize (arg1, 0, &data.src); no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, /*maxread=*/NULL_TREE, size, - /*objsize=*/NULL_TREE); + /*objsize=*/NULL_TREE, true, &data); if (no_overflow) { - size = compute_objsize (arg2, 0); + access_data data; + size = compute_objsize (arg2, 0, &data.src); no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, /*maxread=*/NULL_TREE, size, - /*objsize=*/NULL_TREE); + /*objsize=*/NULL_TREE, true, &data); } /* If the specified length exceeds the size of either object, diff --git a/gcc/builtins.h b/gcc/builtins.h index 73e85d66e9b..8b812ceb2c4 100644 --- a/gcc/builtins.h +++ b/gcc/builtins.h @@ -152,12 +152,39 @@ extern bool target_char_cst_p (tree t, char *p); extern internal_fn associated_internal_fn (tree); extern internal_fn replacement_internal_fn (gcall *); -bool check_nul_terminated_array (tree, tree, tree = NULL_TREE); +extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE); extern void warn_string_no_nul (location_t, const char *, tree, tree); extern tree unterminated_array (tree, tree * = NULL, bool * = NULL); extern bool builtin_with_linkage_p (tree); -extern bool check_access (tree, tree, tree, tree, tree, tree, tree, - bool = true); +/* Describes a reference to an object used in an access. */ +struct access_ref +{ + access_ref (): ref () + { + /* Set to valid. */ + offrng[0] = offrng[1] = 0; + /* Invalidate. */ + sizrng[0] = sizrng[1] = -1; + } + + /* Reference to the object. */ + tree ref; + + /* Range of offsets into and sizes of the object(s). */ + offset_int offrng[2]; + offset_int sizrng[2]; +}; + +/* Describes a pair of references used in an access by built-in + functions like memcpy. */ +struct access_data +{ + /* Destination and source of the access. */ + access_ref dst, src; +}; + +extern bool check_access (tree, tree, tree, tree, tree, tree, tree, + bool = true, const access_data * = NULL); #endif /* GCC_BUILTINS_H */ diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c index 19d2ec09aa5..512fc138528 100644 --- a/gcc/gimple-ssa-warn-restrict.c +++ b/gcc/gimple-ssa-warn-restrict.c @@ -1274,6 +1274,27 @@ builtin_access::strcpy_overlap () return generic_overlap (); } +/* For a BASE of array type, clamp REFOFF to at most [0, BASE_SIZE] + if known, or [0, MAXOBJSIZE] otherwise. */ + +static void +clamp_offset (tree base, offset_int refoff[2], offset_int maxobjsize) +{ + if (!base || TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE) + return; + + if (refoff[0] < 0 && refoff[1] >= 0) + refoff[0] = 0; + + if (refoff[1] < refoff[0]) + { + offset_int maxsize = maxobjsize; + if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (base))) + maxsize = wi::to_offset (size); + + refoff[1] = wi::umin (refoff[1], maxsize); + } +} /* Return true if DSTREF and SRCREF describe accesses that either overlap one another or that, in order not to overlap, would imply that the size @@ -1312,35 +1333,12 @@ builtin_access::overlap () /* 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 - && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE) - { - if (acs.dstoff[0] < 0 && acs.dstoff[1] >= 0) - acs.dstoff[0] = 0; - - if (acs.dstoff[1] < acs.dstoff[0]) - { - if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (dstref->base))) - acs.dstoff[1] = wi::umin (acs.dstoff[1], wi::to_offset (size)); - else - acs.dstoff[1] = wi::umin (acs.dstoff[1], maxobjsize); - } - } + clamp_offset (dstref->base, acs.dstoff, maxobjsize); acs.srcoff[0] = srcref->offrange[0]; acs.srcoff[1] = srcref->offrange[1]; - if (srcref->base - && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE) - { - if (acs.srcoff[0] < 0 && acs.srcoff[1] >= 0) - acs.srcoff[0] = 0; - - if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (srcref->base))) - acs.srcoff[1] = wi::umin (acs.srcoff[1], wi::to_offset (size)); - else if (acs.srcoff[1] < acs.srcoff[0]) - acs.srcoff[1] = wi::umin (acs.srcoff[1], maxobjsize); - } + clamp_offset (srcref->base, acs.srcoff, maxobjsize); /* When the upper bound of the offset is less than the lower bound the former is the result of a negative offset being represented diff --git a/gcc/testsuite/c-c++-common/Wstringop-truncation.c b/gcc/testsuite/c-c++-common/Wstringop-truncation.c index 5e43405fde8..f29eee29e85 100644 --- a/gcc/testsuite/c-c++-common/Wstringop-truncation.c +++ b/gcc/testsuite/c-c++-common/Wstringop-truncation.c @@ -269,7 +269,7 @@ void test_strncpy_array (Dest *pd, int i, const char* s) CPY (dst7, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */ CPY (dst7, s, sizeof dst7); /* { dg-warning "specified bound 7 equals destination size" } */ - CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" { xfail *-*-* } } */ + CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" } */ CPY (dst2_5[1], s, sizeof dst2_5[1]); /* { dg-warning "specified bound 5 equals destination size" } */ /* Verify that copies that nul-terminate are not diagnosed. */ diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-46.c b/gcc/testsuite/gcc.dg/Warray-bounds-46.c index 9078c6f5998..3f1c6c715ea 100644 --- a/gcc/testsuite/gcc.dg/Warray-bounds-46.c +++ b/gcc/testsuite/gcc.dg/Warray-bounds-46.c @@ -66,8 +66,10 @@ void strcpy_global (void) 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 from the object at 'gma' is out of the bounds of referenced subobject 'ax' with type 'char[]' at offset 157|'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" } + // GCC allows static initialization of flexible array members of + // non-local objects. Verify that writing into one that may be + // initialized in another translation unit isn't diagnosed. */ + T (gma.ax, 0); // { dg-bogus "\\\[-Warray-bounds" } } diff --git a/gcc/testsuite/gcc.dg/Wrestrict-9.c b/gcc/testsuite/gcc.dg/Wrestrict-9.c index 5ad87401ed9..878fc811bc6 100644 --- a/gcc/testsuite/gcc.dg/Wrestrict-9.c +++ b/gcc/testsuite/gcc.dg/Wrestrict-9.c @@ -1,7 +1,7 @@ /* PR tree-optimization/84095 - false-positive -Wrestrict warnings for strcpy within array { dg-do compile } - { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */ + { dg-options "-O2 -Wrestrict -Wno-stringop-overflow -ftrack-macro-expansion=0" } */ typedef __SIZE_TYPE__ size_t; diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c index 5a05f5cf7cf..1e67b5fd928 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c @@ -29,15 +29,15 @@ void test_memcpy_array_cst_range_off (const void *s) T (d + UR (1, 2), 6); /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */ T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */ - T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */ + T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */ T (d + SR (-2, -1), 1); T (d + SR (-2, -1), 2); /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */ - T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */ + T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " } */ d = ga7 + 7; T (d + SR (-7, -6), 1); T (d + SR (-7, -1), 1); - T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */ + T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " } */ T (d + UR (1, 2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */ } @@ -67,15 +67,15 @@ void test_memset_array_unsigned_off (void) T (d + UR (1, 2), 6); /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */ T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */ - T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */ + T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */ T (d + SR (-2, -1), 1); T (d + SR (-2, -1), 2); /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */ - T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */ + T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " } */ d = ga7 + 7; T (d + SR (-7, -6), 1); T (d + SR (-7, -1), 1); - T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */ + T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " } */ T (d + UR (1, 2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */ } @@ -110,8 +110,8 @@ void test_memcpy_array_signed_off (const void *s) T (d + SR (-7, 7), 7); T (d + SR (-1, 1), 7); - T (d + SR (-1, 1), 9); /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */ - T (d + SR (-1, 2), 9); /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */ + T (d + SR (-1, 1), 9); /* { dg-warning "writing 9 bytes into a region of size " } */ + T (d + SR (-1, 2), 9); /* { dg-warning "writing 9 bytes into a region of size " } */ T (d + SR (1, 2), 1); T (d + SR (1, 2), 5); diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c index 8844b9f5e71..be7f51ad3a5 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c @@ -224,7 +224,7 @@ void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3) q += n; // N=1 N=2 q[-1] = 0; // p[0] p[1] q[-2] = 1; // p[-1] p[0] - q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } } + q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" } /* The following isn't diagnosed because the warning doesn't recognize the index below as necessarily having the same value as the size diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c new file mode 100644 index 00000000000..fd43f3afb59 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c @@ -0,0 +1,252 @@ +/* PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing + array plus offset + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +struct S0 { char n, a[0]; }; + + +void s0_nowarn_cstidx (struct S0 *p) +{ + char *q = p->a; + q[1] = __LINE__; + q[9] = __LINE__; +} + +void s0_nowarn_cstoff_cstidx (struct S0 *p) +{ + char *q = p->a + 1; + q[1] = __LINE__; + q[9] = __LINE__; +} + +void s0_nowarn_varoff_cstdix (struct S0 *p, int i) +{ + char *q = p->a + i; + q[1] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" } + q[9] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" } +} + +void s0_nowarn_cstoff_varidx (struct S0 *p, int i) +{ + char *q = p->a + 1; + q[i] = __LINE__; +} + +void s0_nowarn_varoff_varidx (struct S0 *p, int i, int j) +{ + char *q = p->a + i; + q[j] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" } +} + + +/* Accesses past the end of a trailing array with one element is + discouraged but still reluctantly not diagnosed. This should + change. */ + +struct S1 { char n, a[1]; }; + + +void s1_nowarn_cstidx (struct S1 *p) +{ + char *q = p->a; + q[1] = __LINE__; + q[9] = __LINE__; +} + +void s1_nowarn_cstoff_cstidx (struct S1 *p) +{ + char *q = p->a + 1; + q[1] = __LINE__; + q[9] = __LINE__; +} + +void s1_nowarn_varoff_cstdix (struct S1 *p, int i) +{ + char *q = p->a + i; + q[1] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" } + q[9] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" } +} + +void s1_nowarn_cstoff_varidx (struct S1 *p, int i) +{ + char *q = p->a + 1; + q[i] = __LINE__; +} + +void s1_nowarn_varoff_varidx (struct S1 *p, int i, int j) +{ + char *q = p->a + i; + q[j] = __LINE__; +} + + +/* Accesses past the end of a trailing array with more than one + element should be diagnosed but aren't yet because the MEM_REF + makes the out-of-bounds accesses indistinguishable from valid + ones to subsequent elements of the array pointed by P. */ + +struct S2 { char n, a[2]; }; + + +void s2_warn_cstidx (struct S2 *p) +{ + char *q = p->a; + + /* The following invalid store is represented as + MEM[(char *)p_1(D) + 3B] = __LINE__; + which is indistinguishable from the valid + q = &p[1].n; q[0] = __LINE__; + */ + q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void s2_warn_cstoff_cstidx (struct S2 *p) +{ + char *q = p->a + 1; + q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void s2_warn_varoff_cstdix (struct S2 *p, int i) +{ + char *q = p->a + i; + q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void s2_warn_cstoff_varidx (struct S2 *p, int i) +{ + char *q = p->a + 1; + q[i] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void s2_warn_varoff_varidx (struct S2 *p, int i, int j) +{ + char *q = p->a + i; + q[j] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + + +/* Verify that none of these triggers a bogus warning (not tested + elsewhere but triggered during bootstrap). */ + +void s2_nowarn_varidx_int (struct S2 *p, int i) +{ + extern struct S2 s2; + extern struct S2 s2a[]; + + s2.a[i - 1] = __LINE__; + s2.a[i] = __LINE__; + s2.a[i + 1] = __LINE__; + + s2a[i].a[i - 1] = __LINE__; + s2a[i].a[i] = __LINE__; + s2a[i].a[i + 1] = __LINE__; + + p[i].a[i - 1] = __LINE__; + p[i].a[i] = __LINE__; + p[i].a[i + 1] = __LINE__; + + char *q = p->a; + q[i - 1] = __LINE__; + q[i] = __LINE__; + q[i + 1] = __LINE__; +} + +/* Same as above but with a size_t index in range [1, SIZE_MAX]. */ + +void* s2_nowarn_varidx_size (struct S2 *p, size_t i, size_t j) +{ + extern struct S2 s2; + extern struct S2 s2a[]; + struct S2 *ps2 = __builtin_malloc (3 * sizeof *ps2); + + s2.a[i - 1] = __LINE__; + s2.a[i] = __LINE__; + s2.a[i + 1] = __LINE__; + + s2a[i].a[i - 1] = __LINE__; + s2a[i].a[i] = __LINE__; + s2a[i].a[i + 1] = __LINE__; + + p[i].a[i - 1] = __LINE__; + p[i].a[i] = __LINE__; + p[i].a[i + 1] = __LINE__; + + ps2->a[i - 1] = __LINE__; + ps2->a[i] = __LINE__; + ps2->a[i + 1] = __LINE__; + + char *q = p->a; + q[i - 1] = __LINE__; + q[i] = __LINE__; + q[i + 1] = __LINE__; + + if (j == 0) + return ps2; + + s2.a[j - 1] = __LINE__; + s2.a[j] = __LINE__; + s2.a[j + 1] = __LINE__; + + s2a[j].a[j - 1] = __LINE__; + s2a[j].a[j] = __LINE__; + s2a[j].a[j + 1] = __LINE__; + + p[j].a[j - 1] = __LINE__; + p[j].a[j] = __LINE__; + p[j].a[j + 1] = __LINE__; + + ps2->a[j - 1] = __LINE__; + ps2->a[j] = __LINE__; + ps2->a[j + 1] = __LINE__; + + q = p->a; + q[j - 1] = __LINE__; + q[j] = __LINE__; + q[j + 1] = __LINE__; + + return ps2; +} + +/* Verify that accesses to an interior zero-length array are diagnosed. */ + +struct Si0 { char c, a[0], d; }; + +void si0_warn_cstidx (struct Si0 *p) +{ + // These are indistinguishable from valid accesses to p->d: + // MEM[(char *)p_1(D) + 1B] = 0; + char *q = p->a; + q[1] = __LINE__; // { dg-warning "writing 1 byte into a region of size 0" "pr?????" { xfail *-*-* } } + q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void si0_warn_cstoff_cstidx (struct Si0 *p) +{ + // Like those above, these too are indistinguishable from valid accesses + // to p->d. + char *q = p->a + 1; + q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } + q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void si0_warn_varoff_cstdix (struct Si0 *p, int i) +{ + char *q = p->a + i; + q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" } + q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" } +} + +void si0_warn_cstoff_varidx (struct Si0 *p, int i) +{ + char *q = p->a + 1; + q[i] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } } +} + +void si0_warn_varoff_varidx (struct Si0 *p, int i, int j) +{ + char *q = p->a + i; + q[j] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" } +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-35.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-35.c new file mode 100644 index 00000000000..612137ee469 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-35.c @@ -0,0 +1,76 @@ +/* Verify that calls to strcpy to write to an element of an array of pointers + are not diagnosed (due to mistakenly using the size of the array as that + of the destination). + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +typedef char A1[1]; +typedef char A2[2]; +typedef char A3[3]; +typedef char A4[4]; +typedef char A5[5]; +typedef char A6[6]; +typedef char A7[7]; +typedef char A8[8]; +typedef char A9[9]; +typedef char A10[10]; + +A1* pa1[3]; +A2* pa2[3]; +A3* pa3[3]; +A4* pa4[3]; +A5* pa5[3]; +A6* pa6[3]; +A7* pa7[3]; +A8* pa8[3]; +A9* pa9[3]; +A10* pa10[3]; + +void nowarn_a1_1 (int i) +{ + __builtin_strcpy (*pa1[0], ""); + __builtin_strcpy (*pa1[1], ""); + __builtin_strcpy (*pa1[i], ""); +} + +void nowarn_a2_2 (int i) +{ + __builtin_strcpy (*pa2[0], "1"); + __builtin_strcpy (*pa2[1], "2"); + __builtin_strcpy (*pa2[i], "3"); +} + +void nowarn_a3_3 (int i) +{ + __builtin_strcpy (*pa3[0], "12"); + __builtin_strcpy (*pa3[1], "23"); + __builtin_strcpy (*pa3[i], "34"); +} + +void nowarn_a4_4 (int i) +{ + __builtin_strcpy (*pa4[0], "123"); + __builtin_strcpy (*pa4[1], "234"); + __builtin_strcpy (*pa4[i], "345"); +} + +void nowarn_a5_5 (int i) +{ + __builtin_strcpy (*pa5[0], "1234"); + __builtin_strcpy (*pa5[1], "2345"); + __builtin_strcpy (*pa5[i], "3456"); +} + +void nowarn_a6_6 (int i) +{ + __builtin_strcpy (*pa6[0], "12345"); + __builtin_strcpy (*pa6[1], "23456"); + __builtin_strcpy (*pa6[1], "34567"); +} + +void nowarn_a10_10 (int i) +{ + __builtin_strcpy (*pa10[0], "0123456789"); + __builtin_strcpy (*pa10[1], "1234567890"); + __builtin_strcpy (*pa10[i], "2345678909"); +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-36.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-36.c new file mode 100644 index 00000000000..3f0874dc5bb --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-36.c @@ -0,0 +1,24 @@ +/* Verify that casts between pointers and integers don't trigger false + positives. Test derived from Glibc's _dl_allocate_tls_storage() in + dl-tls.c. + { dg-do compile } + { dg-options "-O2 -Wall -Wno-array-bounds" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +size_t a; +size_t s; + +void* _dl_allocate_tls_storage (void) +{ + void *p = __builtin_malloc (s + a + sizeof (void *)); + + char *q = (char *)(__builtin_constant_p (a) && (((a - 1) & a) == 0) + ? ((((uintptr_t)p) + a - 1) & ~(a - 1)) + : (((((uintptr_t)p) + (a - 1)) / a) * a)); + + char *r = q + s - sizeof (int[4]); + __builtin_memset (r, '\0', sizeof (int[4])); + return r; +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c new file mode 100644 index 00000000000..339f904d7a6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c @@ -0,0 +1,229 @@ +/* Verify that -Wstringop-overflow detects writing past the end of each + individual element of a multidimensional array. + { dg-do compile } + { dg-options "-O2 -Wall -Wno-array-bounds -Wno-stringop-truncation" } */ + +#define CONCAT(x, y) x ## y +#define CAT(name, line) CONCAT (name, line) +#define UNIQ_NAME(name) CAT (name, __LINE__) + +typedef __SIZE_TYPE__ size_t; + +extern void* malloc (size_t); +extern char* strncpy (char*, const char*, size_t); + +extern char a2_2_8[2][2][8]; + +void nowarn_a2_2_8 (const char *s) +{ + // The following trigger -Wstringop-truncation. + strncpy (a2_2_8[0][0], s, 8); + strncpy (a2_2_8[0][1], s, 8); + strncpy (a2_2_8[1][0], s, 8); + strncpy (a2_2_8[1][1], s, 8); +} + +void warn_a2_2_8 (const char *s) +{ + strncpy (a2_2_8[0][0], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " } + strncpy (a2_2_8[0][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " } + strncpy (a2_2_8[1][0], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " } + strncpy (a2_2_8[1][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " } +} + + +extern char a2_3_5[2][3][5]; + +void nowarn_a2_3_5 (const char *s) +{ + // The following trigger -Wstringop-truncation. + strncpy (a2_3_5[0][0], s, 5); + strncpy (a2_3_5[0][1], s, 5); + strncpy (a2_3_5[0][2], s, 5); + strncpy (a2_3_5[1][0], s, 5); + strncpy (a2_3_5[1][1], s, 5); + strncpy (a2_3_5[1][2], s, 5); +} + +void warn_a2_3_5 (const char *s) +{ + strncpy (a2_3_5[0][0], s, 6); // { dg-warning "writing 6 bytes into a region of size 5 " } + strncpy (a2_3_5[0][1], s, 7); // { dg-warning "writing 7 bytes into a region of size 5 " } + strncpy (a2_3_5[1][0], s, 8); // { dg-warning "writing 8 bytes into a region of size 5 " } + strncpy (a2_3_5[1][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 5 " } +} + + +void* nowarn_malloc_3_5 (const char *s, unsigned n) +{ + if (n < 3 || 5 < n) + n = 3; + char *p = (char*)malloc (n); + strncpy (p + 1, s, 4); + return p; +} + +void* warn_malloc_3_5 (const char *s, unsigned n) +{ + if (n < 3 || 5 < n) + n = 3; + char *p = (char*)malloc (n); // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" } + // The size below should be a range like the one above. + strncpy (p + 1, s, 5); // { dg-warning "writing 5 bytes into a region of size 4 " } + return p; +} + + +typedef __attribute__ ((alloc_size (1, 2))) void* UsrAlloc (int, int); + +void* nowarn_use_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n) +{ + if (n < 3 || 5 < n) + n = 3; + char *p = (char*)usr_alloc (n, 3); + strncpy (p + 1, s, 14); + return p; +} + +void* warn_usr_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n) +{ + if (n < 3 || 5 < n) + n = 3; + char *p = (char*)usr_alloc (n, 3); // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" } + // The size below should be a range like the one above. + strncpy (p + 1, s, 15); // { dg-warning "writing 15 bytes into a region of size 14 " } + return p; +} + +struct S +{ + char a2_3_4[2][3][4]; + char a3_4_5[3][4][5]; +}; + +extern struct S sa[]; + +void nowarn_sa_cstidx_cstsize (const char* const s[]) +{ + strncpy (sa[0].a2_3_4[0][0], s[0], 4); + strncpy (sa[0].a2_3_4[0][1], s[1], 4); + strncpy (sa[0].a2_3_4[0][2], s[2], 4); + + strncpy (sa[0].a2_3_4[1][0], s[3], 4); + strncpy (sa[0].a2_3_4[1][1], s[4], 4); + strncpy (sa[0].a2_3_4[1][2], s[5], 4); + + strncpy (sa[1].a2_3_4[0][0], s[6], 4); + strncpy (sa[1].a2_3_4[0][1], s[7], 4); + strncpy (sa[1].a2_3_4[0][2], s[8], 4); + + strncpy (sa[1].a2_3_4[1][0], s[9], 4); + strncpy (sa[1].a2_3_4[1][1], s[10], 4); + strncpy (sa[1].a2_3_4[1][2], s[11], 4); +} + +void warn_sa_cstidx_cstsize (const char* const s[]) +{ + strncpy (sa[0].a2_3_4[0][0], s[0], 5); // { dg-warning "writing 5 bytes into a region of size 4 " } + strncpy (sa[0].a2_3_4[0][1], s[1], 6); // { dg-warning "writing 6 bytes into a region of size 4 " } + strncpy (sa[0].a2_3_4[0][2], s[2], 7); // { dg-warning "writing 7 bytes into a region of size 4 " } + + strncpy (sa[0].a2_3_4[1][0], s[3], 5); // { dg-warning "writing 5 bytes into a region of size 4 " } + strncpy (sa[0].a2_3_4[1][1], s[4], 6); // { dg-warning "writing 6 bytes into a region of size 4 " } + strncpy (sa[0].a2_3_4[1][2], s[5], 7); // { dg-warning "writing 7 bytes into a region of size 4 " } + + strncpy (sa[1].a2_3_4[0][0], s[6], 5); // { dg-warning "writing 5 bytes into a region of size 4 " } + strncpy (sa[1].a2_3_4[0][1], s[7], 6); // { dg-warning "writing 6 bytes into a region of size 4 " } + strncpy (sa[1].a2_3_4[0][2], s[8], 7); // { dg-warning "writing 7 bytes into a region of size 4 " } + + strncpy (sa[1].a2_3_4[1][0], s[9], 5); // { dg-warning "writing 5 bytes into a region of size 4 " } + strncpy (sa[1].a2_3_4[1][1], s[10], 6); // { dg-warning "writing 6 bytes into a region of size 4 " } + strncpy (sa[1].a2_3_4[1][2], s[11], 7); // { dg-warning "writing 7 bytes into a region of size 4 " } +} + +void nowarn_sa_cstidx_varsize (const char* const s[], unsigned n) +{ + strncpy (sa[0].a2_3_4[0][0], s[0], n); + strncpy (sa[0].a2_3_4[0][1], s[1], n); + strncpy (sa[0].a2_3_4[0][2], s[2], n); + + strncpy (sa[0].a2_3_4[1][0], s[3], n); + strncpy (sa[0].a2_3_4[1][1], s[4], n); + strncpy (sa[0].a2_3_4[1][2], s[5], n); + + strncpy (sa[1].a2_3_4[0][0], s[6], n); + strncpy (sa[1].a2_3_4[0][1], s[7], n); + strncpy (sa[1].a2_3_4[0][2], s[8], n); + + strncpy (sa[1].a2_3_4[1][0], s[9], n); + strncpy (sa[1].a2_3_4[1][1], s[10], n); + strncpy (sa[1].a2_3_4[1][2], s[11], n); +} + +void nowarn_sa_loop (const char* const s[], unsigned n) +{ + for (unsigned i0 = 0; i0 != 5; ++i0) + for (unsigned i1 = 0; i1 != 3; ++i1) + for (unsigned i2 = 0; i2 != 2; ++i2) + strncpy (sa[i0].a2_3_4[i1][i2], s[i2], n); +} + + +/* Verify that a note after the warning points to the accessed object + and mentions the starting offset of the access. Another alternative + might be for the offset to be the starting offset of the overflow. + As it is, it's not clear to which of the two the offset refers. */ + +void test_note (const char *s) +{ + extern void sink (void*); + + { + char a[1][1][2]; // { dg-message "destination object" } + strncpy (a[0][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " } + sink (a); + } + + { + char a[1][2][2]; // { dg-message "at offset 2 into " } + strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " } + sink (a); + } + + { + char a[1][2][2]; // { dg-message "at offset 4 into " } + strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " } + sink (a); + } + + { + char a[2][1][2]; // { dg-message "at offset 2 into " } + strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " } + sink (a); + } + + { + char a[2][2][3]; // { dg-message "at offset 9 into " } + strncpy (a[1][1], s, 4); // { dg-warning "writing 4 bytes into a region of size 3 " } + sink (a); + } + + { + char a[2][3][3]; // { dg-message "at offset 12 into " } + strncpy (a[1][1], s, 5); // { dg-warning "writing 5 bytes into a region of size 3 " } + sink (a); + } + + { + char a[2][3][3]; // { dg-message "at offset 12 into " } + strncpy (a[1][1], s, 6); // { dg-warning "writing 6 bytes into a region of size 3 " } + sink (a); + } + + { + char a[2][3][3]; // { dg-message "at offset 15 into " } + strncpy (a[1][2], s, 7); // { dg-warning "writing 7 bytes into a region of size 3 " } + sink (a); + } + +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-38.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-38.c new file mode 100644 index 00000000000..12bda2b31a7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-38.c @@ -0,0 +1,94 @@ +/* Verify that non-constant offsets don't suppress warnings in cases + when the size of the access alone makes it invalid, regardless of + the offset value. + Also verify that writing into unknown objects through pointers + (or pointer members) doesn't trigger warnings. + { dg-do compile } + { dg-options "-O1 -Wall -Wno-array-bounds" } */ + +typedef __SIZE_TYPE__ size_t; + +extern void* memcpy (void*, const void*, size_t); +extern void* memset (void*, int, size_t); + + +struct Xp { char *p; } xp; +struct Xa { char *a[2]; } xa; + +void nowarn_copy_read_pointed_obj_plus_cst (void *d) +{ + int i = 0; + memcpy (d, xp.p + i, 9); + memcpy (d, xa.a[i], 9); +} + +void nowarn_copy_read_pointed_obj_plus_var (void *d, int i) +{ + memcpy (d, xp.p + i, 9); + memcpy (d, xa.a[i], 9); +} + + +void warn_copy_read_pointer_plus_cst (void *d) +{ + int i = 0; + memcpy (d, &xp.p + i, 9); // { dg-warning "reading 9 bytes from a region of size . " } +} + +void warn_copy_read_pointer_plus_var (void *d, int i) +{ + memcpy (d, &xp.p + i, 9); // { dg-warning "reading 9 bytes from a region of size . " } +} + + +void nowarn_copy_write_pointed_obj_plus_cst (const void *s) +{ + int i = 0; + memcpy (xp.p + i, s, 9); + memcpy (xa.a[i], s, 9); +} + +void nowarn_copy_write_pointed_obj_plus_var (const void *s, int i) +{ + memcpy (xp.p + i, s, 9); + memcpy (xa.a[i], s, 9); +} + + +void warn_copy_write_pointer_plus_cst (const void *s) +{ + int i = 0; + memcpy (&xp.p + i, s, 9); // { dg-warning "writing 9 bytes into a region of size . " } +} + +void warn_copy_write_pointer_plus_var (const void *s, int i) +{ + memcpy (&xp.p + i, s, 9); // { dg-warning "writing 9 bytes into a region of size . " } +} + + +void nowarn_set_pointed_obj_plus_cst (void) +{ + int i = 0; + memset (xp.p + i, 0, 9); + memset (xa.a[i], 0, 9); +} + +void nowarn_set_pointed_obj_plus_var (int i) +{ + memset (xp.p + i, 0, 9); + memset (xa.a[i], 0, 9); +} + + +void warn_set_pointer_plus_cst (void) +{ + int i = 0; + memset (&xp.p + i, 0, 9); // { dg-warning "writing 9 bytes into a region of size . " } +} + +void warn_set_pointer_plus_var (int i) +{ + memset (&xp.p + i, 0, 9); // { dg-warning "writing 9 bytes into a region of size . " } + memset (&xa.a[i], 0, 17); // { dg-warning "writing 17 bytes into a region of size \[0-9\]+ " } +} diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c index 6e836c5d4d3..2409e597aac 100644 --- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c @@ -99,7 +99,7 @@ void test_memcpy_range (void *d, const void *s) memcpy (buf + 5, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" } */ - memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */ + memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" } */ memcpy (buf, s, UR (ssize_max, size_max)); /* { dg-warning "writing \[0-9\]+ or more bytes into a region of size 5 overflows the destination" } */ memcpy (buf, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified \(bound|size\) between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */ diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c index 87dd6ac4e89..5398e9f1f9c 100644 --- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c @@ -1,6 +1,6 @@ /* Test exercising -Wstringop-overflow warnings. */ /* { dg-do compile } */ -/* { dg-options "-O2 -Wstringop-overflow=1" } */ +/* { dg-options "-O2 -Wstringop-overflow=1 -Wno-array-bounds" } */ #define offsetof(type, mem) __builtin_offsetof (type, mem) @@ -120,7 +120,7 @@ void test_memop_warn_alloc (const void *src) /* Verify the same as above but by writing into the first mmeber of the first element of the array. */ - memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */ + memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size " "memcpy into allocated" } */ escape (a, src); n = range (12, 32); @@ -133,33 +133,33 @@ void test_memop_warn_alloc (const void *src) /* The following idiom of clearing multiple members of a struct is used in a few places in the Linux kernel. Verify that a warning is issued for it when it writes past the end of the array object. */ - memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */ escape (b); /* Same as above but clearing just elements of the second element of the array. */ - memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size " "memcpy into allocated" } */ escape (b); - memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */ + memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" } */ escape (b); } diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c index 741c1f88eaa..12d2491b9f5 100644 --- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c @@ -106,7 +106,7 @@ void test_memop_warn_alloc (void *p) memcpy (p, &a[0], n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */ - memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */ + memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size " "memcpy from allocated" } */ n = range (12, 32); diff --git a/gcc/testsuite/gcc.dg/strlenopt-74.c b/gcc/testsuite/gcc.dg/strlenopt-74.c index 0a9ac6c1da2..19e9a608583 100644 --- a/gcc/testsuite/gcc.dg/strlenopt-74.c +++ b/gcc/testsuite/gcc.dg/strlenopt-74.c @@ -75,7 +75,7 @@ VERIFY (i0 ? (a8 + 2) : (b8 + 2), 7, 6); VERIFY (i0 ? (a8 + 0) : (c4 + 0), 9, 4); VERIFY (i0 ? (a8 + 0) : (c4 + 1), 9, 3); VERIFY (i0 ? (a8 + 0) : (c4 + 3), 9, 1); -VERIFY (i0 ? (a8 + 0) : (c4 + 4), 9, 0); +VERIFY (i0 ? (a8 + 0) : (c4 + 4), 8, 0); VERIFY (i0 ? (a8 + 1) : (c4 + 0), 8, 4); VERIFY (i0 ? (a8 + 1) : (c4 + 1), 8, 3); VERIFY (i0 ? (a8 + 1) : (c4 + 2), 8, 2); diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c index 8855065f577..bc55b27cff0 100644 --- a/gcc/tree-object-size.c +++ b/gcc/tree-object-size.c @@ -172,7 +172,7 @@ compute_object_offset (const_tree expr, const_tree var) is true, else null. An object's initializer affects the object's size if it's a struct type with a flexible array member. */ -static tree +tree decl_init_size (tree decl, bool min) { tree size = DECL_SIZE_UNIT (decl); @@ -259,6 +259,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, offset_int mem_offset; if (mem_ref_offset (pt_var).is_constant (&mem_offset)) { + if (*poff) + *poff = wide_int_to_tree (ptrdiff_type_node, + mem_offset + wi::to_offset (*poff)); + else + *poff = wide_int_to_tree (ptrdiff_type_node, mem_offset); offset_int dsz = wi::sub (sz, mem_offset); if (wi::neg_p (dsz)) sz = 0; @@ -413,12 +418,12 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var); if (bytes != error_mark_node) { + *poff = bytes; if (TREE_CODE (bytes) == INTEGER_CST && tree_int_cst_lt (var_size, bytes)) bytes = size_zero_node; else bytes = size_binop (MINUS_EXPR, var_size, bytes); - *poff = bytes; } if (var != pt_var && pt_var_size @@ -441,7 +446,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, else if (!pt_var_size) return false; else - bytes = pt_var_size; + { + bytes = pt_var_size; + if (!*poff) + *poff = size_zero_node; + } if (tree_fits_uhwi_p (bytes)) { diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h index 871dcdba06a..e677c973ab9 100644 --- a/gcc/tree-object-size.h +++ b/gcc/tree-object-size.h @@ -24,5 +24,6 @@ extern void init_object_sizes (void); extern void fini_object_sizes (void); extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *, tree * = NULL, tree * = NULL); +extern tree decl_init_size (tree, bool); #endif // GCC_TREE_OBJECT_SIZE_H diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 93d095e1896..fbaee745f7d 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -5589,7 +5589,6 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh, set_strinfo (idx, si); si->writable = true; si->dont_invalidate = true; - maybe_warn_overflow (stmt, lenrange[2], rvals); } } }