PR middle-end/91582 - missing heap overflow detection for strcpy

PR middle-end/91582 - missing heap overflow detection for strcpy
PR middle-end/92868 - ICE: tree check: expected integer_cst, have ssa_name

gcc/ChangeLog:

	PR middle-end/91582
	PR middle-end/92868
	* builtins.c (addr_decl_size): New function.
	(gimple_call_alloc_size): Add arguments.
	(compute_objsize): Add an argument.  Set *PDECL even for allocated
	objects.
	Correct checking for negative wide_int.
	Correct handling of negative outer offsets into unknown regions
	or with unknown inner offsets.
	Extend offsets to at most sizetype precision.
	Only handle constant subobject sizes.
	* builtins.h (gimple_call_alloc_size): Add arguments.
	* tree.c (component_ref_size): Always return sizetype.
	* tree-ssa-strlen.c (strinfo::alloc): New member.
	(get_addr_stridx): Add argument.
	(get_stridx): Use ptrdiff_t.  Add argument.
	(new_strinfo): Set new member.
	(get_string_length): Handle alloca and VLA.
	(dump_strlen_info): Dump more state.
	(maybe_invalidate): Print more info.  Decrease indentation.
	(unshare_strinfo): Set new member.
	(valid_builtin_call): Handle alloca and VLA.
	(maybe_warn_overflow): Check and set no-warning bit.  Improve
	handling of offsets.  Print allocated objects.
	(handle_builtin_strlen): Handle strinfo records with null lengths.
	(handle_builtin_strcpy): Add argument.  Call maybe_warn_overflow.
	(is_strlen_related_p): Handle dynamically allocated objects.
	(get_range): Add argument.
	(handle_builtin_malloc): Rename...
	(handle_alloc): ...to this and handle all allocation functions.
	(handle_builtin_memset): Call maybe_warn_overflow.
	(count_nonzero_bytes): Handle more MEM_REF forms.
	(strlen_check_and_optimize_call): Call handle_alloc_call.  Pass
	arguments to more callees.
	(handle_integral_assign): Add argument.  Create strinfo entries
	for MEM_REF assignments.
	(check_and_optimize_stmt): Handle more MEM_REF forms.

gcc/testsuite/ChangeLog:

	PR middle-end/91582
	* c-c++-common/Wrestrict.c: Adjust expected warnings.
	* gcc/testsuite/c-c++-common/Wstringop-truncation-4.c: Enable more
	warnings.
	* gcc/testsuite/c-c++-common/Wstringop-truncation.c: Remove an xfail.
	* gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
	* gcc.dg/Warray-bounds-47.c: Same.
	* gcc.dg/Warray-bounds-52.c: New test.
	* gcc.dg/Wstringop-overflow-27.c: New test.
	* gcc.dg/Wstringop-overflow-28.c: New test.
	* gcc.dg/Wstringop-overflow-29.c: New test.
	* gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
	* gcc.dg/attr-copy-2.c: Adjust expected warnings.
	* gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
	* gcc.dg/strlenopt-86.c: Relax test.
	* gcc.target/i386/pr82002-1.c: Prune expected warnings.

From-SVN: r279392
This commit is contained in:
Martin Sebor 2019-12-14 00:52:46 +00:00 committed by Martin Sebor
parent e78b9a6fca
commit ef29b12cfb
22 changed files with 1548 additions and 397 deletions

View File

@ -1,3 +1,43 @@
2019-12-13 Martin Sebor <msebor@redhat.com>
PR middle-end/91582
PR middle-end/92868
* builtins.c (addr_decl_size): New function.
(gimple_call_alloc_size): Add arguments.
(compute_objsize): Add an argument. Set *PDECL even for allocated
objects.
Correct checking for negative wide_int.
Correct handling of negative outer offsets into unknown regions
or with unknown inner offsets.
Extend offsets to at most sizetype precision.
Only handle constant subobject sizes.
* builtins.h (gimple_call_alloc_size): Add arguments.
* tree.c (component_ref_size): Always return sizetype.
* tree-ssa-strlen.c (strinfo::alloc): New member.
(get_addr_stridx): Add argument.
(get_stridx): Use ptrdiff_t. Add argument.
(new_strinfo): Set new member.
(get_string_length): Handle alloca and VLA.
(dump_strlen_info): Dump more state.
(maybe_invalidate): Print more info. Decrease indentation.
(unshare_strinfo): Set new member.
(valid_builtin_call): Handle alloca and VLA.
(maybe_warn_overflow): Check and set no-warning bit. Improve
handling of offsets. Print allocated objects.
(handle_builtin_strlen): Handle strinfo records with null lengths.
(handle_builtin_strcpy): Add argument. Call maybe_warn_overflow.
(is_strlen_related_p): Handle dynamically allocated objects.
(get_range): Add argument.
(handle_builtin_malloc): Rename...
(handle_alloc): ...to this and handle all allocation functions.
(handle_builtin_memset): Call maybe_warn_overflow.
(count_nonzero_bytes): Handle more MEM_REF forms.
(strlen_check_and_optimize_call): Call handle_alloc_call. Pass
arguments to more callees.
(handle_integral_assign): Add argument. Create strinfo entries
for MEM_REF assignments.
(check_and_optimize_stmt): Handle more MEM_REF forms.
2019-12-13 Iain Sandoe <iain@sandoe.co.uk>
* config/rs6000/darwin.h (DARWIN_DYLIB1_SPEC): New.

