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
This commit is contained in:
Martin Sebor 2019-12-05 01:28:11 +00:00 committed by Martin Sebor
parent a59c50bd14
commit 268209f3a0
17 changed files with 895 additions and 40 deletions

View File

@ -1,3 +1,14 @@
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.

View File

@ -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;

View File

@ -133,7 +133,8 @@ extern tree fold_call_stmt (gcall *, bool);
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);

View File

@ -966,7 +966,6 @@ builtin_access::generic_overlap ()
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
@ -1193,7 +1192,6 @@ builtin_access::strcat_overlap ()
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

View File

@ -1,3 +1,18 @@
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

View File

@ -425,7 +425,7 @@ void test_strncpy_alloc (const char* s)
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" } */

View File

@ -1,6 +1,6 @@
/* 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)))

View File

@ -0,0 +1,157 @@
/* 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

View File

@ -0,0 +1,88 @@
/* 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" }
}

View File

@ -0,0 +1,377 @@
/* 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 *-*-* } }
}

View File

@ -1,5 +1,5 @@
/* { 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);

View File

@ -2,7 +2,7 @@
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]

View File

@ -1,4 +1,4 @@
/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings. */
/* Test exercising -Wstringop-overflow warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wstringop-overflow=1" } */
@ -49,7 +49,7 @@ void test_memop_warn_local (const void *src)
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);
@ -110,12 +110,12 @@ void test_memop_warn_alloc (const void *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
@ -127,7 +127,7 @@ void test_memop_warn_alloc (const void *src)
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

View File

@ -102,9 +102,9 @@ void test_memop_warn_alloc (void *p)
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 *-*-*} } */
@ -112,13 +112,13 @@ void test_memop_warn_alloc (void *p)
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" } */
}

View File

@ -10,3 +10,5 @@ b ()
a (c);
a (c);
}
/* { dg-prune-output "\\\[-Wstringop-overflow" } */

View File

@ -10,3 +10,5 @@ b ()
a (c);
a (c);
}
/* { dg-prune-output "\\\[-Wstringop-overflow" } */

View File

@ -4394,8 +4394,22 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rval
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);
}
}