Commit 268209f3 by Martin Sebor Committed by Martin Sebor

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
parent a59c50bd
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> 2019-12-04 Julian Brown <julian@codesourcery.com>
* config/gcn/gcn.h (FIXED_REGISTERS): Make s6/s7 fixed registers. * config/gcn/gcn.h (FIXED_REGISTERS): Make s6/s7 fixed registers.
...@@ -3696,6 +3696,97 @@ check_access (tree exp, tree, tree, tree dstwrite, ...@@ -3696,6 +3696,97 @@ check_access (tree exp, tree, tree, tree dstwrite,
return true; 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 /* Helper to compute the size of the object referenced by the DEST
expression which must have pointer type, using Object Size type expression which must have pointer type, using Object Size type
OSTYPE (only the least significant 2 bits are used). Return OSTYPE (only the least significant 2 bits are used). Return
...@@ -3704,16 +3795,22 @@ check_access (tree exp, tree, tree, tree dstwrite, ...@@ -3704,16 +3795,22 @@ check_access (tree exp, tree, tree, tree dstwrite,
a non-constant offset in some range the returned value represents a non-constant offset in some range the returned value represents
the largest size given the smallest non-negative offset in the the largest size given the smallest non-negative offset in the
range. If nonnull, set *PDECL to the decl of the referenced 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 The function is intended for diagnostics and should not be used
to influence code generation or optimization. */ to influence code generation or optimization. */
tree 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) if (!pdecl)
pdecl = &dummy; pdecl = &dummy_decl;
tree dummy_off = size_zero_node;
if (!poff)
poff = &dummy_off;
unsigned HOST_WIDE_INT size; unsigned HOST_WIDE_INT size;
...@@ -3726,6 +3823,13 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) ...@@ -3726,6 +3823,13 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
if (TREE_CODE (dest) == SSA_NAME) if (TREE_CODE (dest) == SSA_NAME)
{ {
gimple *stmt = SSA_NAME_DEF_STMT (dest); 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)) if (!is_gimple_assign (stmt))
return NULL_TREE; return NULL_TREE;
...@@ -3741,7 +3845,7 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) ...@@ -3741,7 +3845,7 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
tree off = gimple_assign_rhs2 (stmt); tree off = gimple_assign_rhs2 (stmt);
if (TREE_CODE (off) == INTEGER_CST) 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 wioff = wi::to_wide (off);
wide_int wisiz = wi::to_wide (size); wide_int wisiz = wi::to_wide (size);
...@@ -3752,10 +3856,16 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) ...@@ -3752,10 +3856,16 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
if (wi::sign_mask (wioff)) if (wi::sign_mask (wioff))
; ;
else if (wi::ltu_p (wioff, wisiz)) 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 else
return size_zero_node; {
*poff = size_binop (PLUS_EXPR, *poff, off);
return size_zero_node;
}
} }
} }
else if (TREE_CODE (off) == SSA_NAME else if (TREE_CODE (off) == SSA_NAME
...@@ -3777,10 +3887,18 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) ...@@ -3777,10 +3887,18 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
|| wi::sign_mask (max)) || wi::sign_mask (max))
; ;
else if (wi::ltu_p (min, wisiz)) 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 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 */) ...@@ -3799,19 +3917,24 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
{ {
tree ref = TREE_OPERAND (dest, 0); tree ref = TREE_OPERAND (dest, 0);
tree off = TREE_OPERAND (dest, 1); 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 /* If the declaration of the destination object is known
to have zero size, return zero. */ 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; return integer_zero_node;
if (TREE_CODE (off) != INTEGER_CST /* A valid offset into a declared object cannot be negative. */
|| TREE_CODE (size) != INTEGER_CST) if (tree_int_cst_sgn (*poff) < 0)
return NULL_TREE; return size_zero_node;
/* Adjust SIZE either up or down by the sum of *POFF and OFF
above. */
if (TREE_CODE (dest) == ARRAY_REF) if (TREE_CODE (dest) == ARRAY_REF)
{ {
/* Convert the array index into a byte offset. */
tree eltype = TREE_TYPE (dest); tree eltype = TREE_TYPE (dest);
tree tpsize = TYPE_SIZE_UNIT (eltype); tree tpsize = TYPE_SIZE_UNIT (eltype);
if (tpsize && TREE_CODE (tpsize) == INTEGER_CST) if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
...@@ -3820,9 +3943,74 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) ...@@ -3820,9 +3943,74 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
return NULL_TREE; return NULL_TREE;
} }
if (tree_int_cst_lt (off, size)) wide_int offrng[2];
return fold_build2 (MINUS_EXPR, size_type_node, size, off); if (TREE_CODE (off) == INTEGER_CST)
return integer_zero_node; 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; return NULL_TREE;
...@@ -3850,9 +4038,11 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */) ...@@ -3850,9 +4038,11 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
type = TREE_TYPE (type); type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type); type = TYPE_MAIN_VARIANT (type);
if (TREE_CODE (dest) == ADDR_EXPR)
dest = TREE_OPERAND (dest, 0);
if (TREE_CODE (type) == ARRAY_TYPE 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)) if (tree size = TYPE_SIZE_UNIT (type))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE; return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
......
...@@ -133,7 +133,8 @@ extern tree fold_call_stmt (gcall *, bool); ...@@ -133,7 +133,8 @@ extern tree fold_call_stmt (gcall *, bool);
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec); extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
extern bool is_simple_builtin (tree); extern bool is_simple_builtin (tree);
extern bool is_inexpensive_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 readonly_data_expr (tree exp);
extern bool init_target_chars (void); extern bool init_target_chars (void);
......
...@@ -966,7 +966,6 @@ builtin_access::generic_overlap () ...@@ -966,7 +966,6 @@ builtin_access::generic_overlap ()
const offset_int maxobjsize = acs.dstref->maxobjsize; const offset_int maxobjsize = acs.dstref->maxobjsize;
offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize; 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 /* Adjust the larger bounds of the offsets (which may be the first
element if the lower bound is larger than the upper bound) to element if the lower bound is larger than the upper bound) to
...@@ -1193,7 +1192,6 @@ builtin_access::strcat_overlap () ...@@ -1193,7 +1192,6 @@ builtin_access::strcat_overlap ()
acs.dstsiz[1] = 1; acs.dstsiz[1] = 1;
offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize; 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 /* For references to the same base object, determine if there's a pair
of valid offsets into the two references such that access between 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> 2019-12-04 Joseph Myers <joseph@codesourcery.com>
PR c/36941 PR c/36941
......
...@@ -425,7 +425,7 @@ void test_strncpy_alloc (const char* s) ...@@ -425,7 +425,7 @@ void test_strncpy_alloc (const char* s)
size_t n = 7; size_t n = 7;
char *d = (char *)__builtin_malloc (n); 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); Dest *pd = (Dest *)__builtin_malloc (sizeof *pd * n);
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */ 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 /* PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
{ dg-do compile } { dg-do compile }
{ dg-options "-O2 -Wall" } */ { dg-options "-O2 -Wall -Wno-array-bounds" } */
#define ALLOC_SIZE(N) __attribute__ ((alloc_size (N))) #define ALLOC_SIZE(N) __attribute__ ((alloc_size (N)))
......
/* 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
/* 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" }
}
/* { dg-do compile } */ /* { 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); extern void abort (void);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Exercise attribute copy for functions. Exercise attribute copy for functions.
{ dg-do compile } { dg-do compile }
{ dg-require-alias "" } { dg-require-alias "" }
{ dg-options "-O2 -Wall" } */ { dg-options "-O2 -Wall -Wno-array-bounds" } */
#define Assert(expr) typedef char AssertExpr[2 * !!(expr) - 1] #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-do compile } */
/* { dg-options "-O2 -Wstringop-overflow=1" } */ /* { dg-options "-O2 -Wstringop-overflow=1" } */
...@@ -49,7 +49,7 @@ void test_memop_warn_local (const void *src) ...@@ -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" } */ memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src); 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. */ 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 (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src); escape (a, src);
...@@ -110,12 +110,12 @@ void test_memop_warn_alloc (const void *src) ...@@ -110,12 +110,12 @@ void test_memop_warn_alloc (const void *src)
struct A *a = __builtin_malloc (sizeof *a * 2); 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); 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. */ 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); escape (a, src);
/* Verify the same as above but by writing into the first mmeber /* Verify the same as above but by writing into the first mmeber
...@@ -127,7 +127,7 @@ void test_memop_warn_alloc (const void *src) ...@@ -127,7 +127,7 @@ void test_memop_warn_alloc (const void *src)
struct B *b = __builtin_malloc (sizeof *b * 2); 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); escape (b);
/* The following idiom of clearing multiple members of a struct is /* The following idiom of clearing multiple members of a struct is
......
...@@ -102,9 +102,9 @@ void test_memop_warn_alloc (void *p) ...@@ -102,9 +102,9 @@ void test_memop_warn_alloc (void *p)
struct A *a = __builtin_malloc (sizeof *a * 2); 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 *-*-*} } */ 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) ...@@ -112,13 +112,13 @@ void test_memop_warn_alloc (void *p)
struct B *b = __builtin_malloc (sizeof *b * 2); 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. */ /* Verify memchr/memcmp. */
n = sizeof *b * 2 + 1; n = sizeof *b * 2 + 1;
memchr (b, 1, 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 5 bytes from a region of size 4" "memcmp from allocated" { xfail *-*-* } } */ memcmp (p, b, n); /* { dg-warning "reading 9 bytes from a region of size 8" "memcmp from allocated" } */
} }
......
...@@ -10,3 +10,5 @@ b () ...@@ -10,3 +10,5 @@ b ()
a (c); a (c);
a (c); a (c);
} }
/* { dg-prune-output "\\\[-Wstringop-overflow" } */
...@@ -10,3 +10,5 @@ b () ...@@ -10,3 +10,5 @@ b ()
a (c); a (c);
a (c); a (c);
} }
/* { dg-prune-output "\\\[-Wstringop-overflow" } */
...@@ -4394,8 +4394,22 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rval ...@@ -4394,8 +4394,22 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rval
stmt, lenrange[2], dstsize)) stmt, lenrange[2], dstsize))
{ {
if (decl) 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); gimple_set_no_warning (stmt, true);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment