+2019-12-03 Martin Sebor <msebor@redhat.com>
+
+ 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.
+
2019-12-04 Julian Brown <julian@codesourcery.com>
* config/gcn/gcn.h (FIXED_REGISTERS): Make s6/s7 fixed registers.
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
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;
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;
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);
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
|| 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;
+ }
}
}
}
{
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)
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;
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;
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
extern bool is_simple_builtin (tree);
extern bool is_inexpensive_builtin (tree);
-extern tree compute_objsize (tree, int, tree * = NULL);
+extern tree gimple_call_alloc_size (gimple *);
+extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL);
extern bool readonly_data_expr (tree exp);
extern bool init_target_chars (void);
const offset_int maxobjsize = acs.dstref->maxobjsize;
offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
- gcc_assert (maxsize <= maxobjsize);
/* Adjust the larger bounds of the offsets (which may be the first
element if the lower bound is larger than the upper bound) to
acs.dstsiz[1] = 1;
offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
- gcc_assert (maxsize <= maxobjsize);
/* For references to the same base object, determine if there's a pair
of valid offsets into the two references such that access between
+2019-12-03 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/91582
+ * c-c++-common/Wstringop-truncation.c: Remove xfails.
+ * g++.dg/warn/Wstringop-overflow-4.C: New test.
+ * gcc/testsuite/g++.dg/ext/attr-alloc_size.C: Suppress -Warray-bounds.
+ * gcc.dg/Wstringop-overflow-25.c: New test.
+ * gcc/testsuite/gcc.dg/attr-alloc_size.c: Suppress -Warray-bounds.
+ * gcc/testsuite/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.
+
2019-12-04 Joseph Myers <joseph@codesourcery.com>
PR c/36941
size_t n = 7;
char *d = (char *)__builtin_malloc (n);
- CPY (d, s, n); /* { dg-warning "specified bound 7 equals destination size" "bug 79016" { xfail *-*-* } } */
+ CPY (d, s, n); /* { dg-warning "specified bound 7 equals destination size" } */
Dest *pd = (Dest *)__builtin_malloc (sizeof *pd * n);
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */
/* PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
{ dg-do compile }
- { dg-options "-O2 -Wall" } */
+ { dg-options "-O2 -Wall -Wno-array-bounds" } */
#define ALLOC_SIZE(N) __attribute__ ((alloc_size (N)))
--- /dev/null
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "../../gcc.dg/range.h"
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+extern "C" char* strcpy (char*, const char*);
+
+void sink (void*);
+
+#define S36 "0123456789abcdefghijklmnopqrstuvwxyz"
+#define S(N) (S36 + sizeof S36 - N - 1)
+
+#define T(src, alloc) do { \
+ const char *s = src; \
+ char *d = (char*)alloc; \
+ strcpy (d, s); \
+ sink (d); \
+ } while (0)
+
+
+void test_strcpy_new_char (size_t n)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (S (0), new char[r_0_1]);
+ T (S (1), new char[r_0_1]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), new char[r_1_2]);
+ T (S (1), new char[r_1_2]);
+ T (S (2), new char[r_1_2]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), new char[r_2_3]);
+ T (S (2), new char[r_2_3]);
+ T (S (3), new char[r_2_3]); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), new char[r_2_3]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ size_t r_2_smax = UR (2, SIZE_MAX);
+ T (S (0), new char[r_2_smax]);
+ T (S (1), new char[r_2_smax]);
+ T (S (2), new char[r_2_smax]);
+ T (S (3), new char[r_2_smax * 2]);
+ T (S (4), new char[r_2_smax * 2 + 1]);
+
+ T (S (1), new char[n]);
+ T (S (2), new char[n + 1]);
+ T (S (9), new char[n * 2 + 1]);
+
+ int r_imin_imax = SR (INT_MIN, INT_MAX);
+ T (S (1), new char[r_imin_imax]);
+ T (S (2), new char[r_imin_imax + 1]);
+ T (S (9), new char[r_imin_imax * 2 + 1]);
+
+ int r_0_imax = SR (0, INT_MAX);
+ T (S (1), new char[r_0_imax]);
+ T (S (2), new char[r_0_imax + 1]);
+ T (S (9), new char[r_0_imax * 2 + 1]);
+
+ int r_1_imax = SR (1, INT_MAX);
+ T (S (1), new char[r_1_imax]);
+ T (S (2), new char[r_1_imax + 1]);
+ T (S (9), new char[r_1_imax * 2 + 1]);
+
+ ptrdiff_t r_dmin_dmax = SR (DIFF_MIN, DIFF_MAX);
+ T (S (1), new char[r_dmin_dmax]);
+ T (S (2), new char[r_dmin_dmax + 1]);
+ T (S (9), new char[r_dmin_dmax * 2 + 1]);
+}
+
+
+void test_strcpy_new_char_array (size_t n)
+{
+ size_t r_0_1 = UR (0, 1);
+
+ T (S (0), new char[r_0_1][1]);
+ T (S (1), new char[r_0_1][1]); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (1), new char[r_0_1][2]);
+ T (S (2), new char[r_0_1][2]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ size_t r_1_2 = UR (1, 2);
+ T (S (0), new char[r_1_2][0]); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (0), new char[r_1_2][1]);
+ T (S (1), new char[r_1_2][1]);
+ T (S (2), new char[r_1_2][1]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), new char[r_1_2][0]); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (0), new char[r_1_2][1]);
+ T (S (1), new char[r_1_2][2]);
+ T (S (3), new char[r_1_2][2]);
+ T (S (4), new char[r_1_2][2]); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+#ifdef __INT16_TYPE__
+
+typedef __INT16_TYPE__ int16_t;
+
+void test_strcpy_new_int16_t (size_t n)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (S (0), new int16_t[r_0_1]);
+ T (S (1), new int16_t[r_0_1]);
+ T (S (2), new int16_t[r_0_1]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), new int16_t[r_1_2]);
+ T (S (1), new int16_t[r_1_2]);
+ T (S (2), new int16_t[r_1_2]);
+ T (S (3), new int16_t[r_1_2]);
+ T (S (4), new int16_t[r_1_2]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), new int16_t[r_2_3]);
+ T (S (1), new int16_t[r_2_3]);
+ T (S (5), new int16_t[r_2_3]);
+ T (S (6), new int16_t[r_2_3]); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), new int16_t[r_2_3]); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ size_t r_2_smax = UR (2, SIZE_MAX);
+ T (S (0), new int16_t[r_2_smax]);
+ T (S (1), new int16_t[r_2_smax]);
+ T (S (2), new int16_t[r_2_smax]);
+ T (S (3), new int16_t[r_2_smax * 2]);
+ T (S (4), new int16_t[r_2_smax * 2 + 1]);
+
+ T (S (1), new int16_t[n]);
+ T (S (2), new int16_t[n + 1]);
+ T (S (9), new int16_t[n * 2 + 1]);
+
+ int r_imin_imax = SR (INT_MIN, INT_MAX);
+ T (S (1), new int16_t[r_imin_imax]);
+ T (S (2), new int16_t[r_imin_imax + 1]);
+ T (S (9), new int16_t[r_imin_imax * 2 + 1]);
+
+ int r_0_imax = SR (0, INT_MAX);
+ T (S (1), new int16_t[r_0_imax]);
+ T (S (2), new int16_t[r_0_imax + 1]);
+ T (S (9), new int16_t[r_0_imax * 2 + 1]);
+
+ int r_1_imax = SR (1, INT_MAX);
+ T (S (1), new int16_t[r_1_imax]);
+ T (S (2), new int16_t[r_1_imax + 1]);
+ T (S (9), new int16_t[r_1_imax * 2 + 1]);
+
+ ptrdiff_t r_dmin_dmax = SR (DIFF_MIN, DIFF_MAX);
+ T (S (1), new int16_t[r_dmin_dmax]);
+ T (S (2), new int16_t[r_dmin_dmax + 1]);
+ T (S (9), new int16_t[r_dmin_dmax * 2 + 1]);
+}
+
+#endif // int16_t
--- /dev/null
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+
+ The -Warray-bounds instances here probably should be replaced by
+ -Wstringop-overflow when it detects these overflows (see also
+ the xfails in Wstringop-overflow-25.c).
+
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+#define NOIPA ATTR (noipa)
+
+extern void* malloc (size_t);
+extern char* strcpy (char*, const char*);
+
+void sink (void*);
+
+#define S36 "0123456789abcdefghijklmnopqrstuvwxyz"
+#define S(N) (S36 + sizeof S36 - N - 1)
+
+struct Flex
+{
+ char n, ax[];
+};
+
+extern struct Flex fx;
+struct Flex f1 = { 1, { 1 } };
+struct Flex f2 = { 2, { 1, 2 } };
+struct Flex f3 = { 3, { 1, 2, 3 } };
+
+#define T(src, f) do { \
+ char *s = src; \
+ char *d = f.ax; \
+ strcpy (d, s); \
+ sink (&f); \
+ } while (0)
+
+NOIPA void test_strcpy_flexarray (void)
+{
+ T (S (0), fx); // { dg-bogus "\\\[-Warray-bounds" "pr92815" { xfail *-*-*} }
+ T (S (9), fx); // { dg-bogus "\\\[-Warray-bounds" "pr92815" { xfail *-*-*} }
+
+ T (S (0), f1);
+ T (S (1), f1); // { dg-warning "\\\[-Warray-bounds" }
+
+ T (S (0), f2);
+ T (S (1), f2);
+ T (S (2), f2); // { dg-warning "\\\[-Warray-bounds" }
+
+ T (S (0), f3);
+ T (S (2), f3);
+ T (S (3), f3); // { dg-warning "\\\[-Warray-bounds" }
+ T (S (9), f3); // { dg-warning "\\\[-Warray-bounds" }
+}
+
+#undef T
+#define T(T, src, n) do { \
+ char *s = src; \
+ typedef struct { T n, ax[]; } Flex; \
+ Flex *p = (Flex*)malloc (sizeof *p + n); \
+ char *d = (char*)p->ax; \
+ strcpy (d, s); \
+ sink (p); \
+ } while (0)
+
+NOIPA void test_strcpy_malloc_flexarray (void)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (char, S (0), r_0_1);
+ T (char, S (1), r_0_1); // { dg-warning "\\\[-Warray-bounds" }
+
+ T (char, S (0), r_1_2);
+ T (char, S (1), r_1_2);
+ T (char, S (2), r_1_2); // { dg-warning "\\\[-Warray-bounds" }
+
+ T (char, S (0), r_2_3);
+ T (char, S (2), r_2_3);
+ T (char, S (3), r_2_3); // { dg-warning "\\\[-Warray-bounds" }
+ T (char, S (9), r_2_3); // { dg-warning "\\\[-Warray-bounds" }
+}
--- /dev/null
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+#define NOIPA ATTR (noipa)
+
+extern void* alloca (size_t);
+extern void* calloc (size_t, size_t);
+extern void* malloc (size_t);
+
+extern ATTR (alloc_size (1), malloc) void*
+ alloc1 (size_t, int);
+extern ATTR (alloc_size (2), malloc) void*
+ alloc2 (int, size_t);
+extern ATTR (alloc_size (2, 4), malloc) void*
+ alloc2_4 (int, size_t, int, size_t);
+
+extern char* strcpy (char*, const char*);
+
+void sink (void*);
+
+#define S36 "0123456789abcdefghijklmnopqrstuvwxyz"
+#define S(N) (S36 + sizeof S36 - N - 1)
+
+#define T(src, alloc) do { \
+ char *s = src; \
+ char *d = alloc; \
+ strcpy (d, s); \
+ sink (d); \
+ } while (0)
+
+
+NOIPA void test_strcpy_alloca (size_t n)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (S (0), alloca (r_0_1));
+ T (S (1), alloca (r_0_1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloca (r_1_2));
+ T (S (1), alloca (r_1_2));
+ T (S (2), alloca (r_1_2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloca (r_2_3));
+ T (S (2), alloca (r_2_3));
+ T (S (3), alloca (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), alloca (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ size_t r_2_smax = UR (2, SIZE_MAX);
+ T (S (0), alloca (r_2_smax));
+ T (S (1), alloca (r_2_smax));
+ T (S (2), alloca (r_2_smax));
+ T (S (3), alloca (r_2_smax * 2));
+ T (S (4), alloca (r_2_smax * 2 + 1));
+
+ T (S (1), alloca (n));
+ T (S (2), alloca (n + 1));
+ T (S (9), alloca (n * 2 + 1));
+
+ int r_imin_imax = SR (INT_MIN, INT_MAX);
+ T (S (1), alloca (r_imin_imax));
+ T (S (2), alloca (r_imin_imax + 1));
+ T (S (9), alloca (r_imin_imax * 2 + 1));
+
+ int r_0_imax = SR (0, INT_MAX);
+ T (S (1), alloca (r_0_imax));
+ T (S (2), alloca (r_0_imax + 1));
+ T (S (9), alloca (r_0_imax * 2 + 1));
+
+ int r_1_imax = SR (1, INT_MAX);
+ T (S (1), alloca (r_1_imax));
+ T (S (2), alloca (r_1_imax + 1));
+ T (S (9), alloca (r_1_imax * 2 + 1));
+
+ ptrdiff_t r_dmin_dmax = SR (DIFF_MIN, DIFF_MAX);
+ T (S (1), alloca (r_dmin_dmax));
+ T (S (2), alloca (r_dmin_dmax + 1));
+ T (S (9), alloca (r_dmin_dmax * 2 + 1));
+}
+
+NOIPA void test_strcpy_calloc (void)
+{
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (S (0), calloc (r_1_2, 1));
+ T (S (1), calloc (r_1_2, 1));
+ T (S (2), calloc (r_1_2, 1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (2), calloc (r_2_3, 1));
+ T (S (3), calloc (r_2_3, 1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), calloc (1, r_1_2));
+ T (S (1), calloc (1, r_1_2));
+ T (S (2), calloc (1, r_1_2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (2), calloc (1, r_2_3));
+ T (S (3), calloc (1, r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), calloc (r_1_2, 2));
+ T (S (1), calloc (r_1_2, 2));
+ T (S (2), calloc (r_1_2, 2));
+ T (S (3), calloc (r_1_2, 2));
+ T (S (4), calloc (r_1_2, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), calloc (r_2_3, 2));
+ T (S (1), calloc (r_2_3, 2));
+ T (S (2), calloc (r_2_3, 2));
+ T (S (5), calloc (r_2_3, 2));
+ T (S (6), calloc (r_2_3, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), calloc (r_1_2, 2));
+ T (S (1), calloc (r_1_2, 2));
+ T (S (2), calloc (r_1_2, 2));
+ T (S (3), calloc (r_1_2, 2));
+ T (S (4), calloc (r_1_2, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), calloc (r_2_3, 2));
+ T (S (1), calloc (r_2_3, 2));
+ T (S (2), calloc (r_2_3, 2));
+ T (S (5), calloc (r_2_3, 2));
+ T (S (6), calloc (r_2_3, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), calloc (r_1_2, r_2_3));
+ T (S (1), calloc (r_1_2, r_2_3));
+ T (S (2), calloc (r_1_2, r_2_3));
+ T (S (3), calloc (r_1_2, r_2_3));
+ T (S (4), calloc (r_1_2, r_2_3));
+ T (S (5), calloc (r_1_2, r_2_3));
+ T (S (6), calloc (r_1_2, r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), calloc (r_1_2, r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ size_t r_2_dmax = UR (2, DIFF_MAX);
+ T (S (0), calloc (0, r_2_dmax)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (0), calloc (1, r_2_dmax));
+ T (S (9), calloc (2, r_2_dmax));
+
+ T (S (0), calloc (r_2_dmax, r_2_dmax));
+ T (S (9), calloc (r_2_dmax, r_2_dmax));
+
+ size_t r_2_smax = UR (2, SIZE_MAX);
+ T (S (0), calloc (r_2_smax, 1));
+ T (S (9), calloc (r_2_smax, 2));
+
+ T (S (0), calloc (r_2_smax, r_2_smax));
+ T (S (9), calloc (r_2_smax, r_2_smax));
+}
+
+
+NOIPA void test_strcpy_malloc (void)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (S (0), malloc (r_0_1));
+ T (S (1), malloc (r_0_1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), malloc (r_1_2));
+ T (S (1), malloc (r_1_2));
+ T (S (2), malloc (r_1_2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), malloc (r_2_3));
+ T (S (2), malloc (r_2_3));
+ T (S (3), malloc (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), malloc (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+NOIPA void test_strcpy_alloc1 (void)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+#define alloc1(n) alloc1 (n, 1)
+
+ T (S (0), alloc1 (r_0_1));
+ T (S (1), alloc1 (r_0_1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc1 (r_1_2));
+ T (S (1), alloc1 (r_1_2));
+ T (S (2), alloc1 (r_1_2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc1 (r_2_3));
+ T (S (2), alloc1 (r_2_3));
+ T (S (3), alloc1 (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), alloc1 (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void test_strcpy_alloc2 (void)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+#define alloc2(n) alloc2 (1, n)
+
+ T (S (0), alloc1 (r_0_1));
+ T (S (1), alloc1 (r_0_1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc1 (r_1_2));
+ T (S (1), alloc1 (r_1_2));
+ T (S (2), alloc1 (r_1_2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc1 (r_2_3));
+ T (S (2), alloc1 (r_2_3));
+ T (S (3), alloc1 (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), alloc1 (r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+NOIPA void test_strcpy_alloc2_4 (void)
+{
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+#define alloc2_4(n1, n2) alloc2_4 (1, n1, 2, n2)
+
+ T (S (0), alloc2_4 (r_1_2, 1));
+ T (S (1), alloc2_4 (r_1_2, 1));
+ T (S (2), alloc2_4 (r_1_2, 1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (2), alloc2_4 (r_2_3, 1));
+ T (S (3), alloc2_4 (r_2_3, 1)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc2_4 (1, r_1_2));
+ T (S (1), alloc2_4 (1, r_1_2));
+ T (S (2), alloc2_4 (1, r_1_2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (2), alloc2_4 (1, r_2_3));
+ T (S (3), alloc2_4 (1, r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc2_4 (r_1_2, 2));
+ T (S (1), alloc2_4 (r_1_2, 2));
+ T (S (2), alloc2_4 (r_1_2, 2));
+ T (S (3), alloc2_4 (r_1_2, 2));
+ T (S (4), alloc2_4 (r_1_2, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc2_4 (r_2_3, 2));
+ T (S (1), alloc2_4 (r_2_3, 2));
+ T (S (2), alloc2_4 (r_2_3, 2));
+ T (S (5), alloc2_4 (r_2_3, 2));
+ T (S (6), alloc2_4 (r_2_3, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc2_4 (r_1_2, 2));
+ T (S (1), alloc2_4 (r_1_2, 2));
+ T (S (2), alloc2_4 (r_1_2, 2));
+ T (S (3), alloc2_4 (r_1_2, 2));
+ T (S (4), alloc2_4 (r_1_2, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc2_4 (r_2_3, 2));
+ T (S (1), alloc2_4 (r_2_3, 2));
+ T (S (2), alloc2_4 (r_2_3, 2));
+ T (S (5), alloc2_4 (r_2_3, 2));
+ T (S (6), alloc2_4 (r_2_3, 2)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (S (0), alloc2_4 (r_1_2, r_2_3));
+ T (S (1), alloc2_4 (r_1_2, r_2_3));
+ T (S (2), alloc2_4 (r_1_2, r_2_3));
+ T (S (3), alloc2_4 (r_1_2, r_2_3));
+ T (S (4), alloc2_4 (r_1_2, r_2_3));
+ T (S (5), alloc2_4 (r_1_2, r_2_3));
+ T (S (6), alloc2_4 (r_1_2, r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (S (9), alloc2_4 (r_1_2, r_2_3)); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ size_t r_2_dmax = UR (2, DIFF_MAX);
+ T (S (0), alloc2_4 (r_2_dmax, r_2_dmax));
+ T (S (9), alloc2_4 (r_2_dmax, r_2_dmax));
+
+ size_t r_2_smax = UR (2, SIZE_MAX);
+ T (S (0), alloc2_4 (r_2_smax, r_2_smax));
+ T (S (9), alloc2_4 (r_2_smax, r_2_smax));
+}
+
+#undef T
+#define T(T, src, n) do { \
+ char *s = src; \
+ T vla[n]; \
+ char *d = (char*)vla; \
+ strcpy (d, s); \
+ sink (vla); \
+ } while (0)
+
+NOIPA void test_strcpy_vla (void)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (char, S (0), r_0_1);
+ T (char, S (1), r_0_1); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (char, S (0), r_1_2);
+ T (char, S (1), r_1_2);
+ T (char, S (2), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (char, S (0), r_2_3);
+ T (char, S (2), r_2_3);
+ T (char, S (3), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (char, S (9), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" }
+
+#ifdef __INT16_TYPE__
+ typedef __INT16_TYPE__ int16_t;
+
+ T (int16_t, S (0), r_1_2);
+ T (int16_t, S (2), r_1_2);
+ T (int16_t, S (3), r_1_2);
+ T (int16_t, S (4), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (int16_t, S (5), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (int16_t, S (9), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" }
+
+ T (int16_t, S (0), r_2_3);
+ T (int16_t, S (2), r_2_3);
+ T (int16_t, S (3), r_2_3);
+ T (int16_t, S (4), r_2_3);
+ T (int16_t, S (5), r_2_3);
+ T (int16_t, S (6), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" }
+#endif
+
+#ifdef __INT32_TYPE__
+ typedef __INT32_TYPE__ int32_t;
+
+ T (int32_t, S ( 0), r_2_3);
+ T (int32_t, S ( 2), r_2_3);
+ T (int32_t, S ( 3), r_2_3);
+ T (int32_t, S ( 4), r_2_3);
+ T (int32_t, S ( 5), r_2_3);
+ T (int32_t, S ( 6), r_2_3);
+ T (int32_t, S (11), r_2_3);
+ T (int32_t, S (12), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" }
+ T (int32_t, S (36), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" }
+#endif
+}
+
+
+struct Flex
+{
+ char n, ax[];
+};
+
+#undef T
+#define T(T, src, n) do { \
+ char *s = src; \
+ typedef struct { T n, ax[]; } Flex; \
+ Flex *p = (Flex*)malloc (sizeof *p + n); \
+ char *d = (char*)p->ax; \
+ strcpy (d, s); \
+ sink (p); \
+ } while (0)
+
+NOIPA void test_strcpy_malloc_flexarray (void)
+{
+ size_t r_0_1 = UR (0, 1);
+ size_t r_1_2 = UR (1, 2);
+ size_t r_2_3 = UR (2, 3);
+
+ T (char, S (0), r_0_1);
+ T (char, S (1), r_0_1); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } }
+
+ T (char, S (0), r_1_2);
+ T (char, S (1), r_1_2);
+ T (char, S (2), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } }
+
+ T (char, S (0), r_2_3);
+ T (char, S (2), r_2_3);
+ T (char, S (3), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } }
+ T (char, S (9), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } }
+}
/* { dg-do compile } */
-/* { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+/* { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
extern void abort (void);
Exercise attribute copy for functions.
{ dg-do compile }
{ dg-require-alias "" }
- { dg-options "-O2 -Wall" } */
+ { dg-options "-O2 -Wall -Wno-array-bounds" } */
#define Assert(expr) typedef char AssertExpr[2 * !!(expr) - 1]
-/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings. */
+/* Test exercising -Wstringop-overflow warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wstringop-overflow=1" } */
memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src);
- /* At -Wrawmem-overflow=1 the destination is considered to be
+ /* At -Wstringop-overflow=1 the destination is considered to be
the whole array and its size is therefore sizeof a. */
memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src);
struct A *a = __builtin_malloc (sizeof *a * 2);
- memcpy (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, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
escape (a, src);
- /* At -Wrawmem-overflow=1 the destination is considered to be
+ /* At -Wstringop-overflow=1 the destination is considered to be
the whole array and its size is therefore sizeof a. */
- memcpy (&a[0], 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], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
escape (a, src);
/* Verify the same as above but by writing into the first mmeber
struct B *b = __builtin_malloc (sizeof *b * 2);
- memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+ memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" } */
escape (b);
/* The following idiom of clearing multiple members of a struct is
struct A *a = __builtin_malloc (sizeof *a * 2);
- memcpy (p, a, n); /* { dg-warning "reading between 8 and 32 bytes from region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+ memcpy (p, a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */
- memcpy (p, &a[0], n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+ 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 *-*-*} } */
struct B *b = __builtin_malloc (sizeof *b * 2);
- memcpy (p, &b[0], n); /* { dg-warning "reading between 12 and 32 bytes from a region of size 8" "memcpy from allocated" { xfail *-*-*} } */
+ memcpy (p, &b[0], n); /* { dg-warning "reading between 12 and 32 bytes from a region of size 8" "memcpy from allocated" } */
/* Verify memchr/memcmp. */
n = sizeof *b * 2 + 1;
- memchr (b, 1, n); /* { dg-warning "reading 5 bytes from a region of size 4" "memcmp from allocated" { xfail *-*-* } } */
- memcmp (p, b, n); /* { dg-warning "reading 5 bytes from a region of size 4" "memcmp from allocated" { xfail *-*-* } } */
+ memchr (b, 1, n); /* { dg-warning "reading 9 bytes from a region of size 8" "memcmp from allocated" } */
+ memcmp (p, b, n); /* { dg-warning "reading 9 bytes from a region of size 8" "memcmp from allocated" } */
}
a (c);
a (c);
}
+
+/* { dg-prune-output "\\\[-Wstringop-overflow" } */
a (c);
a (c);
}
+
+/* { dg-prune-output "\\\[-Wstringop-overflow" } */
stmt, lenrange[2], dstsize))
{
if (decl)
- inform (DECL_SOURCE_LOCATION (decl),
- "destination object declared here");
+ {
+ if (TREE_CODE (decl) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (decl);
+ if (is_gimple_call (stmt))
+ {
+ tree allocfn = gimple_call_fndecl (stmt);
+ inform (gimple_location (stmt),
+ "destination region allocated by %qD "
+ "here", allocfn);
+ }
+ }
+ else
+ inform (DECL_SOURCE_LOCATION (decl),
+ "destination object declared here");
+ }
gimple_set_no_warning (stmt, true);
}
}