aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2020-10-09 14:48:43 -0600
committerMartin Sebor <msebor@redhat.com>2020-10-12 09:05:55 -0600
commit83685efd5fd1623cfc4e4c435ce2773d95d458d1 (patch)
tree5a6514717cd82b39bcec8872ba61871599c5684a /gcc
parentde05c19d5fd661ae16dd75a895b49d32d12f5edc (diff)
downloadgcc-83685efd5fd1623cfc4e4c435ce2773d95d458d1.zip
gcc-83685efd5fd1623cfc4e4c435ce2773d95d458d1.tar.gz
gcc-83685efd5fd1623cfc4e4c435ce2773d95d458d1.tar.bz2
Generalize compute_objsize to return maximum size/offset instead of failing (PR middle-end/97023).
Also resolves: PR middle-end/97342 - bogus -Wstringop-overflow with nonzero signed and unsigned offsets PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy PR middle-end/96384 - bogus -Wstringop-overflow= storing into multidimensional array with index in range gcc/ChangeLog: PR middle-end/97342 PR middle-end/97023 PR middle-end/96384 * builtins.c (access_ref::access_ref): Initialize new member. Use new enum. (access_ref::size_remaining): Define new member function. (inform_access): Handle expressions referencing objects. (gimple_call_alloc_size): Call get_size_range instead of get_range. (gimple_call_return_array): New function. (get_range): Rename... (get_offset_range): ...to this. Improve detection of ranges from types of expressions. (gimple_call_return_array): Adjust calls to get_range per above. (compute_objsize): Same. Set maximum size or offset instead of failing for unknown objects and handle more kinds of expressions. (compute_objsize): Call access_ref::size_remaining. (compute_objsize): Have transitional wrapper fail for pointers into unknown objects. (expand_builtin_strncmp): Call access_ref::size_remaining and handle new cases. * builtins.h (access_ref::size_remaining): Declare new member function. (access_ref::set_max_size_range): Define new member function. (access_ref::add_ofset, access_ref::add_max_ofset): Same. (access_ref::add_base0): New data member. * calls.c (get_size_range): Change argument type. Handle new condition. * calls.h (get_size_range): Adjust signature. (enum size_range_flags): Define new type. * gimple-ssa-warn-restrict.c (builtin_memref::builtin_memref): Correct argument to get_size_range. * tree-ssa-strlen.c (get_range): Handle anti-ranges. (maybe_warn_overflow): Check DECL_P before assuming it's one. gcc/testsuite/ChangeLog: PR middle-end/97342 PR middle-end/97023 PR middle-end/96384 * c-c++-common/Wrestrict.c: Adjust comment. * gcc.dg/Wstringop-overflow-34.c: Remove xfail. * gcc.dg/Wstringop-overflow-43.c: Remove xfails. Adjust regex patterns. * gcc.dg/pr51683.c: Prune out expected warning. * gcc.target/i386/pr60693.c: Same. * g++.dg/warn/Wplacement-new-size-8.C: New test. * gcc.dg/Wstringop-overflow-41.c: New test. * gcc.dg/Wstringop-overflow-44.s: New test. * gcc.dg/Wstringop-overflow-45.c: New test. * gcc.dg/Wstringop-overflow-46.c: New test. * gcc.dg/Wstringop-overflow-47.c: New test. * gcc.dg/Wstringop-overflow-49.c: New test. * gcc.dg/Wstringop-overflow-50.c: New test. * gcc.dg/Wstringop-overflow-51.c: New test. * gcc.dg/Wstringop-overflow-52.c: New test. * gcc.dg/Wstringop-overflow-53.c: New test. * gcc.dg/Wstringop-overflow-54.c: New test. * gcc.dg/Wstringop-overflow-55.c: New test. * gcc.dg/Wstringop-overread-5.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/builtins.c659
-rw-r--r--gcc/builtins.h41
-rw-r--r--gcc/calls.c69
-rw-r--r--gcc/calls.h10
-rw-r--r--gcc/gimple-ssa-warn-restrict.c2
-rw-r--r--gcc/testsuite/c-c++-common/Wrestrict.c13
-rw-r--r--gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C147
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-34.c2
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-41.c120
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-43.c9
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-44.s271
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-45.c255
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-46.c97
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-47.c69
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-49.c146
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-50.c125
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-51.c34
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-52.c62
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-53.c116
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-54.c103
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-55.c97
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overread-5.c76
-rw-r--r--gcc/testsuite/gcc.dg/pr51683.c3
-rw-r--r--gcc/testsuite/gcc.target/i386/pr60693.c3
-rw-r--r--gcc/tree-ssa-strlen.c14
25 files changed, 2339 insertions, 204 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 283c1e6..3a77da2 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -183,8 +183,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function);
static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
static void maybe_emit_free_warning (tree);
static tree fold_builtin_object_size (tree, tree);
-static bool get_range (tree, gimple *, signop, offset_int[2],
- range_query * = NULL);
static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
unsigned HOST_WIDE_INT target_newline;
@@ -200,7 +198,7 @@ static void expand_builtin_sync_synchronize (void);
access_ref::access_ref (tree bound /* = NULL_TREE */,
bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), trail1special (true)
+: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true)
{
/* Set to valid. */
offrng[0] = offrng[1] = 0;
@@ -215,7 +213,7 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
set the bounds of the access to reflect both it and MINACCESS.
BNDRNG[0] is the size of the minimum access. */
tree rng[2];
- if (bound && get_size_range (bound, rng, true))
+ if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
{
bndrng[0] = wi::to_offset (rng[0]);
bndrng[1] = wi::to_offset (rng[1]);
@@ -223,6 +221,131 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
}
}
+/* Return the maximum amount of space remaining and if non-null, set
+ argument to the minimum. */
+
+offset_int
+access_ref::size_remaining (offset_int *pmin /* = NULL */) const
+{
+ offset_int minbuf;
+ if (!pmin)
+ pmin = &minbuf;
+
+ /* add_offset() ensures the offset range isn't inverted. */
+ gcc_checking_assert (offrng[0] <= offrng[1]);
+
+ if (base0)
+ {
+ /* The offset into referenced object is zero-based (i.e., it's
+ not referenced by a pointer into middle of some unknown object). */
+ if (offrng[0] < 0 && offrng[1] < 0)
+ {
+ /* If the offset is negative the remaining size is zero. */
+ *pmin = 0;
+ return 0;
+ }
+
+ if (sizrng[1] <= offrng[0])
+ {
+ /* If the starting offset is greater than or equal to the upper
+ bound on the size of the object, the space remaining is zero.
+ As a special case, if it's equal, set *PMIN to -1 to let
+ the caller know the offset is valid and just past the end. */
+ *pmin = sizrng[1] == offrng[0] ? -1 : 0;
+ return 0;
+ }
+
+ /* Otherwise return the size minus the lower bound of the offset. */
+ offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+ *pmin = sizrng[0] - or0;
+ return sizrng[1] - or0;
+ }
+
+ /* The offset to the referenced object isn't zero-based (i.e., it may
+ refer to a byte other than the first. The size of such an object
+ is constrained only by the size of the address space (the result
+ of max_object_size()). */
+ if (sizrng[1] <= offrng[0])
+ {
+ *pmin = 0;
+ return 0;
+ }
+
+ offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+ *pmin = sizrng[0] - or0;
+ return sizrng[1] - or0;
+}
+
+/* Add the range [MIN, MAX] to the offset range. For known objects (with
+ zero-based offsets) at least one of whose offset's bounds is in range,
+ constrain the other (or both) to the bounds of the object (i.e., zero
+ and the upper bound of its size). This improves the quality of
+ diagnostics. */
+
+void access_ref::add_offset (const offset_int &min, const offset_int &max)
+{
+ if (min <= max)
+ {
+ /* To add an ordinary range just add it to the bounds. */
+ offrng[0] += min;
+ offrng[1] += max;
+ }
+ else if (!base0)
+ {
+ /* To add an inverted range to an offset to an unknown object
+ expand it to the maximum. */
+ add_max_offset ();
+ return;
+ }
+ else
+ {
+ /* To add an inverted range to an offset to an known object set
+ the upper bound to the maximum representable offset value
+ (which may be greater than MAX_OBJECT_SIZE).
+ The lower bound is either the sum of the current offset and
+ MIN when abs(MAX) is greater than the former, or zero otherwise.
+ Zero because then then inverted range includes the negative of
+ the lower bound. */
+ offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+ offrng[1] = maxoff;
+
+ if (max >= 0)
+ {
+ offrng[0] = 0;
+ return;
+ }
+
+ offrng[1] = maxoff;
+ offset_int absmax = wi::abs (max);
+ if (offrng[0] < absmax)
+ offrng[0] += min;
+ else
+ offrng[0] = 0;
+ }
+
+ if (!base0)
+ return;
+
+ /* When referencing a known object check to see if the offset computed
+ so far is in bounds... */
+ offset_int remrng[2];
+ remrng[1] = size_remaining (remrng);
+ if (remrng[1] > 0 || remrng[0] < 0)
+ {
+ /* ...if so, constrain it so that neither bound exceeds the size of
+ the object. Out of bounds offsets are left unchanged, and, for
+ better or worse, become in bounds later. They should be detected
+ and diagnosed at the point they first become invalid by
+ -Warray-bounds. */
+ if (offrng[0] < 0)
+ offrng[0] = 0;
+ if (offrng[1] > sizrng[1])
+ offrng[1] = sizrng[1];
+ }
+}
+
/* Return true if NAME starts with __builtin_ or __sync_. */
static bool
@@ -3757,15 +3880,24 @@ inform_access (const access_ref &ref, access_mode mode)
if (!ref.ref)
return;
- /* Convert offset range and avoid including a zero range since it isn't
- necessarily meaningful. */
- long long minoff = 0, maxoff = 0;
- if (wi::fits_shwi_p (ref.offrng[0])
- && wi::fits_shwi_p (ref.offrng[1]))
- {
- minoff = ref.offrng[0].to_shwi ();
- maxoff = ref.offrng[1].to_shwi ();
- }
+ /* Convert offset range and avoid including a zero range since it
+ isn't necessarily meaningful. */
+ HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node));
+ HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
+ HOST_WIDE_INT minoff;
+ HOST_WIDE_INT maxoff = diff_max;
+ if (wi::fits_shwi_p (ref.offrng[0]))
+ minoff = ref.offrng[0].to_shwi ();
+ else
+ minoff = ref.offrng[0] < 0 ? diff_min : diff_max;
+
+ if (wi::fits_shwi_p (ref.offrng[1]))
+ maxoff = ref.offrng[1].to_shwi ();
+
+ if (maxoff <= diff_min || maxoff >= diff_max)
+ /* Avoid mentioning an upper bound that's equal to or in excess
+ of the maximum of ptrdiff_t. */
+ maxoff = minoff;
/* Convert size range and always include it since all sizes are
meaningful. */
@@ -3799,23 +3931,27 @@ inform_access (const access_ref &ref, access_mode mode)
sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
}
- else
+ else if (DECL_P (ref.ref))
loc = DECL_SOURCE_LOCATION (ref.ref);
+ else if (EXPR_P (ref.ref) && EXPR_HAS_LOCATION (ref.ref))
+ loc = EXPR_LOCATION (ref.ref);
+ else
+ return;
if (mode == access_read_write || mode == access_write_only)
{
- if (DECL_P (ref.ref))
+ if (allocfn == NULL_TREE)
{
if (minoff == maxoff)
{
if (minoff == 0)
- inform (loc, "destination object %qD", ref.ref);
+ inform (loc, "destination object %qE", ref.ref);
else
- inform (loc, "at offset %lli into destination object %qD",
+ inform (loc, "at offset %wi into destination object %qE",
minoff, ref.ref);
}
else
- inform (loc, "at offset [%lli, %lli] into destination object %qD",
+ inform (loc, "at offset [%wi, %wi] into destination object %qE",
minoff, maxoff, ref.ref);
return;
}
@@ -3827,12 +3963,12 @@ inform_access (const access_ref &ref, access_mode mode)
sizestr, allocfn);
else
inform (loc,
- "at offset %lli into destination object of size %s "
+ "at offset %wi into destination object of size %s "
"allocated by %qE", minoff, sizestr, allocfn);
}
else
inform (loc,
- "at offset [%lli, %lli] into destination object of size %s "
+ "at offset [%wi, %wi] into destination object of size %s "
"allocated by %qE",
minoff, maxoff, sizestr, allocfn);
@@ -3846,11 +3982,11 @@ inform_access (const access_ref &ref, access_mode mode)
if (minoff == 0)
inform (loc, "source object %qD", ref.ref);
else
- inform (loc, "at offset %lli into source object %qD",
+ inform (loc, "at offset %wi into source object %qD",
minoff, ref.ref);
}
else
- inform (loc, "at offset [%lli, %lli] into source object %qD",
+ inform (loc, "at offset [%wi, %wi] into source object %qD",
minoff, maxoff, ref.ref);
return;
}
@@ -3862,12 +3998,12 @@ inform_access (const access_ref &ref, access_mode mode)
sizestr, allocfn);
else
inform (loc,
- "at offset %lli into source object of size %s "
+ "at offset %wi into source object of size %s "
"allocated by %qE", minoff, sizestr, allocfn);
}
else
inform (loc,
- "at offset [%lli, %lli] into source object of size %s "
+ "at offset [%wi, %wi] into source object of size %s "
"allocated by %qE",
minoff, maxoff, sizestr, allocfn);
}
@@ -4152,8 +4288,14 @@ check_access (tree exp, tree dstwrite,
&& range[0]
&& TREE_CODE (slen) == INTEGER_CST
&& tree_int_cst_lt (slen, range[0]));
-
- if (!overread && pad && pad->src.sizrng[1] >= 0 && pad->src.offrng[0] >= 0)
+ /* If none is determined try to get a better answer based on the details
+ in PAD. */
+ if (!overread
+ && pad
+ && pad->src.sizrng[1] >= 0
+ && pad->src.offrng[0] >= 0
+ && (pad->src.offrng[1] < 0
+ || pad->src.offrng[0] <= pad->src.offrng[1]))
{
/* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
PAD is nonnull and BNDRNG is valid. */
@@ -4215,7 +4357,7 @@ check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
tree
gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
- range_query *rvals /* = NULL */)
+ range_query * /* = NULL */)
{
if (!stmt)
return NULL_TREE;
@@ -4267,14 +4409,17 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
if (!rng1)
rng1 = rng1_buf;
+ /* Use maximum precision to avoid overflow below. */
const int prec = ADDR_MAX_PRECISION;
- const tree size_max = TYPE_MAX_VALUE (sizetype);
- if (!get_range (size, stmt, rng1, rvals))
- {
- /* Use the full non-negative range on failure. */
- rng1[0] = wi::zero (prec);
- rng1[1] = wi::to_wide (size_max, prec);
- }
+
+ {
+ tree r[2];
+ /* Determine the largest valid range size, including zero. */
+ if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+ return NULL_TREE;
+ rng1[0] = wi::to_wide (r[0], prec);
+ rng1[1] = wi::to_wide (r[1], prec);
+ }
if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
return fold_convert (sizetype, size);
@@ -4283,26 +4428,24 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
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 (!get_range (n, stmt, rng2, rvals))
- {
+ {
+ tree r[2];
/* As above, use the full non-negative range on failure. */
- rng2[0] = wi::zero (prec);
- rng2[1] = wi::to_wide (size_max, prec);
- }
-
- /* Extend to the maximum precision to avoid overflow. */
- 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);
+ if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+ return NULL_TREE;
+ rng2[0] = wi::to_wide (r[0], prec);
+ rng2[1] = wi::to_wide (r[1], prec);
+ }
/* 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];
+
+ const 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);
+ rng1[1] = wi::to_wide (size_max, prec);
return size_max;
}
@@ -4315,7 +4458,8 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
Return the function parameter on success and null otherwise. */
tree
-gimple_parm_array_size (tree ptr, wide_int rng[2], range_query * /* = NULL */)
+gimple_parm_array_size (tree ptr, wide_int rng[2],
+ range_query * /* = NULL */)
{
/* For a function argument try to determine the byte size of the array
from the current function declaratation (e.g., attribute access or
@@ -4363,27 +4507,43 @@ gimple_parm_array_size (tree ptr, wide_int rng[2], range_query * /* = NULL */)
return var;
}
-/* Wrapper around the wide_int overload of get_range. Returns the same
- result but accepts offset_int instead. */
+/* Wrapper around the wide_int overload of get_range that accepts
+ offset_int instead. For middle end expressions returns the same
+ result. For a subset of nonconstamt expressions emitted by the front
+ end determines a more precise range than would be possible otherwise. */
static bool
-get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
- range_query *rvals /* = NULL */)
+get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
{
- tree type = TREE_TYPE (x);
- if (TREE_CODE (x) != INTEGER_CST
- && TREE_CODE (x) != SSA_NAME)
+ offset_int add = 0;
+ if (TREE_CODE (x) == PLUS_EXPR)
{
- if (TYPE_UNSIGNED (type))
+ /* Handle constant offsets in pointer addition expressions seen
+ n the front end IL. */
+ tree op = TREE_OPERAND (x, 1);
+ if (TREE_CODE (op) == INTEGER_CST)
{
- if (sgn == SIGNED)
- type = signed_type_for (type);
+ op = fold_convert (signed_type_for (TREE_TYPE (op)), op);
+ add = wi::to_offset (op);
+ x = TREE_OPERAND (x, 0);
}
- else if (sgn == UNSIGNED)
- type = unsigned_type_for (type);
+ }
+
+ if (TREE_CODE (x) == NOP_EXPR)
+ /* Also handle conversions to sizetype seen in the front end IL. */
+ x = TREE_OPERAND (x, 0);
+
+ tree type = TREE_TYPE (x);
- r[0] = wi::to_offset (TYPE_MIN_VALUE (type));
- r[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+ if (TREE_CODE (x) != INTEGER_CST
+ && TREE_CODE (x) != SSA_NAME)
+ {
+ if (TYPE_UNSIGNED (type)
+ && TYPE_PRECISION (type) == TYPE_PRECISION (sizetype))
+ type = signed_type_for (type);
+
+ r[0] = wi::to_offset (TYPE_MIN_VALUE (type)) + add;
+ r[1] = wi::to_offset (TYPE_MAX_VALUE (type)) + add;
return x;
}
@@ -4391,6 +4551,7 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
if (!get_range (x, stmt, wr, rvals))
return false;
+ signop sgn = SIGNED;
/* Only convert signed integers or unsigned sizetype to a signed
offset and avoid converting large positive values in narrower
types to negative offsets. */
@@ -4403,6 +4564,83 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
return true;
}
+/* Return the argument that the call STMT to a built-in function returns
+ or null if it doesn't. On success, set OFFRNG[] to the range of offsets
+ from the argument reflected in the value returned by the built-in if it
+ can be determined, otherwise to 0 and HWI_M1U respectively. */
+
+static tree
+gimple_call_return_array (gimple *stmt, offset_int offrng[2],
+ range_query *rvals)
+{
+ if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+ || gimple_call_num_args (stmt) < 1)
+ return NULL_TREE;
+
+ tree fn = gimple_call_fndecl (stmt);
+ switch (DECL_FUNCTION_CODE (fn))
+ {
+ case BUILT_IN_MEMCPY:
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMMOVE:
+ case BUILT_IN_MEMMOVE_CHK:
+ case BUILT_IN_MEMSET:
+ case BUILT_IN_STPCPY:
+ case BUILT_IN_STPCPY_CHK:
+ case BUILT_IN_STPNCPY:
+ case BUILT_IN_STPNCPY_CHK:
+ case BUILT_IN_STRCAT:
+ case BUILT_IN_STRCAT_CHK:
+ case BUILT_IN_STRCPY:
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STRNCAT:
+ case BUILT_IN_STRNCAT_CHK:
+ case BUILT_IN_STRNCPY:
+ case BUILT_IN_STRNCPY_CHK:
+ offrng[0] = offrng[1] = 0;
+ return gimple_call_arg (stmt, 0);
+
+ case BUILT_IN_MEMPCPY:
+ case BUILT_IN_MEMPCPY_CHK:
+ {
+ tree off = gimple_call_arg (stmt, 2);
+ if (!get_offset_range (off, stmt, offrng, rvals))
+ {
+ offrng[0] = 0;
+ offrng[1] = HOST_WIDE_INT_M1U;
+ }
+ return gimple_call_arg (stmt, 0);
+ }
+
+ case BUILT_IN_MEMCHR:
+ {
+ tree off = gimple_call_arg (stmt, 2);
+ if (get_offset_range (off, stmt, offrng, rvals))
+ offrng[0] = 0;
+ else
+ {
+ offrng[0] = 0;
+ offrng[1] = HOST_WIDE_INT_M1U;
+ }
+ return gimple_call_arg (stmt, 0);
+ }
+
+ case BUILT_IN_STRCHR:
+ case BUILT_IN_STRRCHR:
+ case BUILT_IN_STRSTR:
+ {
+ offrng[0] = 0;
+ offrng[1] = HOST_WIDE_INT_M1U;
+ }
+ return gimple_call_arg (stmt, 0);
+
+ default:
+ break;
+ }
+
+ return NULL_TREE;
+}
+
/* Helper to compute the size of the object referenced by the PTR
expression which must have pointer type, using Object Size type
OSTYPE (only the least significant 2 bits are used).
@@ -4412,7 +4650,8 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
the object(s).
VISITED is used to avoid visiting the same PHI operand multiple
times, and, when nonnull, RVALS to determine range information.
- Returns true on success, false when the size cannot be determined.
+ Returns true on success, false when a meaningful size (or range)
+ cannot be determined.
The function is intended for diagnostics and should not be used
to influence code generation or optimization. */
@@ -4429,25 +4668,40 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (DECL_P (ptr))
{
- /* Bail if the reference is to the pointer itself (as opposed
- to what it points to). */
+ pref->ref = ptr;
+
if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
- return false;
+ {
+ /* Set the maximum size if the reference is to the pointer
+ itself (as opposed to what it points to). */
+ pref->set_max_size_range ();
+ return true;
+ }
- pref->ref = ptr;
if (tree size = decl_init_size (ptr, false))
if (TREE_CODE (size) == INTEGER_CST)
{
pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
return true;
}
- pref->sizrng[0] = 0;
- pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+
+ pref->set_max_size_range ();
return true;
}
const tree_code code = TREE_CODE (ptr);
+ if (code == BIT_FIELD_REF)
+ {
+ tree ref = TREE_OPERAND (ptr, 0);
+ if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ return false;
+
+ offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
+ pref->add_offset (off / BITS_PER_UNIT);
+ return true;
+ }
+
if (code == COMPONENT_REF)
{
tree ref = TREE_OPERAND (ptr, 0);
@@ -4455,27 +4709,29 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (ostype == 0)
{
- /* For raw memory functions like memcpy bail if the size
- of the enclosing object cannot be determined. */
- if (!compute_objsize (ref, ostype, pref, visited, rvals)
- || !pref->ref)
+ /* In OSTYPE zero (for raw memory functions like memcpy), use
+ the maximum size instead if the identity of the enclosing
+ object cannot be determined. */
+ if (!compute_objsize (ref, ostype, pref, visited, rvals))
return false;
/* Otherwise, use the size of the enclosing object and add
the offset of the member to the offset computed so far. */
tree offset = byte_position (field);
- if (TREE_CODE (offset) != INTEGER_CST)
- return false;
- offset_int off = wi::to_offset (offset);
- pref->offrng[0] += off;
- pref->offrng[1] += off;
+ if (TREE_CODE (offset) == INTEGER_CST)
+ pref->add_offset (wi::to_offset (offset));
+ else
+ pref->add_max_offset ();
return true;
}
- /* Bail if the reference is to the pointer itself (as opposed
- to what it points to). */
if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
- return false;
+ {
+ /* Set maximum size if the reference is to the pointer member
+ itself (as opposed to what it points to). */
+ pref->set_max_size_range ();
+ return true;
+ }
pref->ref = field;
@@ -4530,9 +4786,12 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
offset_int orng[2];
tree off = pref->eval (TREE_OPERAND (ptr, 1));
- if (!get_range (off, NULL, SIGNED, orng, rvals))
- /* Fail unless the size of the object is zero. */
- return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
+ if (!get_offset_range (off, NULL, orng, rvals))
+ {
+ /* Set ORNG to the maximum offset representable in ptrdiff_t. */
+ orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+ orng[0] = -orng[1] - 1;
+ }
if (TREE_CODE (ptr) == ARRAY_REF)
{
@@ -4551,7 +4810,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
tree eltype = TREE_TYPE (ptr);
tree tpsize = TYPE_SIZE_UNIT (eltype);
if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
- return false;
+ {
+ pref->add_max_offset ();
+ return true;
+ }
offset_int sz = wi::to_offset (tpsize);
orng[0] *= sz;
@@ -4578,12 +4840,44 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
}
}
- pref->offrng[0] += orng[0];
- pref->offrng[1] += orng[1];
+ pref->add_offset (orng[0], orng[1]);
+ return true;
+ }
+ if (code == TARGET_MEM_REF)
+ {
+ tree ref = TREE_OPERAND (ptr, 0);
+ if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ return false;
+
+ /* TODO: Handle remaining operands. Until then, add maximum offset. */
+ pref->ref = ptr;
+ pref->add_max_offset ();
+ return true;
+ }
+
+ if (code == INTEGER_CST)
+ {
+ /* Pointer constants other than null are most likely the result
+ of erroneous null pointer addition/subtraction. Set size to
+ zero. For null pointers, set size to the maximum for now
+ since those may be the result of jump threading. */
+ if (integer_zerop (ptr))
+ pref->set_max_size_range ();
+ else
+ pref->sizrng[0] = pref->sizrng[1] = 0;
+ pref->ref = ptr;
+
+ return true;
+ }
+
+ if (code == STRING_CST)
+ {
+ pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
return true;
}
- else if (code == POINTER_PLUS_EXPR)
+
+ if (code == POINTER_PLUS_EXPR)
{
tree ref = TREE_OPERAND (ptr, 0);
if (!compute_objsize (ref, ostype, pref, visited, rvals))
@@ -4591,16 +4885,14 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
offset_int orng[2];
tree off = pref->eval (TREE_OPERAND (ptr, 1));
- if (!get_range (off, NULL, SIGNED, orng, rvals))
- /* Fail unless the size of the object is zero. */
- return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
-
- pref->offrng[0] += orng[0];
- pref->offrng[1] += orng[1];
-
+ if (get_offset_range (off, NULL, orng, rvals))
+ pref->add_offset (orng[0], orng[1]);
+ else
+ pref->add_max_offset ();
return true;
}
- else if (code == VIEW_CONVERT_EXPR)
+
+ if (code == VIEW_CONVERT_EXPR)
{
ptr = TREE_OPERAND (ptr, 0);
return compute_objsize (ptr, ostype, pref, visited, rvals);
@@ -4612,17 +4904,53 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
if (is_gimple_call (stmt))
{
/* If STMT is a call to an allocation function get the size
- from its argument(s). If successful, also set *PDECL to
- PTR for the caller to include in diagnostics. */
+ from its argument(s). If successful, also set *PREF->REF
+ to PTR for the caller to include in diagnostics. */
wide_int wr[2];
if (gimple_call_alloc_size (stmt, wr, rvals))
{
pref->ref = ptr;
pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
- return true;
+ /* Constrain both bounds to a valid size. */
+ offset_int maxsize = wi::to_offset (max_object_size ());
+ if (pref->sizrng[0] > maxsize)
+ pref->sizrng[0] = maxsize;
+ if (pref->sizrng[1] > maxsize)
+ pref->sizrng[1] = maxsize;
}
- return false;
+ else
+ {
+ /* For functions known to return one of their pointer arguments
+ try to determine what the returned pointer points to, and on
+ success add OFFRNG which was set to the offset added by
+ the function (e.g., memchr) to the overall offset. */
+ offset_int offrng[2];
+ if (tree ret = gimple_call_return_array (stmt, offrng, rvals))
+ {
+ if (!compute_objsize (ret, ostype, pref, visited, rvals))
+ return false;
+
+ /* Cap OFFRNG[1] to at most the remaining size of
+ the object. */
+ offset_int remrng[2];
+ remrng[1] = pref->size_remaining (remrng);
+ if (remrng[1] < offrng[1])
+ offrng[1] = remrng[1];
+ pref->add_offset (offrng[0], offrng[1]);
+ }
+ else
+ {
+ /* For other calls that might return arbitrary pointers
+ including into the middle of objects set the size
+ range to maximum, clear PREF->BASE0, and also set
+ PREF->REF to include in diagnostics. */
+ pref->set_max_size_range ();
+ pref->base0 = false;
+ pref->ref = ptr;
+ }
+ }
+ return true;
}
if (gimple_nop_p (stmt))
@@ -4631,62 +4959,74 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
of the array from the current function declaratation
(e.g., attribute access or related). */
wide_int wr[2];
- tree ref = gimple_parm_array_size (ptr, wr, rvals);
- if (!ref)
- return NULL_TREE;
- pref->ref = ref;
- pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
- pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+ if (tree ref = gimple_parm_array_size (ptr, wr, rvals))
+ {
+ pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+ pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+ pref->ref = ref;
+ return true;
+ }
+
+ pref->set_max_size_range ();
+ pref->base0 = false;
+ pref->ref = ptr;
+ if (tree var = SSA_NAME_VAR (ptr))
+ if (TREE_CODE (var) == PARM_DECL)
+ pref->ref = var;
+
return true;
}
/* TODO: Handle PHI. */
if (!is_gimple_assign (stmt))
- return false;
+ {
+ /* Clear BASE0 since the assigned pointer might point into
+ the middle of the object, set the maximum size range and,
+ if the SSA_NAME refers to a function argumnent, set
+ PREF->REF to it. */
+ pref->base0 = false;
+ pref->set_max_size_range ();
+ if (tree var = SSA_NAME_VAR (ptr))
+ if (TREE_CODE (var) == PARM_DECL)
+ pref->ref = var;
+ return true;
+ }
ptr = gimple_assign_rhs1 (stmt);
tree_code code = gimple_assign_rhs_code (stmt);
- if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
- /* Avoid conversions from non-pointers. */
- return false;
- if (code == POINTER_PLUS_EXPR)
+ if (code == POINTER_PLUS_EXPR
+ && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
{
- /* If the the offset in the expression can be determined use
- it to adjust the overall offset. Otherwise, set the overall
- offset to the maximum. */
+ /* Compute the size of the object first. */
+ if (!compute_objsize (ptr, ostype, pref, visited, rvals))
+ return false;
+
offset_int orng[2];
tree off = gimple_assign_rhs2 (stmt);
- if (!get_range (off, stmt, SIGNED, orng, rvals))
- {
- orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
- orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
- }
-
- pref->offrng[0] += orng[0];
- pref->offrng[1] += orng[1];
+ if (get_offset_range (off, stmt, orng, rvals))
+ pref->add_offset (orng[0], orng[1]);
+ else
+ pref->add_max_offset ();
+ return true;
}
- else if (code != ADDR_EXPR)
- return false;
- return compute_objsize (ptr, ostype, pref, visited, rvals);
- }
-
- tree type = TREE_TYPE (ptr);
- type = TYPE_MAIN_VARIANT (type);
- if (TREE_CODE (ptr) == ADDR_EXPR)
- ptr = TREE_OPERAND (ptr, 0);
+ if (code == ADDR_EXPR)
+ return compute_objsize (ptr, ostype, pref, visited, rvals);
- if (TREE_CODE (type) == ARRAY_TYPE
- && !array_at_struct_end_p (ptr))
- {
- if (tree size = TYPE_SIZE_UNIT (type))
- return get_range (size, NULL, UNSIGNED, pref->sizrng, rvals);
+ /* This could be an assignment from a nonlocal pointer. Save PTR
+ to mention in diagnostics but otherwise treat it as a pointer
+ to an unknown object. */
+ pref->ref = ptr;
}
- return false;
+ /* Assume all other expressions point into an unknown object
+ of the maximum valid size. */
+ pref->base0 = false;
+ pref->set_max_size_range ();
+ return true;
}
/* A "public" wrapper around the above. Clients should use this overload
@@ -4707,27 +5047,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
if (!success)
return NULL_TREE;
- if (pref->offrng[1] < pref->offrng[0])
- {
- if (pref->offrng[1] < 0
- && pref->sizrng[1] <= pref->offrng[0])
- return size_zero_node;
-
- return wide_int_to_tree (sizetype, pref->sizrng[1]);
- }
-
- if (pref->offrng[0] < 0)
- {
- if (pref->offrng[1] < 0)
- return size_zero_node;
-
- pref->offrng[0] = 0;
- }
-
- if (pref->sizrng[1] <= pref->offrng[0])
- return size_zero_node;
-
- return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]);
+ offset_int maxsize = pref->size_remaining ();
+ if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
+ pref->offrng[0] = 0;
+ return wide_int_to_tree (sizetype, maxsize);
}
/* Transitional wrapper around the above. The function should be removed
@@ -4735,13 +5058,13 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
tree
compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
- tree *poff /* = NULL */, class range_query *rvals /* = NULL */)
+ tree *poff /* = NULL */, range_query *rvals /* = NULL */)
{
/* Set the initial offsets to zero and size to negative to indicate
none has been computed yet. */
access_ref ref;
tree size = compute_objsize (ptr, ostype, &ref, rvals);
- if (!size)
+ if (!size || !ref.base0)
return NULL_TREE;
if (pdecl)
@@ -6020,13 +6343,21 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
tree size2 = compute_objsize (arg2, 1, &ref2);
tree func = get_callee_fndecl (exp);
- if (size1 && size2)
+ if (size1 && size2 && bndrng[0] && !integer_zerop (bndrng[0]))
{
- tree maxsize = tree_int_cst_le (size1, size2) ? size2 : size1;
-
- if (tree_int_cst_lt (maxsize, bndrng[0]))
+ offset_int rem1 = ref1.size_remaining ();
+ offset_int rem2 = ref2.size_remaining ();
+ if (rem1 == 0 || rem2 == 0)
maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
- bndrng, maxsize);
+ bndrng, integer_zero_node);
+ else
+ {
+ offset_int maxrem = wi::max (rem1, rem2, UNSIGNED);
+ if (maxrem < wi::to_offset (bndrng[0]))
+ maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp,
+ func, bndrng,
+ wide_int_to_tree (sizetype, maxrem));
+ }
}
else if (bndrng[0]
&& !integer_zerop (bndrng[0])
diff --git a/gcc/builtins.h b/gcc/builtins.h
index f226c63..c09f36d 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -180,15 +180,45 @@ struct access_ref
return offrng[0] == 0 && offrng[1] == 0;
}
- /* Return true if OFFRNG is bounded to a subrange of possible offset
- values. */
+ /* Return true if OFFRNG is bounded to a subrange of offset values
+ valid for the largest possible object. */
bool offset_bounded () const;
+ /* Return the maximum amount of space remaining and if non-null, set
+ argument to the minimum. */
+ offset_int size_remaining (offset_int * = NULL) const;
+
+ /* Set the size range to the maximum. */
+ void set_max_size_range ()
+ {
+ sizrng[0] = 0;
+ sizrng[1] = wi::to_offset (max_object_size ());
+ }
+
+ /* Add OFF to the offset range. */
+ void add_offset (const offset_int &off)
+ {
+ add_offset (off, off);
+ }
+
+ /* Add the range [MIN, MAX] to the offset range. */
+ void add_offset (const offset_int &, const offset_int &);
+
+ /* Add the maximum representable offset to the offset range. */
+ void add_max_offset ()
+ {
+ offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+ add_offset (-maxoff - 1, maxoff);
+ }
+
/* Used to fold integer expressions when called from front ends. */
tree (*eval)(tree);
/* Set if trailing one-element arrays should be treated as flexible
array members. */
bool trail1special;
+ /* Set if valid offsets must start at zero (for declared and allocated
+ objects but not for others referenced by pointers). */
+ bool base0;
};
/* Describes a pair of references used in an access by built-in
@@ -216,11 +246,10 @@ class range_query;
extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
range_query * = NULL);
extern tree gimple_parm_array_size (tree, wide_int[2], range_query * = NULL);
+extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
range_query * = NULL);
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
-
-extern bool check_access (tree, tree, tree, tree, tree, access_mode,
- const access_data * = NULL);
+extern bool check_access (tree, tree, tree, tree, tree,
+ access_mode, const access_data * = NULL);
#endif /* GCC_BUILTINS_H */
diff --git a/gcc/calls.c b/gcc/calls.c
index 93da3d6..d3120b2 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1235,14 +1235,16 @@ alloc_max_size (void)
after adjusting it if necessary to make EXP a represents a valid size
of object, or a valid size argument to an allocation function declared
with attribute alloc_size (whose argument may be signed), or to a string
- manipulation function like memset. When ALLOW_ZERO is true, allow
- returning a range of [0, 0] for a size in an anti-range [1, N] where
- N > PTRDIFF_MAX. A zero range is a (nearly) invalid argument to
- allocation functions like malloc but it is a valid argument to
- functions like memset. */
+ manipulation function like memset.
+ When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
+ a size in an anti-range [1, N] where N > PTRDIFF_MAX. A zero range is
+ a (nearly) invalid argument to allocation functions like malloc but it
+ is a valid argument to functions like memset.
+ When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
+ in a multi-range, otherwise to the smallest valid subrange. */
bool
-get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
+get_size_range (tree exp, tree range[2], int flags /* = 0 */)
{
if (!exp)
return false;
@@ -1314,25 +1316,50 @@ get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
min = wi::zero (expprec);
}
}
- else if (wi::eq_p (0, min - 1))
+ else
{
- /* EXP is unsigned and not in the range [1, MAX]. That means
- it's either zero or greater than MAX. Even though 0 would
- normally be detected by -Walloc-zero, unless ALLOW_ZERO
- is true, set the range to [MAX, TYPE_MAX] so that when MAX
- is greater than the limit the whole range is diagnosed. */
- if (allow_zero)
- min = max = wi::zero (expprec);
- else
+ wide_int maxsize = wi::to_wide (max_object_size ());
+ min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
+ max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
+ if (wi::eq_p (0, min - 1))
{
+ /* EXP is unsigned and not in the range [1, MAX]. That means
+ it's either zero or greater than MAX. Even though 0 would
+ normally be detected by -Walloc-zero, unless ALLOW_ZERO
+ is set, set the range to [MAX, TYPE_MAX] so that when MAX
+ is greater than the limit the whole range is diagnosed. */
+ wide_int maxsize = wi::to_wide (max_object_size ());
+ if (flags & SR_ALLOW_ZERO)
+ {
+ if (wi::leu_p (maxsize, max + 1)
+ || !(flags & SR_USE_LARGEST))
+ min = max = wi::zero (expprec);
+ else
+ {
+ min = max + 1;
+ max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+ }
+ }
+ else
+ {
+ min = max + 1;
+ max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+ }
+ }
+ else if ((flags & SR_USE_LARGEST)
+ && wi::ltu_p (max + 1, maxsize))
+ {
+ /* When USE_LARGEST is set and the larger of the two subranges
+ is a valid size, use it... */
min = max + 1;
- max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+ max = maxsize;
+ }
+ else
+ {
+ /* ...otherwise use the smaller subrange. */
+ max = min - 1;
+ min = wi::zero (expprec);
}
- }
- else
- {
- max = min - 1;
- min = wi::zero (expprec);
}
}
diff --git a/gcc/calls.h b/gcc/calls.h
index dfb951c..644ec45 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -133,7 +133,15 @@ extern bool reference_callee_copied (CUMULATIVE_ARGS *,
extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
extern tree get_attr_nonstring_decl (tree, tree * = NULL);
extern bool maybe_warn_nonstring_arg (tree, tree);
-extern bool get_size_range (tree, tree[2], bool = false);
+enum size_range_flags
+ {
+ /* Set to consider zero a valid range. */
+ SR_ALLOW_ZERO = 1,
+ /* Set to use the largest subrange of a set of ranges as opposed
+ to the smallest. */
+ SR_USE_LARGEST = 2
+ };
+extern bool get_size_range (tree, tree[2], int = 0);
extern rtx rtx_for_static_chain (const_tree, bool);
extern bool cxx17_empty_base_field_p (const_tree);
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index 512fc13..e2734c8 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -264,7 +264,7 @@ builtin_memref::builtin_memref (tree expr, tree size)
tree range[2];
/* Determine the size range, allowing for the result to be [0, 0]
for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX. */
- get_size_range (size, range, true);
+ get_size_range (size, range, SR_ALLOW_ZERO);
sizrange[0] = wi::to_offset (range[0]);
sizrange[1] = wi::to_offset (range[1]);
/* get_size_range returns SIZE_MAX for the maximum size.
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
index 3b019c8..9eb02bd 100644
--- a/gcc/testsuite/c-c++-common/Wrestrict.c
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -320,14 +320,17 @@ void test_memcpy_anti_range (char *d, const char *s)
T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX)); /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" "memcpy" } */
- /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
- doesn't trigger a warning. */
+ /* Verify that a size in an anti-range ~[1, N] where N >= PTRDIFF_MAX - 2
+ doesn't trigger a warning.
+ With ~[1, PTRDIFF_MAX - 1] the difference between the just-past-the-end
+ pointer to A and A for char A[PTRDIFF_MAX] wouldn't be representable in
+ ptrdiff_t. Since such a large object cannot exist, so the size of
+ the region must be zero. */
+ T (d, s, UAR (1, DIFF_MAX / 2 - 1));
T (d, s, UAR (1, DIFF_MAX - 1));
T (d, s, UAR (1, DIFF_MAX));
T (d, s, UAR (1, SIZE_MAX - 1));
-
- /* This causes the last dg-warning test to fail for some reason.
- T (d, s, UAR (1, SIZE_MAX)); */
+ T (d, s, UAR (1, SIZE_MAX));
}
/* Verify calls to memcpy() where the combination of offsets in some
diff --git a/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C b/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C
new file mode 100644
index 0000000..a0d1362
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C
@@ -0,0 +1,147 @@
+/* Verify informational notes following the warning.
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+#define DISS_MAX __PTRDIFF_MAX__
+#define SIZE_MAX __SIZE_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+template <int N> struct S { char a[N]; };
+
+void* operator new (size_t, void *p) { return p; }
+void* operator new[] (size_t, void *p) { return p; }
+
+
+void test_cst_off ()
+{
+ {
+ char ca0[0]; // { dg-message "'ca0' declared here" "note" }
+ new (ca0 + 0) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+ }
+ {
+ char ca1[1];
+ new (ca1 + 0) S<1>;
+ }
+ {
+ char ca1[1]; // { dg-message "'ca1' declared here" "note" }
+ new (ca1 + 0) S<2>; // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size '1'" }
+ }
+ {
+ char ca1[1]; // { dg-message "at offset 1 from 'ca1' declared here" "note" }
+ new (ca1 + 1) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+ }
+ {
+ char ca1[1]; // { dg-message "at offset 2 from 'ca1' declared here" "note" }
+ new (ca1 + 2) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+ }
+ {
+ char ca1[1]; // { dg-message "at offset -1 from 'ca1' declared here" "note" }
+ new (ca1 - 1) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+ }
+ {
+ /* Offsets are treated as signed so SIZE_MAX is indistinguishable
+ from -1. */
+ char ca1[1]; // { dg-message "at offset \\d+ from 'ca1' declared here" "note" { xfail *-*-* } }
+ // { dg-message "at offset -1 from 'ca1' declared here" "note" { target *-*-* } .-1 }
+ new (ca1 + SIZE_MAX) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+ }
+}
+
+
+/* Verify that the range of the offset included in the note corresponds
+ to the range of its type (plus the optional constant). */
+
+void test_var_off_uchar (unsigned char i)
+{
+ {
+ // Verify that the nore doesn't mention an offset.
+ char ca0[0]; // { dg-message ": 'ca0' declared here" "note" }
+ new (ca0 + i) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+ }
+ {
+ char ca1[1];
+ new (ca1 + i) S<1>;
+ }
+ {
+ // Verify that the nore doesn't mention an offset.
+ char ca1[1]; // { dg-message ": 'ca1' declared here" "note" }
+ new (ca1 + i) S<2>; // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size at most '1'" }
+ }
+ {
+ char ca2[2];
+ new (ca2 + i) S<2>;
+ new (ca2 + 1 - i) S<2>;
+ new (ca2 - i + 1) S<2>;
+ new (ca2 - 2 + i) S<2>;
+ new (ca2 - i + 2) S<2>;
+ new (ca2 - i + i) S<2>;
+ new (ca2 + i + i) S<2>;
+ }
+ {
+ char ca2[2]; // { dg-message "at offset \\\[1, 2] from 'ca2' declared here" "note" }
+ new (ca2 + i + 1) S<2>; // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[2]' and size at most '1'" }
+ }
+
+ {
+ char a[65281];
+ new (a + i + 65280) S<1>;
+ }
+ {
+ char a[65281]; // { dg-message "at offset 65281 from 'a' declared here" "note" }
+ new (a + i + 65281) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[65281]' and size '0'" }
+ }
+ {
+ char a[65281]; // { dg-message "at offset \\\[65154, 65281] from 'a' declared here" "note" }
+ new (a + i + 65154) S<128>; // { dg-warning "constructing an object of type 'S<128>' and size '128' in a region of type 'char \\\[65281]' and size at most '127'" }
+ }
+}
+
+
+/* Same as above but also verify that the signedness of the offset is
+ considered in the issuing the warning. */
+
+void test_var_off_schar (signed char i)
+{
+ {
+ // Verify that the nore doesn't mention an offset.
+ char ca0[0]; // { dg-message ": 'ca0' declared here" "note" }
+ new (ca0 + i) S<1>; // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+ }
+ {
+ char ca1[1];
+ new (ca1 + i) S<1>;
+ new (ca1 - i) S<1>;
+ new (ca1 + i + 1) S<1>;
+ new (ca1 - i + 1) S<1>;
+ new (ca1 + i + i) S<1>;
+ new (ca1 - i - i) S<1>;
+ }
+ {
+ // Verify that the nore doesn't mention an offset.
+ char ca1[1]; // { dg-message ": 'ca1' declared here" "note" }
+ new (ca1 + i) S<2>; // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size at most '1'" }
+ }
+ {
+ char ca2[2];
+ new (ca2 + i) S<2>;
+ new (ca2 + 1 - i) S<2>;
+ new (ca2 - i + 1) S<2>;
+ new (ca2 - 2 + i) S<2>;
+ new (ca2 - i + 2) S<2>;
+ new (ca2 - i + i) S<2>;
+ new (ca2 + i + i) S<2>;
+ }
+ {
+ char ca2[2];
+ new (ca2 + i + 1) S<2>;
+ }
+
+ {
+ char a[65281]; // { dg-message "at offset \\\[65153, 65408] from 'a'" }
+ new (a + i + 65280) S<1>;
+ new (a + i + 65281) S<1>;
+ new (a + i + 65281) S<128>;
+ new (a + i + 65281) S<129>; // { dg-warning "constructing an object of type 'S<129>' and size '129' in a region of type 'char \\\[65281]' and size at most '128'" }
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
index fd43f3a..a1b1039 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
@@ -112,7 +112,7 @@ void s2_warn_cstoff_cstidx (struct S2 *p)
void s2_warn_varoff_cstdix (struct S2 *p, int i)
{
char *q = p->a + i;
- q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+ q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
}
void s2_warn_cstoff_varidx (struct S2 *p, int i)
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c
new file mode 100644
index 0000000..9b2d2cb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c
@@ -0,0 +1,120 @@
+/* Verify that writes at excessive offsets into declared or allocated
+ objects of unknown size are diagnosed.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+
+void char_array_cst_off_cst_size (void)
+{
+ extern char caxcc[]; // { dg-message "at offset \\d+ into destination object 'caxcc'" }
+
+ char *p = caxcc;
+ size_t idx = DIFF_MAX - 3;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
+ sink (p);
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 1" "pr?????" { xfail ilp32 } }
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 0" }
+ sink (p);
+}
+
+
+void char_array_var_off_cst_size (size_t idx)
+{
+ /* The offset is a range with a very large lower bound and an upper
+ bound of DIFF_MAX. There's not point in also mentioning the latter
+ (it wouldn't make the note any more meaningful) so verify it only
+ mentions the lower bound. */
+ extern char caxvc[]; // { dg-message "at offset \\d+ into destination object 'caxvc'" "note" }
+
+ char *p = caxvc;
+
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+ sink (p);
+}
+
+
+void char_array_var_off_var_size (size_t idx, size_t n)
+{
+ extern char caxvv[]; // { dg-message "at offset \\d+ into destination object 'caxvv'" "note" }
+
+ char *p = caxvv;
+
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ if (n < 3 || 7 < n)
+ n = 3;
+
+ memset (p + idx, 0, n);
+ sink (p);
+
+ ++n;
+ memset (p + idx, 0, n); // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+ sink (p);
+}
+
+
+void alloc_array_var_off_cst_size (size_t n, size_t idx)
+{
+ char *p = malloc (n); // { dg-message "at offset \\d+ into destination object" "note" }
+
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+ sink (p);
+}
+
+
+void int_array_cst_off_cst_size (void)
+{
+ extern int iaxc[]; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+
+ int *p = iaxc;
+ size_t idx = DIFF_MAX / sizeof *iaxc;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+ sink (p);
+}
+
+
+void* nowarn_anti_range_1 (char *p, char *q)
+{
+ size_t n = q - p;
+ if (!n) return 0;
+
+ char *d = __builtin_malloc (n + 1);
+ memcpy (d, p, n + 1); // { dg-bogus "-Wstringop-overflow" }
+ return d;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
index 3ac5a88..14ab925 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
@@ -159,9 +159,10 @@ void warn_memset_reversed_range (void)
char *p = &a11[11];
- /* The below is represented as a true anti-range as opposed to a range
- with reversed bounds and the former aren't handled. */
- T1 (p, SAR (INT_MIN, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+ /* Since the offset is excessive, either starting before &a11[0]
+ ot just past &a[11], the region size in the warning should
+ probably be zero, but accept other sizes too. */
+ T1 (p, SAR (INT_MIN, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
/* The following are represented as ordinary ranges with reversed bounds
and those are handled. */
@@ -171,7 +172,7 @@ void warn_memset_reversed_range (void)
T1 (p, SAR (INT_MIN, 1), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail ilp32 } }
T1 (p, SAR (INT_MIN, 0), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
/* Also represented as a true anti-range. */
- T1 (p, SAR ( -12, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+ T1 (p, SAR ( -12, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
T1 (p, SAR ( -12, -1), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
T1 (p, SAR ( -11, 0), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
T1 (p, SAR ( -11, 11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s b/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s
new file mode 100644
index 0000000..0fc73a9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s
@@ -0,0 +1,271 @@
+ .file "Wstringop-overflow-44.c"
+ .text
+ .p2align 4
+ .globl f0
+ .type f0, @function
+f0:
+.LFB0:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+.LFE0:
+ .size f0, .-f0
+ .p2align 4
+ .globl f1
+ .type f1, @function
+f1:
+.LFB1:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+.LFE1:
+ .size f1, .-f1
+ .p2align 4
+ .globl f2
+ .type f2, @function
+f2:
+.LFB2:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L12
+.L4:
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L12:
+ movslq %eax, %rdx
+ movq d(%rip), %rcx
+ testq %rdx, %rdx
+ je .L4
+ xorl %eax, %eax
+.L6:
+ movb $0, (%rcx,%rax)
+ addq $1, %rax
+ cmpq %rdx, %rax
+ jb .L6
+ ret
+ .cfi_endproc
+.LFE2:
+ .size f2, .-f2
+ .p2align 4
+ .globl f3
+ .type f3, @function
+f3:
+.LFB3:
+ .cfi_startproc
+ movslq n(%rip), %rdx
+ testl %edx, %edx
+ jle .L15
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L15:
+ movq %rdi, %rsi
+ movq d(%rip), %rdi
+ jmp strncpy
+ .cfi_endproc
+.LFE3:
+ .size f3, .-f3
+ .p2align 4
+ .globl f4
+ .type f4, @function
+f4:
+.LFB4:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L18
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L18:
+ movq d(%rip), %rax
+ movq %rdi, %rsi
+ movb $0, (%rax)
+ movslq n(%rip), %rdx
+ movq d(%rip), %rdi
+ jmp strncat
+ .cfi_endproc
+.LFE4:
+ .size f4, .-f4
+ .p2align 4
+ .globl g0
+ .type g0, @function
+g0:
+.LFB5:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L25
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L25:
+ subq $24, %rsp
+ .cfi_def_cfa_offset 32
+ leaq 15(%rsp), %rdi
+ call sink
+ addq $24, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE5:
+ .size g0, .-g0
+ .p2align 4
+ .globl g1
+ .type g1, @function
+g1:
+.LFB6:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L32
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L32:
+ subq $24, %rsp
+ .cfi_def_cfa_offset 32
+ leaq 15(%rsp), %rdi
+ call sink
+ addq $24, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE6:
+ .size g1, .-g1
+ .p2align 4
+ .globl g2
+ .type g2, @function
+g2:
+.LFB7:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L45
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L45:
+ movslq %eax, %rdx
+ subq $24, %rsp
+ .cfi_def_cfa_offset 32
+ testq %rdx, %rdx
+ je .L36
+ xorl %eax, %eax
+.L35:
+ movb $0, 15(%rsp,%rax)
+ addq $1, %rax
+ cmpq %rdx, %rax
+ jb .L35
+.L36:
+ leaq 15(%rsp), %rdi
+ call sink
+ addq $24, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE7:
+ .size g2, .-g2
+ .p2align 4
+ .globl g3
+ .type g3, @function
+g3:
+.LFB8:
+ .cfi_startproc
+ movslq n(%rip), %rdx
+ testl %edx, %edx
+ jle .L52
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L52:
+ subq $24, %rsp
+ .cfi_def_cfa_offset 32
+ movq %rdi, %rsi
+ leaq 15(%rsp), %rdi
+ call strncpy
+ leaq 15(%rsp), %rdi
+ call sink
+ addq $24, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE8:
+ .size g3, .-g3
+ .p2align 4
+ .globl g4
+ .type g4, @function
+g4:
+.LFB9:
+ .cfi_startproc
+ movslq n(%rip), %rdx
+ testl %edx, %edx
+ jle .L59
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L59:
+ subq $24, %rsp
+ .cfi_def_cfa_offset 32
+ movq %rdi, %rsi
+ leaq 15(%rsp), %rdi
+ movb $0, 15(%rsp)
+ call strncat
+ leaq 15(%rsp), %rdi
+ call sink
+ addq $24, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE9:
+ .size g4, .-g4
+ .p2align 4
+ .globl h0
+ .type h0, @function
+h0:
+.LFB10:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L66
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L66:
+ subq $8, %rsp
+ .cfi_def_cfa_offset 16
+ movl $1, %edi
+ call malloc
+ movq %rax, d(%rip)
+ addq $8, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE10:
+ .size h0, .-h0
+ .p2align 4
+ .globl h1
+ .type h1, @function
+h1:
+.LFB16:
+ .cfi_startproc
+ movl n(%rip), %eax
+ testl %eax, %eax
+ jle .L73
+ ret
+ .p2align 4,,10
+ .p2align 3
+.L73:
+ subq $8, %rsp
+ .cfi_def_cfa_offset 16
+ movl $1, %edi
+ call malloc
+ movq %rax, d(%rip)
+ addq $8, %rsp
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE16:
+ .size h1, .-h1
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c
new file mode 100644
index 0000000..112d79a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c
@@ -0,0 +1,255 @@
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+ Verify that out of bounds writes by built-ins to objects through pointers
+ returned by other built-ins are diagnosed.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+#include "range.h"
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+
+void sink (void*, ...);
+
+
+void nowarn_memcpy (const void *s)
+{
+ extern char cpy_a4[4];
+ unsigned n = sizeof cpy_a4;
+
+ void *p = cpy_a4;
+ p = memcpy (p, s, n);
+ sink (p);
+ memcpy (p, s, n);
+ sink (p);
+
+ p = cpy_a4 + 1;
+ p = memcpy (p, s, n - 1);
+ sink (p);
+ memcpy (p, s, n - 1);
+ sink (p);
+
+ p = cpy_a4 + 2;
+ p = memcpy (p, s, n - 2);
+ sink (p);
+ memcpy (p, s, n - 2);
+ sink (p);
+
+ p = cpy_a4 + 3;
+ p = memcpy (p, s, n - 3);
+ sink (p);
+ memcpy (p, s, n - 3);
+ sink (p);
+
+ p = cpy_a4 + 4;
+ p = memcpy (p, s, n - 4);
+ sink (p);
+ memcpy (p, s, n - 4);
+ sink (p);
+}
+
+
+void nowarn_memcpy_chain (const void *s)
+{
+ extern char cpy_a8[8];
+
+ char *p = cpy_a8;
+
+ p = memcpy (p + 1, s, 7);
+ sink (p);
+
+ p = memcpy (p + 2 , s, 5);
+ sink (p);
+
+ p = memcpy (p + 3 , s, 2);
+ sink (p);
+
+ p = memcpy (p + 1 , s, 1);
+ sink (p);
+
+ p = memcpy (p - 7 , s, 8);
+ sink (p);
+
+ memcpy (p + 1, s, 7);
+}
+
+
+void warn_memcpy (const void *s)
+{
+ extern char cpy_a5[5]; // { dg-message "destination object 'cpy_a5'" "note" }
+
+ unsigned n = sizeof cpy_a5;
+ void *p = cpy_a5;
+
+ p = memcpy (p, s, n);
+ sink (p);
+ memcpy (p, s, n + 1); // { dg-warning "writing 6 bytes into a region of size 5" }
+ sink (p);
+
+ p = cpy_a5;
+ p = memcpy (p, s, n);
+ sink (p);
+ memcpy (p, s, n + 1); // { dg-warning "writing 6 bytes into a region of size 5" }
+ sink (p);
+
+ p = cpy_a5 + 1;
+ p = memcpy (p, s, n - 1);
+ sink (p);
+ memcpy (p, s, n); // { dg-warning "writing 5 bytes into a region of size 4" }
+ sink (p);
+}
+
+
+void warn_memcpy_chain (const void *s)
+{
+ extern char cpy_a8[8]; // { dg-message "destination object 'cpy_a8'" "note" }
+
+ char *p = cpy_a8;
+
+ p = memcpy (p, s, 9); // { dg-warning "writing 9 bytes into a region of size 8" }
+ sink (p);
+
+ p = memcpy (p + 2, s, 7); // { dg-warning "writing 7 bytes into a region of size 6" }
+ sink (p);
+
+ p = memcpy (p + 3, s, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+ sink (p);
+
+ p = memcpy (p + 3, s, 3); // { dg-warning "writing 3 bytes into a region of size 0" }
+ sink (p);
+}
+
+
+void nowarn_mempcpy (const void *s)
+{
+ extern char a4[4];
+ unsigned n = sizeof a4;
+
+ char *p = mempcpy (a4, s, n);
+ sink (p);
+ mempcpy (p - 4, s, n);
+ sink (p);
+
+ p = mempcpy (a4 + 1, s, n - 1);
+ sink (p);
+ mempcpy (p - 4, s, n);
+ sink (p);
+
+ p = mempcpy (a4 + 2, s, n - 2);
+ sink (p);
+ mempcpy (p - 4, s, n);
+ sink (p);
+
+ p = mempcpy (a4 + 3, s, n - 3);
+ sink (p);
+ mempcpy (p - 4, s, n);
+ sink (p);
+
+ p = mempcpy (a4 + 4, s, n - 4);
+ sink (p);
+ mempcpy (p - 4, s, n);
+ sink (p);
+}
+
+
+void nowarn_mempcpy_chain (const void *s)
+{
+ extern char pcpy_a8[8];
+
+ char *p = pcpy_a8;
+
+ p = mempcpy (p + 1, s, 7);
+ sink (p);
+
+ p = mempcpy (p - 7 , s, 7);
+ sink (p);
+
+ p = mempcpy (p - 5 , s, 5);
+ sink (p);
+
+ p = mempcpy (p - 3 , s, 3);
+ sink (p);
+
+ p = mempcpy (p - 2 , s, 2);
+ sink (p);
+
+ mempcpy (p - 1, s, 1);
+ sink (p);
+
+ mempcpy (p - 8, s, 8);
+}
+
+
+void warn_mempcpy (const void *s)
+{
+ extern char pcpy_a5[5]; // { dg-message "destination object 'pcpy_a5'" "note" }
+
+ char *p = pcpy_a5;
+
+ p = mempcpy (p, s, 5);
+ sink (p);
+ mempcpy (p - 5, s, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ sink (p);
+
+ p = pcpy_a5;
+ p = mempcpy (p, s, 3);
+ sink (p);
+ mempcpy (p, s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ sink (p);
+
+ p = pcpy_a5 + 1;
+ p = mempcpy (p, s, 3);
+ sink (p);
+ mempcpy (p - 1, s, 5); // { dg-warning "writing 5 bytes into a region of size 2 " }
+ sink (p);
+}
+
+
+void warn_mempcpy_chain_3 (const void *s)
+{
+ char *p = malloc (5); // { dg-message "at offset \\\[3, 5] into destination object of size 5" "note" }
+ p = mempcpy (p, s, UR (1, 2));
+ p = mempcpy (p, s, UR (2, 3));
+ p = mempcpy (p, s, UR (3, 4)); // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }
+
+ sink (p);
+}
+
+void warn_mempcpy_offrng_chain_3 (const void *s)
+{
+ char *p = malloc (11); // { dg-message "at offset \\\[9, 11] into destination object of size 11 " "note" }
+ size_t r1_2 = UR (1, 2);
+ size_t r2_3 = r1_2 + 1;
+ size_t r3_4 = r2_3 + 1;
+
+ p = mempcpy (p + r1_2, s, r1_2);
+ p = mempcpy (p + r2_3, s, r2_3);
+ p = mempcpy (p + r3_4, s, r3_4); // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }
+
+ sink (p);
+}
+
+void warn_mempcpy_chain_4 (const void *s)
+{
+ char *p = malloc (9); // { dg-message "at offset \\\[6, 9] into destination object of size 9 " "note" }
+ p = mempcpy (p, s, UR (1, 2));
+ p = mempcpy (p, s, UR (2, 3));
+ p = mempcpy (p, s, UR (3, 4));
+ p = mempcpy (p, s, UR (4, 5)); // { dg-warning "writing between 4 and 5 bytes into a region of size 3 " }
+
+ sink (p);
+}
+
+void warn_mempcpy_chain_5 (const void *s)
+{
+ char *p = malloc (14); // { dg-message "at offset \\\[10, 14] into destination object of size 14 " "note" }
+ p = mempcpy (p, s, UR (1, 2));
+ p = mempcpy (p, s, UR (2, 3));
+ p = mempcpy (p, s, UR (3, 4));
+ p = mempcpy (p, s, UR (4, 5));
+ p = mempcpy (p, s, UR (5, 6)); // { dg-warning "writing between 5 and 6 bytes into a region of size 4 " }
+
+ sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
new file mode 100644
index 0000000..a4d78b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
@@ -0,0 +1,97 @@
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+ Verify that out of bounds writes by built-ins to objects through pointers
+ returned by memchr() are diagnosed.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+#include "range.h"
+
+void* malloc (size_t);
+void* memchr (void*, int, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+
+void nowarn_memchr_cst_memset_cst (const void *s)
+{
+ char *p = malloc (4);
+ sink (p);
+
+ p = memchr (p, '1', 4);
+ memset (p, 0, 4);
+}
+
+void nowarn_memchr_uint_memset_cst (const void *s, unsigned n)
+{
+ char *p = malloc (4);
+ sink (p);
+
+ p = memchr (p, '1', n);
+ memset (p, 0, 4);
+}
+
+void nowarn_memchr_sz_memset_cst (const void *s, size_t n)
+{
+ char *p = malloc (4);
+ sink (p);
+
+ p = memchr (p, '1', n);
+ memset (p, 0, 4);
+}
+
+void nowarn_memchr_anti_range_memset_cst (const void *s, size_t n)
+{
+ char *p = malloc (4);
+ sink (p);
+
+ if (n == 0)
+ n = 1;
+
+ p = memchr (p, '1', n);
+ memset (p, 0, 4);
+}
+
+void warn_memchr_cst_memset_cst (const void *s)
+{
+ char *p = malloc (4); // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+ sink (p);
+
+ p = memchr (p, '1', 4);
+ memset (p, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_memchr_var_memset_cst (const void *s, unsigned n)
+{
+ char *p = malloc (4); // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+ sink (p);
+
+ p = memchr (p, '1', n);
+ memset (p, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_memchr_var_memset_range (const void *s, unsigned n)
+{
+ /* The offsets in the first two notes are bounded by the size of
+ the allocated object. The real upper bound of the offset in
+ the last note includes the upper bound f the offset of the pointer
+ returned from the previous memchr() call, but it ends up getting
+ constrained to the bounds of the allocated object so it's the same
+ as in the first two notes. The exact value probably isn't too
+ important. */
+ char *p0 = malloc (UR (5, 7));
+ // { dg-message "at offset \\\[0, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset \\\[1, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-2 }
+ // { dg-message "at offset \\\[2, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-3 }
+
+ sink (p0);
+ char *p1 = memchr (p0, '1', n);
+ memset (p1, 0, UR (8, 9)); // { dg-warning "writing between 8 and 9 bytes into a region of size 7 " }
+
+ sink (p0);
+ p1 = memchr (p0 + 1, '2', n);
+ memset (p1, 0, UR (7, 9)); // { dg-warning "writing between 7 and 9 bytes into a region of size 6 " }
+
+ sink (p0);
+ char *p2 = memchr (p1 + 1, '3', n);
+ memset (p2, 0, UR (6, 9)); // { dg-warning "writing between 6 and 9 bytes into a region of size 5 " }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
new file mode 100644
index 0000000..02b14ee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
@@ -0,0 +1,69 @@
+/* Verify that storing a bigger vector into smaller space is diagnosed.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+typedef __INT16_TYPE__ int16_t;
+typedef __attribute__ ((__vector_size__ (32))) char C32;
+
+typedef __attribute__ ((__vector_size__ (64))) int16_t I16_64;
+
+void sink (void*);
+
+
+void nowarn_c32 (char c)
+{
+ extern char nowarn_a32[32];
+
+ void *p = nowarn_a32;
+ *(C32*)p = (C32){ c };
+ sink (p);
+
+ char a32[32];
+ p = a32;
+ *(C32*)p = (C32){ c };
+ sink (p);
+}
+
+void warn_c32 (char c)
+{
+ extern char warn_a32[32]; // { dg-message "at offset 32 to object 'warn_a32' with size 32" }
+
+ void *p = warn_a32 + 1;
+ *(C32*)p = (C32){ c }; // { dg-warning "writing 1 byte into a region of size 0" }
+
+ /* Verify a local variable too. */
+ char a32[32];
+ p = a32 + 1;
+ *(C32*)p = (C32){ c }; // { dg-warning "writing 1 byte into a region of size 0" }
+ sink (p);
+}
+
+
+void nowarn_i16_64 (int16_t i)
+{
+ extern char nowarn_a64[64];
+
+ void *p = nowarn_a64;
+ I16_64 *q = (I16_64*)p;
+ *q = (I16_64){ i };
+
+ char a64[64];
+ q = (I16_64*)a64;
+ *q = (I16_64){ i };
+ sink (q);
+}
+
+void warn_i16_64 (int16_t i)
+{
+ extern char warn_a64[64]; // { dg-message "at offset 128 to object 'warn_a64' with size 64" "pr97027" { xfail *-*-* } }
+
+ void *p = warn_a64 + 1;
+ I16_64 *q = (I16_64*)p;
+ *q = (I16_64){ i }; // { dg-warning "writing 1 byte into a region of size 0" "pr97027" { xfail *-*-* } }
+
+ char a64[64];
+ p = a64 + 1;
+ q = (I16_64*)p;
+ *q = (I16_64){ i }; // { dg-warning "writing 1 byte into a region of size 0" "pr97027" { xfail *-*-* } }
+ sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c
new file mode 100644
index 0000000..84b6c94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c
@@ -0,0 +1,146 @@
+/* Verify the handling of anti-ranges/multi-ranges by allocation functions
+ and subsequent accesses.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void bzero (void*, size_t);
+void* memset (void*, int, size_t);
+
+
+/* Exercise size_t (via malloc and memset) and unsigned/signed int. */
+
+__attribute__ ((alloc_size (1))) void*
+alloc_int (int);
+
+__attribute__ ((access (write_only, 1, 2))) void
+access_int (void*, int);
+
+__attribute__ ((alloc_size (1))) void*
+alloc_uint (unsigned);
+
+__attribute__ ((access (write_only, 1, 2))) void
+access_uint (void*, unsigned);
+
+
+void* nowarn_malloc_memset_same_anti_range (size_t n)
+{
+ /* Set N to the anti-range ~[3, 3]. */
+ if (n == 3)
+ n = 4;
+ void *p = malloc (n);
+
+ /* Verify there is no warning for an access to N bytes at P.
+ This means the warning has to assume the value of N in the call
+ to alloc() is in the larger subrange [4, UINT_MAX], while in
+ the call to access() in [0, 3]. */
+ return memset (p, 0, n);
+}
+
+/* Same as above but with two valid ranges. */
+
+void* nowarn_malloc_memset_anti_range (size_t n1, size_t n2)
+{
+ /* Set N1 to the anti-range ~[3, 3]. */
+ if (n1 == 3)
+ n1 = 4;
+ void *p = malloc (n1);
+
+ /* Set N2 to the anti-range ~[7, 7]. */
+ if (n2 == 7)
+ n2 = 8;
+
+ return memset (p, 0, n2);
+}
+
+
+void nowarn_alloc_access_same_anti_range_int (int n)
+{
+ /* Set N to the anti-range ~[3, 3]. */
+ if (n == 3)
+ n = 4;
+ void *p = alloc_int (n);
+
+ /* Verify there is no warning for an access to N bytes at P.
+ This means the warning has to assume the value of N in the call
+ to alloc() is in the larger subrange [4, UINT_MAX], while in
+ the call to access() in [0, 3]. */
+ access_int (p, n);
+}
+
+/* Same as above but with two valid ranges. */
+
+void nowarn_alloc_access_anti_range_int (int n1, int n2)
+{
+ /* Set N1 to the anti-range ~[3, 3]. */
+ if (n1 == 3)
+ n1 = 4;
+ void *p = alloc_int (n1);
+
+ /* Set N2 to the anti-range ~[7, 7]. */
+ if (n2 == 7)
+ n2 = 8;
+
+ access_int (p, n2);
+}
+
+
+void nowarn_alloc_access_same_anti_range_uint (unsigned n)
+{
+ /* Set N to the anti-range ~[3, 3]. */
+ if (n == 3)
+ n = 4;
+ void *p = alloc_uint (n);
+
+ /* Verify there is no warning for an access to N bytes at P.
+ This means the warning has to assume the value of N in the call
+ to alloc() is in the larger subrange [4, UINT_MAX], while in
+ the call to access() in [0, 3]. */
+ access_uint (p, n);
+}
+
+/* Same as above but with two valid ranges. */
+
+void nowarn_alloc_access_anti_range_uint (unsigned n1, unsigned n2)
+{
+ /* Set N1 to the anti-range ~[3, 3]. */
+ if (n1 == 3)
+ n1 = 4;
+ void *p = alloc_uint (n1);
+
+ /* Set N2 to the anti-range ~[7, 7]. */
+ if (n2 == 7)
+ n2 = 8;
+
+ access_uint (p, n2);
+}
+
+
+void* nowarn_malloc_anti_range_memset_range (size_t n1, size_t n2)
+{
+ /* Set N1 to the anti-range ~[3, 3]. */
+ if (n1 == 3)
+ n1 = 4;
+ void *p = malloc (n1);
+
+ /* Set N2 to the range [5, MAX]. */
+ if (n2 < 5)
+ n2 = 5;
+ return memset (p, 0, n2);
+}
+
+void* nowarn_malloc_range_bzero_anti_range (size_t n1, size_t n2)
+{
+ /* Set N1 to the anti-range ~[3, 3]. */
+ if (n1 > 4)
+ n1 = 4;
+ void *p = malloc (n1);
+
+ /* Set N2 to the range [5, MAX]. */
+ if (n2 <= 3 || 5 <= n2)
+ n2 = 4;
+ bzero (p, n2);
+ return p;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c
new file mode 100644
index 0000000..7df58e5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c
@@ -0,0 +1,125 @@
+/* Verify that writes at excessive offsets into objects of unknown size
+ pointed to by function arguments are diagnosed.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+char* fcall (void);
+
+void char_ptr_cst_off_cst_size (char *p)
+ // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+ size_t idx = DIFF_MAX - 3;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
+ sink (p);
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 1" }
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 0" }
+}
+
+
+void char_ptr_var_difoff_cst_size (ptrdiff_t idx)
+{
+ char *p = fcall ();
+ /* The offset is a range with a very large lower bound and an upper
+ bound of DIFF_MAX. There's not point in also mentioning the latter
+ (it wouldn't make the note any more meaningful) so verify it only
+ mentions the lower bound.
+ { dg-message "at offset \\d+ into destination object of size \\\[0, \\d+] (allocated|returned) by 'fcall'" "note" { target *-*-* } .-5 } */
+
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_szoff_cst_size (size_t idx)
+{
+ extern char* gptr;
+ // { dg-message "at offset \\d+ into destination object 'gptr'" "note" { target *-*-* } .-1 }
+
+ char *p = gptr;
+
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" "" { xfail *-*-* } }
+
+ if (idx > DIFF_MAX)
+ idx = DIFF_MAX;
+
+ memset (p + idx, 0, 7); // { dg-warning "writing 7 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_difoff_var_size (char *p, ptrdiff_t idx, size_t n)
+ // { dg-message "at offset \\d+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ if (n < 3 || 7 < n)
+ n = 3;
+
+ memset (p + idx, 0, n);
+ sink (p);
+
+ ++n;
+ memset (p + idx, 0, n); // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_szoff_var_size (char *p, size_t idx, size_t n)
+ // { dg-message "at offset \\\[\[1-9\]\[0-9\]+, \[1-9\]\[0-9\]+] into destination object 'p'" "note" { xfail *-*-* } .-1 }
+{
+ if (idx < DIFF_MAX - 3)
+ idx = DIFF_MAX - 3;
+
+ if (n < 3 || 7 < n)
+ n = 3;
+
+ memset (p + idx, 0, n);
+ sink (p);
+
+ ++n;
+ /* With an unsigned offset large values are interpreted as negative
+ so the addition (p + idx) is effectively treated as subtraction,
+ making an overflow indistinguishable from a valid (if unlikely)
+ store. */
+ memset (p + idx, 0, n); // { dg-warning "writing between 4 and 8 bytes into a region of size 3" "pr?????" { xfail *-*-* } }
+}
+
+
+void int_ptr_cst_off_cst_size (int *p)
+ // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+ size_t idx = DIFF_MAX / sizeof *p;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c
new file mode 100644
index 0000000..6f36643
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c
@@ -0,0 +1,34 @@
+/* Test case derived from Binutils/GDB's readline/readline/histexpand.c.
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+char *
+get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr)
+{
+ int si, i, j, k;
+ char *s;
+
+ s = 0;
+ i = *iptr;
+
+ for (si = i; str[si] && str[si] != delimiter; si++)
+ if (str[si] == '\\' && str[si + 1] == delimiter)
+ si++;
+
+ if (si > i || is_rhs)
+ {
+ s = (char *)__builtin_malloc (si - i + 1);
+ for (j = 0, k = i; k < si; j++, k++)
+ {
+ /* Remove a backslash quoting the search string delimiter. */
+ if (str[k] == '\\' && str[k + 1] == delimiter)
+ k++;
+ s[j] = str[k]; // { dg-bogus "-Wstringop-overflow" }
+ }
+ s[j] = '\0';
+ if (lenptr)
+ *lenptr = j;
+ }
+
+ return s;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c
new file mode 100644
index 0000000..a289655
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c
@@ -0,0 +1,62 @@
+
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+ Verify that writes by built-in functions to objects through pointers
+ returned by ordinary (non-built-int) function are assumed to point to
+ the beginning of objects.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+#include "range.h"
+
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+
+extern char* arrptr[];
+extern char* ptr;
+extern char* retptr (void);
+struct S { char *p; };
+extern struct S retstruct (void);
+
+void nowarn_ptr (void)
+{
+ {
+ void *p = arrptr;
+ memset (p - 1, 0, 12345); // { dg-warning "\\\[-Wstringop-overflow" }
+ memset (p,0, 12345);
+ memset (p,0, DIFF_MAX - 1);
+ }
+
+ {
+ char *p = arrptr[0];
+ memset (p - 1, 0, 12345);
+ memset (p - 12345, 0, 12345);
+ memset (p - 1234, 0, DIFF_MAX - 1);
+ memset (p - DIFF_MAX + 1, 0, 12345);
+ }
+
+ {
+ char *p = ptr;
+ memset (p - 1, 0, 12345);
+ memset (p - 12345, 0, 12345);
+ memset (p - 1234, 0, DIFF_MAX - 1);
+ memset (p - DIFF_MAX + 1, 0, 12345);
+ }
+
+ {
+ char *p = retptr ();
+ memset (p - 1, 0, 12345);
+ memset (p - 12345, 0, 12345);
+ memset (p - 1234, 0, DIFF_MAX - 1);
+ memset (p - DIFF_MAX + 1, 0, 12345);
+ }
+
+ {
+ char *p = retstruct ().p;
+ memset (p - 1, 0, 12345);
+ memset (p - 12345, 0, 12345);
+ memset (p - 1234, 0, DIFF_MAX - 1);
+ memset (p - DIFF_MAX + 1, 0, 12345);
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c
new file mode 100644
index 0000000..cd8fa32
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c
@@ -0,0 +1,116 @@
+/* PR middle-end/96384 - bogus -Wstringop-overflow= storing into
+ multidimensional array with index in range
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+#define SHRT_MAX __SHRT_MAX__
+#define SHRT_MIN (-SHRT_MAX - 1)
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+#define LONG_MAX __LONG_MAX__
+#define LONG_MIN (-LONG_MAX - 1)
+
+#define USHRT_MAX (SHRT_MAX * 2 + 1)
+#define UINT_MAX ~0U
+#define ULONG_MAX ~0LU
+
+char ca3_5_7[3][5][7];
+
+void nowarn_ca_3_5_ssi (short i)
+{
+ if (i > SHRT_MAX - 1)
+ i = SHRT_MAX - 1;
+
+ ca3_5_7[i][0][0] = __LINE__;
+ ca3_5_7[i][0][1] = __LINE__;
+ ca3_5_7[i][0][2] = __LINE__;
+ ca3_5_7[i][0][3] = __LINE__;
+ ca3_5_7[i][0][4] = __LINE__;
+ ca3_5_7[i][0][5] = __LINE__;
+ ca3_5_7[i][0][6] = __LINE__;
+
+ ca3_5_7[i][1][0] = __LINE__;
+ ca3_5_7[i][1][1] = __LINE__;
+ ca3_5_7[i][1][2] = __LINE__;
+ ca3_5_7[i][1][3] = __LINE__;
+ ca3_5_7[i][1][4] = __LINE__;
+ ca3_5_7[i][1][5] = __LINE__;
+ ca3_5_7[i][1][6] = __LINE__;
+
+ ca3_5_7[i][2][0] = __LINE__;
+ ca3_5_7[i][2][1] = __LINE__;
+ ca3_5_7[i][2][2] = __LINE__;
+ ca3_5_7[i][2][3] = __LINE__;
+ ca3_5_7[i][2][4] = __LINE__;
+ ca3_5_7[i][2][5] = __LINE__;
+ ca3_5_7[i][2][6] = __LINE__;
+
+ ca3_5_7[i][3][0] = __LINE__;
+ ca3_5_7[i][3][1] = __LINE__;
+ ca3_5_7[i][3][2] = __LINE__;
+ ca3_5_7[i][3][3] = __LINE__;
+ ca3_5_7[i][3][4] = __LINE__;
+ ca3_5_7[i][3][5] = __LINE__;
+ ca3_5_7[i][3][6] = __LINE__;
+
+ ca3_5_7[i][4][0] = __LINE__;
+ ca3_5_7[i][4][1] = __LINE__;
+ ca3_5_7[i][4][2] = __LINE__;
+ ca3_5_7[i][4][3] = __LINE__;
+ ca3_5_7[i][4][4] = __LINE__;
+ ca3_5_7[i][4][5] = __LINE__;
+ ca3_5_7[i][4][6] = __LINE__;
+
+ ca3_5_7[1][i][5] = __LINE__;
+ ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_usi (unsigned short i)
+{
+ if (i > USHRT_MAX - 1)
+ i = USHRT_MAX - 1;
+
+ ca3_5_7[i][3][5] = __LINE__;
+ ca3_5_7[1][i][5] = __LINE__;
+ ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_si (int i)
+{
+ if (i > INT_MAX - 1)
+ i = INT_MAX - 1;
+
+ ca3_5_7[i][3][5] = __LINE__;
+ ca3_5_7[1][i][5] = __LINE__;
+ ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_ui (unsigned i)
+{
+ if (i > UINT_MAX - 1)
+ i = UINT_MAX - 1;
+
+ ca3_5_7[i][3][5] = __LINE__;
+ ca3_5_7[1][i][5] = __LINE__;
+ ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_li (long i)
+{
+ if (i > LONG_MAX - 1)
+ i = LONG_MAX - 1;
+
+ ca3_5_7[i][3][5] = __LINE__;
+ ca3_5_7[1][i][5] = __LINE__;
+ ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_uli (unsigned long i)
+{
+ if (i > ULONG_MAX - 1)
+ i = ULONG_MAX - 1;
+
+ ca3_5_7[i][3][5] = __LINE__;
+ ca3_5_7[1][i][5] = __LINE__;
+ ca3_5_7[2][3][i] = __LINE__;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
new file mode 100644
index 0000000..26568f8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
@@ -0,0 +1,103 @@
+/* Verify that writes at excessive offsets into flexible array members
+ of extern or allocated objects of unknow size are diagnosed.
+ { dg-do compile }
+ { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+void char_flexarray_cst_off_cst_size (void)
+{
+ extern struct { char n, a[]; }
+ caxcc; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'caxcc'" }
+
+ char *p = caxcc.a;
+ size_t idx = DIFF_MAX - 4;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
+ sink (p);
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 1" }
+
+ ++idx;
+ memset (p + idx, 0, 3); // { dg-warning "writing 3 bytes into a region of size 0" }
+}
+
+
+void char_flexarray_var_off_cst_size (ptrdiff_t idx)
+{
+ extern struct { char n, a[]; }
+ caxvc; // { dg-message "destination object 'caxvc'" }
+
+ char *p = caxvc.a;
+
+ if (idx < DIFF_MAX - 4)
+ idx = DIFF_MAX - 4;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
+{
+ extern struct { char n, a[]; }
+ caxvv; // { dg-message "destination object 'caxvv'" }
+
+ char *p = caxvv.a;
+
+ if (idx < DIFF_MAX - 4)
+ idx = DIFF_MAX - 4;
+
+ if (n < 3 || 7 < n)
+ n = 3;
+
+ memset (p + idx, 0, n);
+ sink (p);
+
+ ++n;
+ memset (p + idx, 0, n); // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+}
+
+
+void alloc_array_var_off_cst_size (size_t n, ptrdiff_t idx)
+{
+ struct { char n, a[]; }
+ *p = __builtin_malloc (n); // { dg-message "at offset \\d+ into destination object" }
+
+ if (idx < DIFF_MAX - 4)
+ idx = DIFF_MAX - 4;
+
+ memset (p->a + idx, 0, 3);
+ sink (p);
+
+ memset (p->a + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void int_array_cst_off_cst_size (void)
+{
+ extern struct { int n, a[]; }
+ iaxc; // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+
+ int *p = iaxc.a;
+ size_t idx = DIFF_MAX / sizeof *p - 1;
+
+ memset (p + idx, 0, 3);
+ sink (p);
+
+ memset (p + idx, 0, 5); // { dg-warning "writing 5 bytes into a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c
new file mode 100644
index 0000000..25f5b82
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c
@@ -0,0 +1,97 @@
+/* Verify that offsets in "anti-ranges" are handled correctly.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+#define T(x) sink (x)
+
+
+void int_range_add_sub_ (int i, int j)
+{
+ if (i < 1) i = 1;
+ if (j > -1) j = -1;
+
+ char ca5[5]; // { dg-message "at offset \\\[1, 5]" "note" }
+ char *p0 = ca5; // offset
+ char *p1 = p0 + i; // 1-5
+ char *p2 = p1 + i; // 2-5
+ char *p3 = p2 + j; // 0-4
+ char *p4 = p3 + j; // 0-3
+ char *p5 = p4 + j; // 0-2
+ char *p6 = p5 + j; // 0-1
+ char *p7 = p6 + i; // 1-2
+
+ memset (p7, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4" }
+
+ sink (p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+
+void ruint_arint_add (unsigned i, int j)
+{
+ i |= 1; // [1, UINT_MAX]
+ j |= 1; // [INT_MIN + 1, -1] U [1, INT_MAX]
+
+ char a[5]; // { dg-message "at offset \\\[1, 5]" "note" }
+ char *p0 = a; // offset
+ char *p1 = p0 + i; // 1-5
+ T (memset (p1, 0, 4));
+
+ char *p2 = p1 + j; // 0-5
+ T (memset (p2, 0, 5));
+
+ char *p3 = p2 + i; // 1-5
+ T (memset (p3, 0, 4));
+
+ char *p4 = p3 + j; // 0-5
+ T (memset (p4, 0, 5));
+
+ char *p5 = p4 + i; // 1-5
+ T (memset (p5, 0, 4));
+
+ char *p6 = p5 + j; // 0-5
+ T (memset (p6, 0, 5));
+
+ char *p7 = p6 + i; // 1-5
+ T (memset (p7, 0, 5)); // { dg-warning "writing 5 bytes into a region of size 4" "" }
+}
+
+
+void warn_ptrdiff_anti_range_add (ptrdiff_t i)
+{
+ i |= 1;
+
+ char ca5[5]; // { dg-message "at offset \\\[1, 5]" "pr?????" { xfail *-*-* } }
+ char *p0 = ca5; // offset
+ char *p1 = p0 + i; // 1-5
+ char *p2 = p1 + i; // 2-5
+ char *p3 = p2 + i; // 3-5
+ char *p4 = p3 + i; // 4-5
+ char *p5 = p4 + i; // 5
+
+ memset (p5, 0, 5); // { dg-warning "writing 5 bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+
+ sink (p0, p1, p2, p3, p4, p5);
+}
+
+void warn_int_anti_range (int i)
+{
+ i |= 1;
+
+ char ca5[5]; // { dg-message "at offset \\\[1, 5]" "pr?????" { xfail *-*-* } }
+ char *p0 = ca5; // offset
+ char *p1 = p0 + i; // 1-5
+ char *p2 = p1 + i; // 2-5
+ char *p3 = p2 + i; // 3-5
+ char *p4 = p3 + i; // 4-5
+ char *p5 = p4 + i; // 5
+
+ memset (p5, 0, 5); // { dg-warning "writing 5 bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+
+ sink (p0, p1, p2, p3, p4, p5);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread-5.c b/gcc/testsuite/gcc.dg/Wstringop-overread-5.c
new file mode 100644
index 0000000..b75002b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overread-5.c
@@ -0,0 +1,76 @@
+/* Verify -Wstringop-overread with a source pointer pointing either
+ before the beginning or past the end of an object.
+ { dg-do compile }
+ { dg-options "-O -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+size_t strlen (const char *);
+
+void sink (void*, ...);
+
+void off_sz_or_1 (size_t i)
+{
+ i |= 1;
+
+ /* Verify the offset in the notes only mentions the meaningful lower
+ bound and not a range with the excessive (and meaningless) upper
+ bound like [2, 9223372036854775807]. */
+ extern char a[1];
+ // { dg-message "at offset 1 into source object 'a'" "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 2 " "note" { target *-*-* } .-2 }
+
+ char *p1 = a + i;
+ char *p2 = p1 + 1;
+ char *p3 = p1 - 1;
+
+ size_t n = 0;
+ n += strlen (p1); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+ n += strlen (p2); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+ n += strlen (p3);
+
+ sink (p1, p2, p3, n);
+}
+
+
+void off_sz_or_2 (size_t i)
+{
+ i |= 2;
+
+ extern char b[2];
+ // { dg-message "at offset 2 " "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 3 " "note" { target *-*-* } .-2 }
+
+ char *p1 = b + i;
+ char *p2 = p1 + 1;
+ char *p3 = p1 - 1;
+
+ size_t n = 0;
+ n += strlen (p1); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+ n += strlen (p2); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+ n += strlen (p3);
+
+ sink (p1, p2, p3, n);
+}
+
+
+void off_sz_or_4 (size_t i)
+{
+ i |= 4;
+
+ extern char c[3];
+ // { dg-message "at offset 4 " "note" { target *-*-* } .-1 }
+ // { dg-message "at offset 5 " "note" { target *-*-* } .-2 }
+ // { dg-message "at offset 3 " "note" { target *-*-* } .-3 }
+
+ char *p1 = c + i;
+ char *p2 = p1 + 1;
+ char *p3 = p1 - 1;
+
+ size_t n = 0;
+ n += strlen (p1); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+ n += strlen (p2); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+ n += strlen (p3); // { dg-warning "reading 1 or more bytes from a region of size 0" }
+
+ sink (p1, p2, p3, n);
+}
diff --git a/gcc/testsuite/gcc.dg/pr51683.c b/gcc/testsuite/gcc.dg/pr51683.c
index c477cd8..7a624e4 100644
--- a/gcc/testsuite/gcc.dg/pr51683.c
+++ b/gcc/testsuite/gcc.dg/pr51683.c
@@ -12,6 +12,9 @@ void *
foo (void *p)
{
return bar ((void *) 0x12345000, p, 256);
+ /* Integers converted to pointers are assumed to be the result of
+ (invalid) arithmetic on null pointers.
+ { dg-prune-output "writing 256 bytes into a region of size 0" } */
}
/* { dg-final { scan-tree-dump "memcpy" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr60693.c b/gcc/testsuite/gcc.target/i386/pr60693.c
index e6033a7..962c705 100644
--- a/gcc/testsuite/gcc.target/i386/pr60693.c
+++ b/gcc/testsuite/gcc.target/i386/pr60693.c
@@ -11,3 +11,6 @@ foo (void)
__builtin_memcpy (buf, (void *) 0x8000, 4096);
bar (buf);
}
+
+/* Reading from a constant address might triggers:
+ { dg-prune-output "\\\[-Wstringop-overread" } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index f4d1c5c..ebb17cd 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -228,8 +228,20 @@ get_range (tree val, gimple *stmt, wide_int minmax[2],
value_range_kind rng = get_range_info (val, minmax, minmax + 1);
if (rng == VR_RANGE)
+ /* This may be an inverted range whose MINMAX[1] < MINMAX[0]. */
return val;
+ if (rng == VR_ANTI_RANGE)
+ {
+ /* An anti-range is the same as an ordinary range with inverted
+ bounds (where MINMAX[1] < MINMAX[0] is true) that may result
+ from the conversion of a signed anti-range to unsigned. */
+ wide_int tmp = minmax[0];
+ minmax[0] = minmax[1] + 1;
+ minmax[1] = wi::sub (tmp, 1);
+ return val;
+ }
+
/* Do not handle anti-ranges and instead make use of the on-demand
VRP if/when it becomes available (hopefully in GCC 11). */
return NULL_TREE;
@@ -2243,7 +2255,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
sprintf (offstr, "[%lli, %lli]",
(long long) offrng[0].to_shwi (), (long long) offrng[1].to_shwi ());
- if (destdecl)
+ if (destdecl && DECL_P (destdecl))
{
if (tree size = DECL_SIZE_UNIT (destdecl))
inform (DECL_SOURCE_LOCATION (destdecl),