View File

@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see
#include "calls.h"
#include "varasm.h"
#include "tree-object-size.h"
#include "tree-ssa-strlen.h"
#include "realmpfr.h"
#include "cfgrtl.h"
#include "except.h"
@ -3696,11 +3697,13 @@ 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. */
/* If STMT is a call to an allocation function, returns the constant
size of the object allocated by the call represented as sizetype.
If nonnull, sets RNG1[] to the range of the size. */
tree
gimple_call_alloc_size (gimple *stmt)
gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
const vr_values *rvals /* = NULL */)
{
if (!stmt)
return NULL_TREE;
@ -3747,11 +3750,12 @@ gimple_call_alloc_size (gimple *stmt)
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)
wide_int rng1_buf[2];
/* If RNG1 is not set, use the buffer. */
if (!rng1)
rng1 = rng1_buf;
if (!get_range (size, rng1, rvals))
return NULL_TREE;
if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
@ -3761,20 +3765,18 @@ gimple_call_alloc_size (gimple *stmt)
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)
if (!get_range (n, rng2, rvals))
return NULL_TREE;
/* Extend to the maximum precsion to avoid overflow. */
/* Extend to the maximum precision 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. */
/* Compute products of both bounds for the caller but return the lesser
of SIZE_MAX and the product of the upper bounds as a constant. */
rng1[0] = rng1[0] * rng2[0];
rng1[1] = rng1[1] * rng2[1];
tree size_max = TYPE_MAX_VALUE (sizetype);
@ -3787,36 +3789,76 @@ gimple_call_alloc_size (gimple *stmt)
return wide_int_to_tree (sizetype, rng1[1]);
}
/* Helper for compute_objsize. Returns the constant size of the DEST
if it refers to a variable or field and sets *PDECL to the DECL and
*POFF to zero. Otherwise returns null for other nodes. */
static tree
addr_decl_size (tree dest, tree *pdecl, tree *poff)
{
if (TREE_CODE (dest) == ADDR_EXPR)
dest = TREE_OPERAND (dest, 0);
if (DECL_P (dest))
{
*pdecl = dest;
*poff = integer_zero_node;
if (tree size = DECL_SIZE_UNIT (dest))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
}
if (TREE_CODE (dest) == COMPONENT_REF)
{
*pdecl = TREE_OPERAND (dest, 1);
*poff = integer_zero_node;
/* Only return constant sizes for now while callers depend on it. */
if (tree size = component_ref_size (dest))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
}
return NULL_TREE;
}
/* 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
an estimate of the size of the object if successful or NULL when
the size cannot be determined. When the referenced object involves
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. Likewise,
when POFF is nonnull *POFF is set to the offset into *PDECL.
OSTYPE (only the least significant 2 bits are used).
Returns an estimate of the size of the object represented as
a sizetype constant if successful or NULL when the size cannot
be determined.
When the referenced object involves 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, sets *PDECL to the decl of the referenced 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 */,
tree *poff /* = NULL */)
tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
{
tree dummy_decl = NULL_TREE;
if (!pdecl)
pdecl = &dummy_decl;
tree dummy_off = size_zero_node;
tree dummy_off = NULL_TREE;
if (!poff)
poff = &dummy_off;
unsigned HOST_WIDE_INT size;
/* Only the two least significant bits are meaningful. */
ostype &= 3;
if (ostype)
/* Except for overly permissive calls to memcpy and other raw
memory functions with zero OSTYPE, detect the size from simple
DECLs first to more reliably than compute_builtin_object_size
set *PDECL and *POFF. */
if (tree size = addr_decl_size (dest, pdecl, poff))
return size;
unsigned HOST_WIDE_INT size;
if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff))
return build_int_cst (sizetype, size);
@ -3826,8 +3868,15 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
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);
from its argument(s). If successful, also set *PDECL to
DEST for the caller to include in diagnostics. */
if (tree size = gimple_call_alloc_size (stmt))
{
*pdecl = dest;
*poff = integer_zero_node;
return size;
}
return NULL_TREE;
}
if (!is_gimple_assign (stmt))
@ -3853,17 +3902,21 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
/* Ignore negative offsets for now. For others,
use the lower bound as the most optimistic
estimate of the (remaining) size. */
if (wi::sign_mask (wioff))
if (wi::neg_p (wioff))
;
else if (wi::ltu_p (wioff, wisiz))
{
*poff = size_binop (PLUS_EXPR, *poff, off);
return wide_int_to_tree (TREE_TYPE (size),
wi::sub (wisiz, wioff));
}
else
{
*poff = size_binop (PLUS_EXPR, *poff, off);
if (*poff)
{
*poff = fold_convert (ptrdiff_type_node, *poff);
off = fold_convert (ptrdiff_type_node, *poff);
*poff = size_binop (PLUS_EXPR, *poff, off);
}
else
*poff = off;
if (wi::ltu_p (wioff, wisiz))
return wide_int_to_tree (TREE_TYPE (size),
wi::sub (wisiz, wioff));
return size_zero_node;
}
}
@ -3875,32 +3928,29 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
enum value_range_kind rng = get_range_info (off, &min, &max);
if (rng == VR_RANGE)
{
if (tree size = compute_objsize (dest, ostype, pdecl))
{
wide_int wisiz = wi::to_wide (size);
if (tree size = compute_objsize (dest, ostype, pdecl, poff))
{
wide_int wisiz = wi::to_wide (size);
/* Ignore negative offsets for now. For others,
use the lower bound as the most optimistic
estimate of the (remaining)size. */
if (wi::sign_mask (min)
|| wi::sign_mask (max))
;
else if (wi::ltu_p (min, wisiz))
{
*poff = size_binop (PLUS_EXPR, *poff,
wide_int_to_tree (sizetype, min));
/* Ignore negative offsets for now. For others,
use the lower bound as the most optimistic
estimate of the (remaining)size. */
if (wi::neg_p (min) || wi::neg_p (max))
;
else
{
/* FIXME: For now, since the offset is non-constant,
clear *POFF to keep it from being "misused."
Eventually *POFF will need to become a range that
can be properly added to the outer offset if it
too is one. */
*poff = NULL_TREE;
if (wi::ltu_p (min, wisiz))
return wide_int_to_tree (TREE_TYPE (size),
wi::sub (wisiz, min));
}
else
{
*poff = size_binop (PLUS_EXPR, *poff,
wide_int_to_tree (sizetype, min));
return size_zero_node;
}
}
}
return size_zero_node;
}
}
}
}
else if (code != ADDR_EXPR)
@ -3926,10 +3976,25 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
&& *poff && integer_zerop (*poff))
return size_zero_node;
/* A valid offset into a declared object cannot be negative. */
if (tree_int_cst_sgn (*poff) < 0)
/* A valid offset into a declared object cannot be negative.
A zero size with a zero "inner" offset is still zero size
regardless of the "other" offset OFF. */
if (*poff
&& ((integer_zerop (*poff) && integer_zerop (size))
|| (TREE_CODE (*poff) == INTEGER_CST
&& tree_int_cst_sgn (*poff) < 0)))
return size_zero_node;
wide_int offrng[2];
if (!get_range (off, offrng, rvals))
return NULL_TREE;
/* Convert to the same precision to keep wide_int from "helpfully"
crashing whenever it sees other arguments. */
const unsigned sizprec = TYPE_PRECISION (sizetype);
offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
/* Adjust SIZE either up or down by the sum of *POFF and OFF
above. */
if (TREE_CODE (dest) == ARRAY_REF)
@ -3938,29 +4003,35 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
tree eltype = TREE_TYPE (dest);
tree tpsize = TYPE_SIZE_UNIT (eltype);
if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
off = fold_build2 (MULT_EXPR, size_type_node, off, tpsize);
{
wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ());
offrng[0] *= wsz;
offrng[1] *= wsz;
}
else
return NULL_TREE;
}
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 wisize = wi::to_wide (size);
if (!*poff)
{
wide_int min, max;
enum value_range_kind rng
= get_range_info (off, offrng, offrng + 1);
if (rng != VR_RANGE)
return NULL_TREE;
/* If the "inner" offset is unknown and the "outer" offset
is either negative or less than SIZE, return the size
minus the offset. This may be overly optimistic in
the first case if the inner offset happens to be less
than the absolute value of the outer offset. */
if (wi::neg_p (offrng[0]))
return size;
if (wi::ltu_p (offrng[0], wisize))
return build_int_cst (sizetype, (wisize - offrng[0]).to_uhwi ());
return size_zero_node;
}
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);
offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
tree dstoff = *poff;
if (integer_zerop (*poff))
@ -3972,14 +4043,14 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
*poff = size_binop (PLUS_EXPR, *poff, off);
}
if (wi::sign_mask (offrng[0]) >= 0)
if (!wi::neg_p (offrng[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);
wide_int wisize = wi::to_wide (size, sizprec);
if (wi::ltu_p (wisize, offrng[0]))
return size_zero_node;
@ -3999,39 +4070,25 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
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);
dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED);
dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED);
wide_int declsize = wi::to_wide (size);
if (wi::sign_mask (dstoffrng[0]) > 0)
declsize += dstoffrng[0];
if (!wi::neg_p (dstoffrng[0]))
wisize += dstoffrng[0];
offrng[1] += dstoffrng[1];
if (wi::sign_mask (offrng[1]) < 0)
if (wi::neg_p (offrng[1]))
return size_zero_node;
return wide_int_to_tree (sizetype, declsize);
return wide_int_to_tree (sizetype, wisize);
}
return NULL_TREE;
}
if (TREE_CODE (dest) == COMPONENT_REF)
{
*pdecl = TREE_OPERAND (dest, 1);
return component_ref_size (dest);
}
if (TREE_CODE (dest) != ADDR_EXPR)
return NULL_TREE;
tree ref = TREE_OPERAND (dest, 0);
if (DECL_P (ref))
{
*pdecl = ref;
if (tree size = DECL_SIZE_UNIT (ref))
return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
}
/* Try simple DECLs not handled above. */
if (tree size = addr_decl_size (dest, pdecl, poff))
return size;
tree type = TREE_TYPE (dest);
if (TREE_CODE (type) == POINTER_TYPE)

