aboutsummaryrefslogtreecommitdiff
path: root/gcc/builtins.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2019-12-05 01:28:11 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2019-12-04 18:28:11 -0700
commit268209f3a0dc07fcf13534610447ab732742eb2f (patch)
tree19c70c9ade74615720a40b206b35dcfc02c63f53 /gcc/builtins.c
parenta59c50bd14f5bcb467a21ceb55f6b082510efc2f (diff)
downloadgcc-268209f3a0dc07fcf13534610447ab732742eb2f.zip
gcc-268209f3a0dc07fcf13534610447ab732742eb2f.tar.gz
gcc-268209f3a0dc07fcf13534610447ab732742eb2f.tar.bz2
PR middle-end/91582 - missing heap overflow detection for strcpy
gcc/ChangeLog: PR middle-end/91582 * builtins.c (gimple_call_alloc_size): New function. (compute_objsize): Add argument. Call gimple_call_alloc_size. Handle variable offsets and indices. * builtins.h (gimple_call_alloc_size): Declare. (compute_objsize): Add argument. * gcc/gimple-ssa-warn-restrict.c: Remove assertions. * tree-ssa-strlen.c (handle_store): Handle calls to allocated objects. gcc/testsuite/ChangeLog: PR middle-end/91582 * c-c++-common/Wstringop-truncation.c: Remove xfails. * g++.dg/warn/Wstringop-overflow-4.C: New test. * g++.dg/ext/attr-alloc_size.C: Suppress -Warray-bounds. * gcc.dg/Warray-bounds-56.c: New test. * gcc.dg/Wstringop-overflow-22.c: New test. * gcc.dg/attr-alloc_size.c: Suppress -Warray-bounds. * gcc.dg/attr-copy-2.c: Same. * gcc.dg/builtin-stringop-chk-5.c: Remove xfails. * gcc.dg/builtin-stringop-chk-8.c: Same. Correct the text of expected warnings. * gcc.target/i386/pr82002-2a.c: Prune expected warning. * gcc.target/i386/pr82002-2b.c: Same. From-SVN: r278983
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r--gcc/builtins.c230
1 files changed, 210 insertions, 20 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 36319a9..53de17c 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3696,6 +3696,97 @@ check_access (tree exp, tree, tree, tree dstwrite,
return true;
}
+/* If STMT is a call to an allocation function, returns the size
+ of the object allocated by the call. */
+
+tree
+gimple_call_alloc_size (gimple *stmt)
+{
+ if (!stmt)
+ return NULL_TREE;
+
+ tree allocfntype;
+ if (tree fndecl = gimple_call_fndecl (stmt))
+ allocfntype = TREE_TYPE (fndecl);
+ else
+ allocfntype = gimple_call_fntype (stmt);
+
+ if (!allocfntype)
+ return NULL_TREE;
+
+ unsigned argidx1 = UINT_MAX, argidx2 = UINT_MAX;
+ tree at = lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (allocfntype));
+ if (!at)
+ {
+ if (!gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+ return NULL_TREE;
+
+ argidx1 = 0;
+ }
+
+ unsigned nargs = gimple_call_num_args (stmt);
+
+ if (argidx1 == UINT_MAX)
+ {
+ tree atval = TREE_VALUE (at);
+ if (!atval)
+ return NULL_TREE;
+
+ argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
+ if (nargs <= argidx1)
+ return NULL_TREE;
+
+ atval = TREE_CHAIN (atval);
+ if (atval)
+ {
+ argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
+ if (nargs <= argidx2)
+ return NULL_TREE;
+ }
+ }
+
+ tree size = gimple_call_arg (stmt, argidx1);
+
+ wide_int rng1[2];
+ if (TREE_CODE (size) == INTEGER_CST)
+ rng1[0] = rng1[1] = wi::to_wide (size);
+ else if (TREE_CODE (size) != SSA_NAME
+ || get_range_info (size, rng1, rng1 + 1) != VR_RANGE)
+ return NULL_TREE;
+
+ if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
+ return size;
+
+ /* To handle ranges do the math in wide_int and return the product
+ of the upper bounds as a constant. Ignore anti-ranges. */
+ tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
+ wide_int rng2[2];
+ if (TREE_CODE (n) == INTEGER_CST)
+ rng2[0] = rng2[1] = wi::to_wide (n);
+ else if (TREE_CODE (n) != SSA_NAME
+ || get_range_info (n, rng2, rng2 + 1) != VR_RANGE)
+ return NULL_TREE;
+
+ /* Extend to the maximum precsion to avoid overflow. */
+ const int prec = ADDR_MAX_PRECISION;
+ rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
+ rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
+ rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
+ rng2[1] = wide_int::from (rng2[1], prec, UNSIGNED);
+
+ /* Return the lesser of SIZE_MAX and the product of the upper bounds. */
+ rng1[0] = rng1[0] * rng2[0];
+ rng1[1] = rng1[1] * rng2[1];
+ tree size_max = TYPE_MAX_VALUE (sizetype);
+ if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
+ {
+ rng1[1] = wi::to_wide (size_max);
+ return size_max;
+ }
+
+ return wide_int_to_tree (sizetype, rng1[1]);
+}
+
/* 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
@@ -3704,16 +3795,22 @@ check_access (tree exp, tree, tree, tree dstwrite,
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, set *PDECL to the decl of the referenced
- subobject if it can be determined, or to null otherwise.
+ subobject if it can be determined, or to null otherwise. Likewise,
+ when POFF is nonnull *POFF is set to the offset into *PDECL.
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 */)
+compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
+ tree *poff /* = NULL */)
{
- tree dummy = NULL_TREE;
+ tree dummy_decl = NULL_TREE;
if (!pdecl)
- pdecl = &dummy;
+ pdecl = &dummy_decl;
+
+ tree dummy_off = size_zero_node;
+ if (!poff)
+ poff = &dummy_off;
unsigned HOST_WIDE_INT size;
@@ -3726,6 +3823,13 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
if (TREE_CODE (dest) == SSA_NAME)
{
gimple *stmt = SSA_NAME_DEF_STMT (dest);
+ if (is_gimple_call (stmt))
+ {
+ /* If STMT is a call to an allocation function get the size
+ from its argument(s). */
+ return gimple_call_alloc_size (stmt);
+ }
+
if (!is_gimple_assign (stmt))
return NULL_TREE;
@@ -3741,7 +3845,7 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
tree off = gimple_assign_rhs2 (stmt);
if (TREE_CODE (off) == INTEGER_CST)
{
- if (tree size = compute_objsize (dest, ostype, pdecl))
+ if (tree size = compute_objsize (dest, ostype, pdecl, poff))
{
wide_int wioff = wi::to_wide (off);
wide_int wisiz = wi::to_wide (size);
@@ -3752,10 +3856,16 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
if (wi::sign_mask (wioff))
;
else if (wi::ltu_p (wioff, wisiz))
- return wide_int_to_tree (TREE_TYPE (size),
- wi::sub (wisiz, wioff));
+ {
+ *poff = size_binop (PLUS_EXPR, *poff, off);
+ return wide_int_to_tree (TREE_TYPE (size),
+ wi::sub (wisiz, wioff));
+ }
else
- return size_zero_node;
+ {
+ *poff = size_binop (PLUS_EXPR, *poff, off);
+ return size_zero_node;
+ }
}
}
else if (TREE_CODE (off) == SSA_NAME
@@ -3777,10 +3887,18 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
|| wi::sign_mask (max))
;
else if (wi::ltu_p (min, wisiz))
- return wide_int_to_tree (TREE_TYPE (size),
- wi::sub (wisiz, min));
+ {
+ *poff = size_binop (PLUS_EXPR, *poff,
+ wide_int_to_tree (sizetype, min));
+ return wide_int_to_tree (TREE_TYPE (size),
+ wi::sub (wisiz, min));
+ }
else
- return size_zero_node;
+ {
+ *poff = size_binop (PLUS_EXPR, *poff,
+ wide_int_to_tree (sizetype, min));
+ return size_zero_node;
+ }
}
}
}
@@ -3799,19 +3917,24 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
{
tree ref = TREE_OPERAND (dest, 0);
tree off = TREE_OPERAND (dest, 1);
- if (tree size = compute_objsize (ref, ostype, pdecl))
+ 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))
+ if (integer_zerop (size)
+ && *pdecl && DECL_P (*pdecl)
+ && *poff && integer_zerop (*poff))
return integer_zero_node;
- if (TREE_CODE (off) != INTEGER_CST
- || TREE_CODE (size) != INTEGER_CST)
- return NULL_TREE;
+ /* A valid offset into a declared object cannot be negative. */
+ if (tree_int_cst_sgn (*poff) < 0)
+ return size_zero_node;
+ /* Adjust SIZE either up or down by the sum of *POFF and OFF
+ above. */
if (TREE_CODE (dest) == ARRAY_REF)
{
+ /* 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)
@@ -3820,9 +3943,74 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
return NULL_TREE;
}
- if (tree_int_cst_lt (off, size))
- return fold_build2 (MINUS_EXPR, size_type_node, size, off);
- return integer_zero_node;
+ wide_int offrng[2];
+ if (TREE_CODE (off) == INTEGER_CST)
+ offrng[0] = offrng[1] = wi::to_wide (off);
+ else if (TREE_CODE (off) == SSA_NAME)
+ {
+ wide_int min, max;
+ enum value_range_kind rng
+ = get_range_info (off, offrng, offrng + 1);
+ if (rng != VR_RANGE)
+ return NULL_TREE;
+ }
+ else
+ return NULL_TREE;
+
+ /* Convert to the same precision to keep wide_int from "helpfuly"
+ crashing whenever it sees other argumments. */
+ offrng[0] = wide_int::from (offrng[0], ADDR_MAX_BITSIZE, SIGNED);
+ offrng[1] = wide_int::from (offrng[1], ADDR_MAX_BITSIZE, SIGNED);
+
+ tree dstoff = *poff;
+ if (integer_zerop (*poff))
+ *poff = off;
+ else if (!integer_zerop (off))
+ {
+ *poff = fold_convert (ptrdiff_type_node, *poff);
+ off = fold_convert (ptrdiff_type_node, off);
+ *poff = size_binop (PLUS_EXPR, *poff, off);
+ }
+
+ if (wi::sign_mask (offrng[0]) >= 0)
+ {
+ if (TREE_CODE (size) != INTEGER_CST)
+ return NULL_TREE;
+
+ /* Return the difference between the size and the offset
+ or zero if the offset is greater. */
+ wide_int wisize = wi::to_wide (size, ADDR_MAX_BITSIZE);
+ if (wi::ltu_p (wisize, offrng[0]))
+ return size_zero_node;
+
+ return wide_int_to_tree (sizetype, wisize - offrng[0]);
+ }
+
+ 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)
+ {
+ enum value_range_kind rng
+ = get_range_info (dstoff, dstoffrng, dstoffrng + 1);
+ if (rng != VR_RANGE)
+ return NULL_TREE;
+ }
+ else
+ return NULL_TREE;
+
+ dstoffrng[0] = wide_int::from (dstoffrng[0], ADDR_MAX_BITSIZE, SIGNED);
+ dstoffrng[1] = wide_int::from (dstoffrng[1], ADDR_MAX_BITSIZE, SIGNED);
+
+ wide_int declsize = wi::to_wide (size);
+ if (wi::sign_mask (dstoffrng[0]) > 0)
+ declsize += dstoffrng[0];
+
+ offrng[1] += dstoffrng[1];
+ if (wi::sign_mask (offrng[1]) < 0)
+ return size_zero_node;
+
+ return wide_int_to_tree (sizetype, declsize);
}
return NULL_TREE;
@@ -3850,9 +4038,11 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
+ if (TREE_CODE (dest) == ADDR_EXPR)
+ dest = TREE_OPERAND (dest, 0);
if (TREE_CODE (type) == ARRAY_TYPE
- && !array_at_struct_end_p (ref))
+ && !array_at_struct_end_p (dest))
{
if (tree size = TYPE_SIZE_UNIT (type))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;