aboutsummaryrefslogtreecommitdiff
path: root/gcc/builtins.c
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/builtins.c
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/builtins.c')
-rw-r--r--gcc/builtins.c659
1 files changed, 495 insertions, 164 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])