View File

@ -133,8 +133,12 @@ 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 gimple_call_alloc_size (gimple *);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL);
class vr_values;
tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
const vr_values * = NULL);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
const vr_values * = NULL);
extern bool readonly_data_expr (tree exp);
extern bool init_target_chars (void);

View File

@ -1,3 +1,22 @@
2019-12-13 Martin Sebor <msebor@redhat.com>
PR middle-end/91582
* c-c++-common/Wrestrict.c: Adjust expected warnings.
* c-c++-common/Wstringop-truncation-4.c: Enable more
warnings.
* c-c++-common/Wstringop-truncation.c: Remove an xfail.
* gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
* gcc.dg/Warray-bounds-47.c: Same.
* gcc.dg/Warray-bounds-52.c: New test.
* gcc.dg/Wstringop-overflow-27.c: New test.
* gcc.dg/Wstringop-overflow-28.c: New test.
* gcc.dg/Wstringop-overflow-29.c: New test.
* gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
* gcc.dg/attr-copy-2.c: Adjust expected warnings.
* gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
* gcc.dg/strlenopt-86.c: Relax test.
* gcc.target/i386/pr82002-1.c: Prune expected warnings.
2019-12-13 Roman Zhuykov <zhroma@ispras.ru>
PR rtl-optimization/92591

