diff options
author | Martin Sebor <msebor@redhat.com> | 2020-10-09 13:56:53 -0600 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2020-10-12 09:04:49 -0600 |
commit | de05c19d5fd661ae16dd75a895b49d32d12f5edc (patch) | |
tree | 05d9906c75f514094c4320b482f200787a34b9b6 /gcc/builtins.c | |
parent | 71dbabccbfb295c87d91719fe72d9d60511c0b44 (diff) | |
download | gcc-de05c19d5fd661ae16dd75a895b49d32d12f5edc.zip gcc-de05c19d5fd661ae16dd75a895b49d32d12f5edc.tar.gz gcc-de05c19d5fd661ae16dd75a895b49d32d12f5edc.tar.bz2 |
Correct handling of indices into arrays with elements larger than 1 (PR c++/96511)
Resolves:
PR c++/96511 - Incorrect -Wplacement-new on POINTER_PLUS into an array with 4-byte elements
PR middle-end/96384 - bogus -Wstringop-overflow= storing into multidimensional array with index in range
gcc/ChangeLog:
PR c++/96511
PR middle-end/96384
* builtins.c (get_range): Return full range of type when neither
value nor its range is available. Fail for ranges inverted due
to the signedness of offsets.
(compute_objsize): Handle more special array members. Handle
POINTER_PLUS_EXPR and VIEW_CONVERT_EXPR that come up in front end
code.
(access_ref::offset_bounded): Define new member function.
* builtins.h (access_ref::eval): New data member.
(access_ref::offset_bounded): New member function.
(access_ref::offset_zero): New member function.
(compute_objsize): Declare a new overload.
* gimple-array-bounds.cc (array_bounds_checker::check_array_ref): Use
enum special_array_member.
* tree.c (component_ref_size): Use special_array_member.
* tree.h (special_array_member): Define a new type.
(component_ref_size): Change signature.
gcc/cp/ChangeLog:
PR c++/96511
PR middle-end/96384
* init.c (warn_placement_new_too_small): Call builtin_objsize instead
of duplicating what it does.
gcc/testsuite/ChangeLog:
PR c++/96511
PR middle-end/96384
* g++.dg/init/strlen.C: Add expected warning.
* g++.dg/warn/Wplacement-new-size-1.C: Relax warnings.
* g++.dg/warn/Wplacement-new-size-2.C: Same.
* g++.dg/warn/Wplacement-new-size-6.C: Same.
* gcc.dg/Warray-bounds-58.c: Adjust
* gcc.dg/Wstringop-overflow-37.c: Same.
* g++.dg/warn/Wplacement-new-size-7.C: New test.
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 135 |
1 files changed, 108 insertions, 27 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index 284926f..283c1e6 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -200,7 +200,7 @@ static void expand_builtin_sync_synchronize (void); access_ref::access_ref (tree bound /* = NULL_TREE */, bool minaccess /* = false */) - : ref () +: ref (), eval ([](tree x){ return x; }), trail1special (true) { /* Set to valid. */ offrng[0] = offrng[1] = 0; @@ -4370,10 +4370,34 @@ static bool get_range (tree x, gimple *stmt, signop sgn, offset_int r[2], range_query *rvals /* = NULL */) { + tree type = TREE_TYPE (x); + if (TREE_CODE (x) != INTEGER_CST + && TREE_CODE (x) != SSA_NAME) + { + if (TYPE_UNSIGNED (type)) + { + if (sgn == SIGNED) + type = signed_type_for (type); + } + else if (sgn == UNSIGNED) + type = unsigned_type_for (type); + + r[0] = wi::to_offset (TYPE_MIN_VALUE (type)); + r[1] = wi::to_offset (TYPE_MAX_VALUE (type)); + return x; + } + wide_int wr[2]; if (!get_range (x, stmt, wr, rvals)) return false; + /* Only convert signed integers or unsigned sizetype to a signed + offset and avoid converting large positive values in narrower + types to negative offsets. */ + if (TYPE_UNSIGNED (type) + && wr[0].get_precision () < TYPE_PRECISION (sizetype)) + sgn = UNSIGNED; + r[0] = offset_int::from (wr[0], sgn); r[1] = offset_int::from (wr[1], sgn); return true; @@ -4394,9 +4418,11 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2], to influence code generation or optimization. */ static bool -compute_objsize (tree ptr, int ostype, access_ref *pref, - bitmap *visited, range_query *rvals /* = NULL */) +compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, + range_query *rvals) { + STRIP_NOPS (ptr); + const bool addr = TREE_CODE (ptr) == ADDR_EXPR; if (addr) ptr = TREE_OPERAND (ptr, 0); @@ -4408,12 +4434,15 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, 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; - pref->ref = ptr; - pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + if (tree size = decl_init_size (ptr, false)) + if (TREE_CODE (size) == INTEGER_CST) + { + pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + return true; + } + pref->sizrng[0] = 0; + pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); return true; } @@ -4421,13 +4450,13 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, if (code == COMPONENT_REF) { + tree ref = TREE_OPERAND (ptr, 0); tree field = TREE_OPERAND (ptr, 1); if (ostype == 0) { /* For raw memory functions like memcpy bail if the size of the enclosing object cannot be determined. */ - tree ref = TREE_OPERAND (ptr, 0); if (!compute_objsize (ref, ostype, pref, visited, rvals) || !pref->ref) return false; @@ -4449,20 +4478,28 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, return false; 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) + + /* SAM is set for array members that might need special treatment. */ + special_array_member sam; + tree size = component_ref_size (ptr, &sam); + if (sam == special_array_member::int_0) + pref->sizrng[0] = pref->sizrng[1] = 0; + else if (!pref->trail1special && sam == special_array_member::trail_1) + pref->sizrng[0] = pref->sizrng[1] = 1; + else if (size && TREE_CODE (size) == INTEGER_CST) + pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + else { - pref->sizrng[0] = pref->sizrng[1] = 0; - return true; + /* When the size of the member is unknown it's either a flexible + array member or a trailing special array member (either zero + length or one-element). Set the size to the maximum minus + the constant size of the type. */ + pref->sizrng[0] = 0; + pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); + if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref))) + if (TREE_CODE (recsize) == INTEGER_CST) + pref->sizrng[1] -= wi::to_offset (recsize); } - - if (!size || TREE_CODE (size) != INTEGER_CST) - return false; - - pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); return true; } @@ -4492,7 +4529,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, return false; offset_int orng[2]; - tree off = TREE_OPERAND (ptr, 1); + tree off = pref->eval (TREE_OPERAND (ptr, 1)); if (!get_range (off, NULL, SIGNED, orng, rvals)) /* Fail unless the size of the object is zero. */ return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1]; @@ -4522,11 +4559,22 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, if (ostype && TREE_CODE (eltype) == ARRAY_TYPE) { - /* Execpt for the permissive raw memory functions which - use the size of the whole object determined above, - use the size of the referenced array. */ - pref->sizrng[0] = pref->offrng[0] + orng[0] + sz; - pref->sizrng[1] = pref->offrng[1] + orng[1] + sz; + /* Except for the permissive raw memory functions which use + the size of the whole object determined above, use the size + of the referenced array. Because the overall offset is from + the beginning of the complete array object add this overall + offset to the size of array. */ + offset_int sizrng[2] = + { + pref->offrng[0] + orng[0] + sz, + pref->offrng[1] + orng[1] + sz + }; + if (sizrng[1] < sizrng[0]) + std::swap (sizrng[0], sizrng[1]); + if (sizrng[0] >= 0 && sizrng[0] <= pref->sizrng[0]) + pref->sizrng[0] = sizrng[0]; + if (sizrng[1] >= 0 && sizrng[1] <= pref->sizrng[1]) + pref->sizrng[1] = sizrng[1]; } } @@ -4535,6 +4583,28 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, return true; } + else if (code == POINTER_PLUS_EXPR) + { + tree ref = TREE_OPERAND (ptr, 0); + if (!compute_objsize (ref, ostype, pref, visited, rvals)) + return false; + + offset_int orng[2]; + tree off = pref->eval (TREE_OPERAND (ptr, 1)); + if (!get_range (off, NULL, SIGNED, orng, rvals)) + /* Fail unless the size of the object is zero. */ + return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1]; + + pref->offrng[0] += orng[0]; + pref->offrng[1] += orng[1]; + + return true; + } + else if (code == VIEW_CONVERT_EXPR) + { + ptr = TREE_OPERAND (ptr, 0); + return compute_objsize (ptr, ostype, pref, visited, rvals); + } if (TREE_CODE (ptr) == SSA_NAME) { @@ -12504,3 +12574,14 @@ builtin_with_linkage_p (tree decl) } return false; } + +/* Return true if OFFRNG is bounded to a subrange of offset values + valid for the largest possible object. */ + +bool +access_ref::offset_bounded () const +{ + tree min = TYPE_MIN_VALUE (ptrdiff_type_node); + tree max = TYPE_MAX_VALUE (ptrdiff_type_node); + return wi::to_offset (min) <= offrng[0] && offrng[1] <= wi::to_offset (max); +} |