View File

@ -731,10 +731,16 @@ void test_strcpy_range (void)
r = SR (3, DIFF_MAX - 3);
T (8, "01", a + r, a);
T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
/* The accesses below might trigger either
-Wrestrict: accessing 4 bytes at offsets [3, \[0-9\]+] and 0 may overlap 1 byte at offset 3
or
-Wstringop-overflow: writing 4 bytes into a region of size 0
Either of the two is appropriate. */
T (8, "012", a + r, a); /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
T (8, "012", a + r, a); /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
/* Exercise the full range of ptrdiff_t. */
r = signed_value ();

View File

@ -21,9 +21,13 @@ struct Arrays
void test_arrays (struct Arrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. The size argument is a good
enough indication that it is not being used as a "legacy"
flexible array member. */
strncpy (p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct Pointers
@ -49,9 +53,11 @@ struct ConstArrays
void test_const_arrays (struct ConstArrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct ConstPointers
@ -77,9 +83,11 @@ struct VolatileArrays
void test_volatile_arrays (struct VolatileArrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct VolatilePointers
@ -105,9 +113,11 @@ struct ConstVolatileArrays
void test_const_volatile_arrays (struct ConstVolatileArrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct ConstVolatilePointers

View File

@ -300,8 +300,7 @@ void test_strncpy_array (Dest *pd, int i, const char* s)
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */
CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */
/* The following is not yet handled. */
CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" { xfail *-*-* } } */
CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" } */
/* Verify that a copy that nul-terminates is not diagnosed. */
CPY (pd->a5, "1234", sizeof pd->a5);

View File

@ -12,7 +12,7 @@ void sink (void*);
struct Ax
{
char n;
char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" }
char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" "note: flexarray" }
};
// Verify warning for a definition with no initializer.
@ -93,7 +93,7 @@ NOIPA void gaxx ()
struct A0
{
char n;
char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" }
char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" "note: trailing zero-length array" }
};
// Verify warning for a definition with no initializer.
@ -160,7 +160,7 @@ NOIPA void ga0x ()
struct A1
{
char n;
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" }
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" "note: trailing one-element array" }
};
// Verify warning for a definition with no initializer.
@ -234,7 +234,7 @@ NOIPA void ga1x ()
struct A1i
{
char n;
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" }
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" "note: interior one-element array" }
char x;
};
@ -307,7 +307,7 @@ NOIPA void ga1ix ()
struct Bx
{
char n;
char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" }
char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" "note: flexarray class member" }
// Verify the warning for a constant.
Bx () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
@ -332,7 +332,7 @@ NOIPA void gbxi (int i)
struct B0
{
char n;
char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" }
char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" "note: zero-length trailing array class member" }
B0 () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
@ -348,7 +348,7 @@ NOIPA void gb0 (void)
struct B1
{
char n;
char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" }
char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" "note: one-element trailing array class member" }
B1 () { a[1] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
@ -362,7 +362,7 @@ NOIPA void gb1 (void)
struct B123
{
char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" }
char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" "note: large trailing array class member" }
B123 () { a[123] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
@ -376,7 +376,7 @@ NOIPA void gb123 (void)
struct B234
{
char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" }
char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" "note: large trailing array class member" }
B234 (int i) { a[i] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};

View File

@ -3,7 +3,7 @@
Test to verify that past-the-end accesses by string functions to member
arrays by-reference objects are diagnosed.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" } */
{ dg-options "-O2 -Wall -Wno-unused-local-typedefs -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
#define SA(expr) typedef int StaticAssert [2 * !!(expr) - 1]

View File

@ -1,7 +1,7 @@
/* PR middle-end/91830 - Bogus -Warray-bounds on strcpy into a member
of a subobject compiling binutils
{ dg-do compile }
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
{ dg-options "-O2 -Wall -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
extern char* strcpy (char*, const char*);
extern void sink (void*);

View File

@ -0,0 +1,97 @@
/* PR middle-end/92341 - missing -Warray-bounds indexing past the end
of a compound literal
{ dg-do compile }
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
#include "range.h"
#define INT_MAX __INT_MAX__
#define INT_MIN (-__INT_MAX__ - 1)
void sink (int, ...);
#define T(...) sink (__LINE__, (__VA_ARGS__))
void direct_idx_cst (void)
{
T ((int[]){ }[-1]); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
T ((int[]){ }[0]); // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
T ((int[]){ }[1]); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
T ((int[]){ 1 }[-1]); // { dg-warning "array subscript -1 is below array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[0]);
T ((int[]){ 1 }[1]); // { dg-warning "array subscript 1 is above array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[INT_MIN]); // { dg-warning "array subscript -\[0-9\]+ is below array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[INT_MAX]); // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[SIZE_MAX]); // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
}
void direct_idx_var (int i)
{
T ((char[]){ }[i]); // { dg-warning "array subscript i is outside array bounds of 'char\\\[0]'" }
T ((int[]){ }[i]); // { dg-warning "array subscript i is outside array bounds of 'int\\\[0]'" }
}
void direct_idx_range (void)
{
ptrdiff_t i = SR (-2, -1);
T ((int[]){ 1 }[i]); // { dg-warning "array subscript \[ \n\r]+ is outside array bounds of 'int\\\[0]'" "pr?????" { xfail *-*-* } }
}
#undef T
#define T(idx, ...) do { \
int *p = (__VA_ARGS__); \
sink (p[idx]); \
} while (0)
void ptr_idx_cst (void)
{
T (-1, (int[]){ }); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
T ( 0, (int[]){ }); // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
T (+1, (int[]){ }); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
T (-1, (int[]){ 1 }); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" }
T ( 0, (int[]){ 1 });
T (+1, (int[]){ 1 }); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[1]'" }
T (INT_MIN, (int[]){ 1 }); // { dg-warning "array subscript -\[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { xfail ilp32 } }
T (INT_MAX, (int[]){ 1 }); // { dg-warning "array subscript \[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { target lp64 } }
// { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" "ilp32" { target ilp32 } .-1 }
T (SIZE_MAX, (int[]){ 1 }); // { dg-warning "array subscript -?\[0-9\]+ is outside array bounds of 'int\\\[1]'" }
}
void ptr_idx_var (int i)
{
T (i, (int[]){ }); // { dg-warning "array subscript \[^\n\r\]+ is outside array bounds of 'int\\\[0]'" }
T (i, (int[]){ 1 });
T (i, (int[]){ i, 1 });
}
void ptr_idx_range (void)
{
ptrdiff_t i = SR (-2, -1);
T (i, (int[]){ }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[0]'" }
T (i, (int[]){ 1 }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
T (i, (int[]){ i }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
i = SR (0, 1);
T (i, (int[]){ }); // { dg-warning "array subscript \\\[0, 1] is outside array bounds of 'int\\\[0]'" }
T (i, (int[]){ 1 });
i = SR (1, 2);
T (i, (int[]){ 1 }); // { dg-warning "array subscript \\\[1, 2] is outside array bounds of 'int\\\[1]'" }
i = SR (2, 3);
T (i, (int[]){ 1, 2, 3 });
i = SR (3, 4);
T (i, (int[]){ 2, 3, 4 }); // { dg-warning "array subscript \\\[3, 4] is outside array bounds of 'int\\\[3]'" }
}

View File

@ -0,0 +1,293 @@
/* PR middle-end/91582 - missing heap overflow detection for strcpy
PR middle-end/85484 - missing -Wstringop-overflow for strcpy with
a string of non-const length
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
typedef __SIZE_TYPE__ size_t;
extern void* calloc (size_t, size_t);
extern void* malloc (size_t);
extern void* memcpy (void*, const void*, size_t);
extern void* memset (void*, int, size_t);
extern char* strcpy (char*, const char*);
extern size_t strlen (const char*);
void sink (void*);
void test_memcpy_nowarn (const void *s, int i, size_t n)
{
sink (memcpy (calloc (1, 1), s, 1));
sink (memcpy (calloc (1, 2), s, 1));
sink (memcpy (calloc (2, 1), s, 1));
sink (memcpy (calloc (3, 1), s, 2));
sink (memcpy (calloc (3, 1), "12", 2));
sink (memcpy (calloc (3, 1), s, 3));
sink (memcpy (calloc (3, 1), "12", 3));
sink (memcpy (calloc (i, 1), s, 1));
sink (memcpy (calloc (n, 1), s, 1));
sink (memcpy (calloc (1, n), "", 1));
sink (memcpy (calloc (1, i), "", 1));
sink (memcpy (calloc (i, 1), "123", 3));
sink (memcpy (calloc (n, 1), "123", 3));
sink (memcpy (calloc (1, i), "123456", 7));
sink (memcpy (calloc (1, n), "123456", 7));
sink (memcpy (calloc (n, 1), s, 12345));
sink (memcpy (calloc (1, n), s, n - 1));
sink (memcpy (calloc (n, 1), s, n));
sink (memcpy ((char*)calloc (1, 1) + i, "123", 1));
sink (memcpy ((char*)calloc (n, 1) + i, "123", n));
sink (memcpy ((char*)calloc (1, 1) + i, s, 1));
sink (memcpy ((char*)calloc (n, 1) + i, s, n));
sink (memcpy (malloc (1), s, 1));
sink (memcpy (malloc (2), s, 1));
sink (memcpy (malloc (3), s, 2));
sink (memcpy (malloc (3), "12", 2));
sink (memcpy (malloc (3), s, 3));
sink (memcpy (malloc (3), "12", 3));
sink (memcpy (malloc (n), s, 1));
sink (memcpy (malloc (n), "", 1));
sink (memcpy (malloc (n), "123", 3));
sink (memcpy (malloc (n), "123456", 7));
sink (memcpy (malloc (n), s, 12345));
sink (memcpy (malloc (n), s, n - 1));
sink (memcpy (malloc (n), s, n));
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof a);
memcpy (p, a, sizeof a);
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
int vla[nelts];
memcpy (vla, a, nelts * sizeof *vla);
sink (vla);
}
}
void test_memcpy_warn (const int *s, size_t n)
{
{
void *p = (char*)malloc (0);
memcpy (p, s, 1); // { dg-warning "writing 1 byte into a region of size 0" }
sink (p);
}
{
void *p = (char*)malloc (1);
memcpy (p, s, 2); // { dg-warning "writing 2 bytes into a region of size 1" }
sink (p);
}
{
void *p = (char*)malloc (2);
memcpy (p, s, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
sink (p);
}
{
void *p = (char*)malloc (3);
memcpy (p, s, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof *a);
memcpy (p, a, sizeof a); // { dg-warning "" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
char vla[nelts];
memcpy (vla, a, nelts * sizeof *a); // { dg-warning "" }
sink (vla);
}
{
void *p = malloc (n);
memcpy (p, s, n * sizeof *s); // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
sink (p);
}
}
void test_memset_nowarn (int x, size_t n)
{
sink (memset (calloc (1, 1), x, 1));
sink (memset (calloc (1, 2), x, 1));
sink (memset (calloc (2, 1), x, 1));
sink (memset (calloc (3, 1), x, 2));
sink (memset (calloc (3, 1), x, 3));
sink (memset (calloc (n, 1), x, 1));
sink (memset (calloc (n, 1), x, 12345));
sink (memset (calloc (1, n), x, n - 1));
sink (memset (calloc (n, 1), x, n));
sink (memset (malloc (1), x, 1));
sink (memset (malloc (2), x, 1));
sink (memset (malloc (3), x, 2));
sink (memset (malloc (3), x, 3));
sink (memset (malloc (n), x, 1));
sink (memset (malloc (n), x, 12345));
sink (memset (malloc (n), x, n - 1));
sink (memset (malloc (n), x, n));
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof a);
memset (p, x, sizeof a);
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
int vla[nelts];
memset (vla, x, nelts * sizeof *vla);
sink (vla);
}
}
void test_memset_warn (int x, size_t n)
{
{
void *p = (char*)malloc (0);
memset (p, x, 1); // { dg-warning "writing 1 byte into a region of size 0" }
sink (p);
}
{
void *p = (char*)malloc (1);
memset (p, x, 2); // { dg-warning "writing 2 bytes into a region of size 1" }
sink (p);
}
{
void *p = (char*)malloc (2);
memset (p, x, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
sink (p);
}
{
void *p = (char*)malloc (3);
memset (p, x, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof *a);
memset (p, 0, sizeof a); // { dg-warning "" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
char vla[nelts];
memset (vla, 0, nelts * sizeof *a); // { dg-warning "" }
sink (vla);
}
{
void *p = malloc (n);
memset (p, x, n * sizeof (int)); // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
sink (p);
}
}
void test_strcpy_nowarn (const char *s)
{
{
const char a[] = "12";
int n = strlen (a);
char *t = (char*)calloc (2, n);
strcpy (t, a);
sink (t);
}
{
const char a[] = "123";
unsigned n = strlen (a) + 1;
char *t = (char*)calloc (n, 1);
strcpy (t, a);
sink (t);
}
{
const char a[] = "1234";
size_t n = strlen (a) * 2;
char *t = (char*)malloc (n);
strcpy (t, a);
sink (t);
}
{
const char a[] = "1234";
size_t len = strlen (a) + 1;
char vla[len];
strcpy (vla, a);
sink (vla);
}
{
size_t n = strlen (s) + 1;
char *t = (char*)malloc (n);
strcpy (t, s);
sink (t);
}
}
void test_strcpy_warn (const char *s)
{
{
const char a[] = "123";
/* Verify that using signed int for the strlen result works (i.e.,
that the conversion from signed int to size_t doesn't prevent
the detection. */
int n = strlen (a);
char *t = (char*)calloc (n, 1); // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note" { xfail *-*-* } }
// { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note" { target *-*-* } .-1 }
strcpy (t, a); // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
sink (t);
}
{
const char a[] = "1234";
size_t n = strlen (a);
char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note" { xfail *-*-* } }
// { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note" { target *-*-* } .-1 }
strcpy (t, a); // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
sink (t);
}
// Exercise PR middle-end/85484.
{
size_t len = strlen (s);
char vla[len]; // { dg-message "at offset 0 to an object declared here" "vla note" }
strcpy (vla, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
sink (vla);
}
{
size_t n = strlen (s);
char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object allocated by 'malloc' here" "malloc note" }
strcpy (t, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
sink (t);
}
}

View File

@ -0,0 +1,236 @@
/* 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) char* alloc1 (size_t);
extern ATTR (alloc_size (1, 2), malloc) char* alloc2 (size_t, size_t);
extern char* strcpy (char*, const char*);
void sink (void*, ...);
/* Verify warning in stores to an object of variable size N in a known
range, at an offset (N + I) with a constant I. */
void same_size_and_offset_idx_cst (void)
{
#define T(size, off, idx) do { \
size_t n_ = size; \
ptrdiff_t i_ = idx; \
char *p_ = alloc1 (n_); \
p_ += off; \
p_[i_] = 0; \
sink (p_); \
} while (0)
{
const size_t n = UR (2, 3);
T (n, n, -4); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, n, -3);
T (n, n, -2);
T (n, n, -1);
T (n, n, 0);
T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (3, 4);
T (n, n, -5); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, n, -4);
T (n, n, -3);
T (n, n, -2);
T (n, n, -1);
T (n, n, 0);
T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (5, SIZE_MAX - 2);
T (n, n, -1);
T (n, n, -1);
T (n, n, -1);
T (n, n, -1);
}
}
/* Verify warning in stores to an object of variable size N in a known
range, at an offset (M + I) with a variable M in some range and
constant I. */
void different_size_and_offset_idx_cst (void)
{
{
const size_t n = UR (2, 3);
const size_t i = UR (1, 2);
T (n, i, -4); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, i, -3); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, i, -2);
T (n, i, -1);
T (n, i, 0);
T (n, i, 1);
T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (3, 4);
const size_t i = UR (2, 5);
T (n, i, -6); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
/* The offsets -5 and -4 are both necessarily invalid even if the sum
(i - 5) and (i - 4) are (or could be) in bounds because they imply
that the intermediate offset (p + i) is out of bounds. */
T (n, i, -5); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
T (n, i, -4); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
T (n, i, -3);
T (n, i, -2);
T (n, i, -1);
T (n, i, 0);
T (n, i, 1);
T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
}
/* Verify warning in stores to an object of variable size N in a known
range, at an offset (M + I) with a variable M in some range and
constant I. */
void different_size_and_offset_idx_var (void)
{
{
const size_t n = UR (3, 4);
const size_t i = UR (1, 2);
T (n, i, SR (DIFF_MIN, 0));
T (n, i, SR ( -3, 0));
T (n, i, SR ( -1, 0));
T (n, i, SR ( 0, 1));
T (n, i, SR ( 1, 2));
T (n, i, SR ( 2, 3));
/* The warning is issued below but the offset and the size in
the note are wrong. See the FIXME in compute_objsize(). */
T (n, i, SR ( 3, 4)); // { dg-warning "\\\[-Wstringop-overflow" }
// { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
// { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
}
}
void ptr_add_2 (int n, int i0, int i1)
{
if (n < 1 || 2 < n) n = 2;
if (i0 < 0 || 1 < i0) i0 = 0;
if (i1 < 1 || 2 < i1) i1 = 1;
char *p = (char*)__builtin_malloc (n);
char *q = p;
q += i0;
q[0] = 0; // p[0]
q += i1;
q[0] = 1; // p[1]
q[1] = 2; // p[2] // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
void ptr_add_3 (int n, int i0, int i1, int i2)
{
if (n < 3 || 4 < n) n = 3;
if (i0 < 0 || 1 < i0) i0 = 0;
if (i1 < 1 || 2 < i1) i1 = 1;
if (i2 < 2 || 3 < i2) i2 = 2;
char *p = (char*)__builtin_malloc (n);
char *q = p;
q += i0;
q[0] = 0; // p[0]
q += i1;
q[0] = 1; // p[1]
q[1] = 2; // p[2]
q += i2;
q[0] = 3; // p[3]
q[1] = 4; // p[4] // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
void ptr_add_4 (int n, int i0, int i1, int i2, int i3)
{
if (n < 7 || 8 < n) n = 7;
if (i0 < 0 || 1 < i0) i0 = 0;
if (i1 < 1 || 2 < i1) i1 = 1;
if (i2 < 2 || 3 < i2) i2 = 2;
if (i3 < 3 || 4 < i3) i3 = 3;
char *p = (char*)__builtin_malloc (n);
char *q = p;
q += i0;
q[0] = 0; // p[0]
q += i1;
q[0] = 1; // p[1]
q[1] = 2; // p[2]
q += i2;
q[0] = 3; // p[3]
q[1] = 4; // p[4]
q[2] = 5; // p[5]
q += i3;
q[0] = 6; // p[6]
q[1] = 7; // p[7]
q[2] = 8; // p[8] // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
{
if (n < 1 || 2 < n) n = 2;
char *p = (char*)__builtin_malloc (n);
char *q = p;
// The following isn't diagnosed due to a bug/limitation.
q += n; // N=1 N=2
q[-1] = 0; // p[0] p[1]
q[-2] = 1; // p[-1] p[0]
q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
/* The following isn't diagnosed because the warning doesn't recognize
the index below as necessarily having the same value as the size
argument to malloc. All it considers is the range. */
q[0] = 2; // { dg-warning "\\\[-Wstringop-overflow" "pr92937: store just past the end" { xfail *-*-* } }
q[1] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}

View File

@ -0,0 +1,66 @@
/* PR middle-end/91582 - missing heap overflow detection for strcpy
Verify calls via function pointers.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
typedef __attribute__ ((alloc_size (1))) char* allocfn_t (unsigned);
extern allocfn_t allocfn;
void sink (void*);
void direct_call (void)
{
char *q = allocfn (0); // { dg-message "at offset 0 to an object with size 0 allocated by 'allocfn'" }
q[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
void local_ptr_call (void)
{
allocfn_t *ptr = allocfn;
char *q = ptr (1); // { dg-message "at offset -1 to an object with size 1 allocated by 'allocfn'" }
q[0] = 0;
q[-1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
void global_ptr_call (void)
{
extern allocfn_t *ptralloc;
allocfn_t *ptr = ptralloc;
char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptralloc'" }
q[0] = 0;
q[1] = 1;
q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
void global_ptr_array_call (void)
{
extern allocfn_t * (arralloc[]);
allocfn_t *ptr = arralloc[0];
char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptr'" }
q[0] = 1;
q[1] = 2;
q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
struct S { allocfn_t *ptralloc; };
void member_ptr_call (struct S *p)
{
char *q = p->ptralloc (3); // { dg-message "at offset 5 to an object with size 3 allocated by 'ptralloc' here" }
q[0] = 0;
q[1] = 1;
q[2] = 2;
q[5] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}

View File

@ -22,15 +22,15 @@ test (void)
strcpy (p, "Hello");
p = malloc1 (6);
strcpy (p, "Hello");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = calloc1 (2, 5);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
}

View File

@ -99,7 +99,7 @@ void* xref12 (int);
void* call_xref12 (void)
{
void *p = xref12 (3);
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
return p;
}
@ -197,7 +197,7 @@ void* falias_malloc (void);
void* call_falias_malloc (void)
{
char *p = falias_malloc ();
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
return p;
}

View File

@ -110,7 +110,7 @@ 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" } */
memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
escape (a, src);
/* At -Wstringop-overflow=1 the destination is considered to be
@ -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" } */
memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
escape (b);
/* The following idiom of clearing multiple members of a struct is

View File

@ -9,11 +9,11 @@
unsigned n0, n1;
void*
keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
keep_strlen_calloc_store_cst_memset (int i, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = 'x';
p[i] = 'x';
__builtin_memset (p, 0, b);
@ -23,11 +23,11 @@ keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
}
void*
keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
keep_strlen_calloc_store_var_memset (int i, int x, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = x;
p[i] = x;
__builtin_memset (p, 0, b);
@ -37,11 +37,11 @@ keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
}
void*
keep_strlen_calloc_store_memset_2 (int x, unsigned a, unsigned b, unsigned c)
keep_strlen_calloc_store_memset_2 (int i, int x, unsigned a, unsigned b, unsigned c)
{
char *p = __builtin_calloc (a, 1);
p[1] = x;
p[i] = x;
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,10 @@ extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
struct c_strlen_data;
class vr_values;
extern tree get_range (tree, wide_int[2], const vr_values * = NULL);
struct c_strlen_data;
extern void get_range_strlen_dynamic (tree , c_strlen_data *, const vr_values *);
/* APIs internal to strlen pass. Defined in in gimple-ssa-sprintf.c. */

View File

@ -13583,8 +13583,8 @@ get_initializer_for (tree init, tree decl)
determine the size of an initialized flexible array member.
If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
an interior zero-length array.
Returns the size (which might be zero for an object with
an uninitialized flexible array member) or null if the size
Returns the size as sizetype (which might be zero for an object
with an uninitialized flexible array member) or null if the size
cannot be determined. */
tree
@ -13733,7 +13733,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
memsz64 -= baseoff;
return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
}
return integer_zero_node;
return size_zero_node;
}
/* Return "don't know" for an external non-array object since its
@ -13744,7 +13744,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
&& DECL_EXTERNAL (base)
&& (!typematch
|| TREE_CODE (basetype) != ARRAY_TYPE)
? NULL_TREE : integer_zero_node);
? NULL_TREE : size_zero_node);
}
/* Return the machine mode of T. For vectors, returns the mode of the