return warned;
}
+/* Issue an inform message describing the target of an access REF.
+ WRITE is set for a write access and clear for a read access. */
+
+static void
+inform_access (const access_ref &ref, bool write)
+{
+ 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 size range and always include it since all sizes are
+ meaningful. */
+ unsigned long long minsize = 0, maxsize = 0;
+ if (wi::fits_shwi_p (ref.sizrng[0])
+ && wi::fits_shwi_p (ref.sizrng[1]))
+ {
+ minsize = ref.sizrng[0].to_shwi ();
+ maxsize = ref.sizrng[1].to_shwi ();
+ }
+
+ char sizestr[80];
+ location_t loc;
+ tree allocfn = NULL_TREE;
+ if (TREE_CODE (ref.ref) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (ref.ref);
+ gcc_assert (is_gimple_call (stmt));
+ loc = gimple_location (stmt);
+ allocfn = gimple_call_fndecl (stmt);
+ if (!allocfn)
+ /* Handle calls through pointers to functions. */
+ allocfn = gimple_call_fn (stmt);
+
+ /* SIZRNG doesn't necessarily have the same range as the allocation
+ size determined by gimple_call_alloc_size (). */
+
+ if (minsize == maxsize)
+ sprintf (sizestr, "%llu", minsize);
+ else
+ sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
+
+ }
+ else
+ loc = DECL_SOURCE_LOCATION (ref.ref);
+
+ if (write)
+ {
+ if (DECL_P (ref.ref))
+ {
+ if (minoff == maxoff)
+ {
+ if (minoff == 0)
+ inform (loc, "destination object %qD", ref.ref);
+ else
+ inform (loc, "at offset %lli into destination object %qD",
+ minoff, ref.ref);
+ }
+ else
+ inform (loc, "at offset [%lli, %lli] into destination object %qD",
+ minoff, maxoff, ref.ref);
+ return;
+ }
+
+ if (minoff == maxoff)
+ {
+ if (minoff == 0)
+ inform (loc, "destination object of size %s allocated by %qE",
+ sizestr, allocfn);
+ else
+ inform (loc,
+ "at offset %lli 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 "
+ "allocated by %qE",
+ minoff, maxoff, sizestr, allocfn);
+
+ return;
+ }
+
+ if (DECL_P (ref.ref))
+ {
+ if (minoff == maxoff)
+ {
+ if (minoff == 0)
+ inform (loc, "source object %qD", ref.ref);
+ else
+ inform (loc, "at offset %lli into source object %qD",
+ minoff, ref.ref);
+ }
+ else
+ inform (loc, "at offset [%lli, %lli] into source object %qD",
+ minoff, maxoff, ref.ref);
+ return;
+ }
+
+ if (minoff == maxoff)
+ {
+ if (minoff == 0)
+ inform (loc, "source object of size %s allocated by %qE",
+ sizestr, allocfn);
+ else
+ inform (loc,
+ "at offset %lli 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 "
+ "allocated by %qE",
+ minoff, maxoff, sizestr, allocfn);
+}
+
/* Try to verify that the sizes and lengths of the arguments to a string
manipulation function given by EXP are within valid bounds and that
the operation does not lead to buffer overflow or read past the end.
ACCESS is true for accesses, false for simple size checks in calls
to functions that neither read from nor write to the region.
+ When nonnull, PAD points to a more detailed description of the access.
+
If the call is successfully verified as safe return true, otherwise
return false. */
bool
check_access (tree exp, tree, tree, tree dstwrite,
tree maxread, tree srcstr, tree dstsize,
- bool access /* = true */)
+ bool access /* = true */,
+ const access_data *pad /* = NULL */)
{
int opt = OPT_Wstringop_overflow_;
exp, range[0], range[1],
dstsize));
if (warned)
- TREE_NO_WARNING (exp) = true;
+ {
+ TREE_NO_WARNING (exp) = true;
+ if (pad)
+ inform_access (pad->dst, true);
+ }
/* Return error when an overflow has been detected. */
return false;
loc = expansion_point_location_if_in_system_header (loc);
if (warn_for_access (loc, func, exp, opt, range, slen, access))
- TREE_NO_WARNING (exp) = true;
-
+ {
+ TREE_NO_WARNING (exp) = true;
+ if (pad)
+ inform_access (pad->src, false);
+ }
return false;
}
return wide_int_to_tree (sizetype, rng1[1]);
}
-/* Helper for compute_objsize. Returns the constant size of the DEST
- if it refers to a variable or field and sets *PDECL to the DECL and
- *POFF to zero. Otherwise returns null for other nodes. */
+/* Wrapper around the wide_int overload of get_range. Returns the same
+ result but accepts offset_int instead. */
-static tree
-addr_decl_size (tree dest, tree *pdecl, tree *poff)
+static bool
+get_range (tree x, signop sgn, offset_int r[2],
+ const vr_values *rvals /* = NULL */)
{
- if (TREE_CODE (dest) == ADDR_EXPR)
- dest = TREE_OPERAND (dest, 0);
-
- if (DECL_P (dest))
- {
- *pdecl = dest;
- *poff = integer_zero_node;
- if (tree size = DECL_SIZE_UNIT (dest))
- return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
- }
-
- if (TREE_CODE (dest) == COMPONENT_REF)
- {
- *pdecl = TREE_OPERAND (dest, 1);
- *poff = integer_zero_node;
- /* Only return constant sizes for now while callers depend on it. */
- if (tree size = component_ref_size (dest))
- return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
- }
+ wide_int wr[2];
+ if (!get_range (x, wr, rvals))
+ return false;
- return NULL_TREE;
+ r[0] = offset_int::from (wr[0], sgn);
+ r[1] = offset_int::from (wr[1], sgn);
+ return true;
}
-/* Helper to compute the size of the object referenced by the DEST
+/* 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).
- Returns an estimate of the size of the object represented as
- a sizetype constant if successful or NULL when the size cannot
- be determined.
- When the referenced object involves a non-constant offset in some
- range the returned value represents the largest size given the
- smallest non-negative offset in the range.
- If nonnull, sets *PDECL to the decl of the referenced subobject
- if it can be determined, or to null otherwise. Likewise, when
- POFF is nonnull *POFF is set to the offset into *PDECL.
+ On success, sets PREF->REF to the DECL of the referenced object
+ if it's unique, otherwise to null, PREF->OFFRNG to the range of
+ offsets into it, and PREF->SIZRNG to the range of sizes of
+ 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.
The function is intended for diagnostics and should not be used
to influence code generation or optimization. */
-tree
-compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
- tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
+static bool
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+ bitmap *visited, const vr_values *rvals /* = NULL */)
{
- tree dummy_decl = NULL_TREE;
- if (!pdecl)
- pdecl = &dummy_decl;
+ if (ostype == 0)
+ {
+ /* Use BOS only for raw memory functions like memcpy to get
+ the size of the largest enclosing object. */
+ tree off = NULL_TREE;
+ unsigned HOST_WIDE_INT size;
+ if (compute_builtin_object_size (ptr, ostype, &size, &pref->ref, &off))
+ {
+ if (off)
+ {
+ offset_int offset = wi::to_offset (off);
+ pref->offrng[0] += offset;
+ pref->offrng[1] += offset;
+
+ /* compute_builtin_object_size() returns the remaining
+ size in PTR. Add the offset to it to get the full
+ size. */
+ pref->sizrng[0] = pref->sizrng[1] = size + offset;
+ }
+ else
+ pref->sizrng[0] = pref->sizrng[1] = size;
+ return true;
+ }
+ }
- tree dummy_off = NULL_TREE;
- if (!poff)
- poff = &dummy_off;
+ const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
+ if (addr)
+ ptr = TREE_OPERAND (ptr, 0);
- /* Only the two least significant bits are meaningful. */
- ostype &= 3;
+ if (DECL_P (ptr))
+ {
+ /* Bail if the reference is to the pointer itself (as opposed
+ to what it points to). */
+ if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
+ return false;
+
+ tree size = decl_init_size (ptr, false);
+ if (!size || TREE_CODE (size) != INTEGER_CST)
+ return false;
- if (ostype)
- /* Except for overly permissive calls to memcpy and other raw
- memory functions with zero OSTYPE, detect the size from simple
- DECLs first to more reliably than compute_builtin_object_size
- set *PDECL and *POFF. */
- if (tree size = addr_decl_size (dest, pdecl, poff))
- return size;
+ pref->ref = ptr;
+ pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+ return true;
+ }
- unsigned HOST_WIDE_INT size;
- if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff))
- return build_int_cst (sizetype, size);
+ const tree_code code = TREE_CODE (ptr);
- if (TREE_CODE (dest) == SSA_NAME)
+ if (code == COMPONENT_REF)
{
- gimple *stmt = SSA_NAME_DEF_STMT (dest);
- if (is_gimple_call (stmt))
+ if (ostype == 0)
{
- /* If STMT is a call to an allocation function get the size
- from its argument(s). If successful, also set *PDECL to
- DEST for the caller to include in diagnostics. */
- if (tree size = gimple_call_alloc_size (stmt))
- {
- *pdecl = dest;
- *poff = integer_zero_node;
- return size;
- }
- return NULL_TREE;
+ /* For raw memory functions like memcpy bail if the size
+ of the enclosing object cannot be determined. */
+ access_ref tmpref;
+ tree ref = TREE_OPERAND (ptr, 0);
+ if (!compute_objsize (ref, ostype, &tmpref, visited, rvals)
+ || !tmpref.ref)
+ return false;
}
- if (!is_gimple_assign (stmt))
- return NULL_TREE;
-
- dest = gimple_assign_rhs1 (stmt);
+ tree field = TREE_OPERAND (ptr, 1);
+ /* 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;
- tree_code code = gimple_assign_rhs_code (stmt);
- if (code == POINTER_PLUS_EXPR)
+ pref->ref = field;
+ /* Only return constant sizes for now while callers depend
+ on it. INT0LEN is true for interior zero-length arrays. */
+ bool int0len = false;
+ tree size = component_ref_size (ptr, &int0len);
+ if (int0len)
{
- /* compute_builtin_object_size fails for addresses with
- non-constant offsets. Try to determine the range of
- such an offset here and use it to adjust the constant
- size. */
- tree off = gimple_assign_rhs2 (stmt);
- if (TREE_CODE (off) == INTEGER_CST)
- {
- if (tree size = compute_objsize (dest, ostype, pdecl, poff))
- {
- wide_int wioff = wi::to_wide (off);
- wide_int wisiz = wi::to_wide (size);
-
- /* Ignore negative offsets for now. For others,
- use the lower bound as the most optimistic
- estimate of the (remaining) size. */
- if (wi::neg_p (wioff))
- ;
- else
- {
- if (*poff)
- {
- *poff = fold_convert (ptrdiff_type_node, *poff);
- off = fold_convert (ptrdiff_type_node, *poff);
- *poff = size_binop (PLUS_EXPR, *poff, off);
- }
- else
- *poff = off;
- if (wi::ltu_p (wioff, wisiz))
- return wide_int_to_tree (TREE_TYPE (size),
- wi::sub (wisiz, wioff));
- return size_zero_node;
- }
- }
- }
- else if (TREE_CODE (off) == SSA_NAME
- && INTEGRAL_TYPE_P (TREE_TYPE (off)))
- {
- wide_int min, max;
- enum value_range_kind rng = get_range_info (off, &min, &max);
-
- if (rng == VR_RANGE)
- if (tree size = compute_objsize (dest, ostype, pdecl, poff))
- {
- wide_int wisiz = wi::to_wide (size);
-
- /* Ignore negative offsets for now. For others,
- use the lower bound as the most optimistic
- estimate of the (remaining)size. */
- if (wi::neg_p (min) || wi::neg_p (max))
- ;
- else
- {
- /* FIXME: For now, since the offset is non-constant,
- clear *POFF to keep it from being "misused."
- Eventually *POFF will need to become a range that
- can be properly added to the outer offset if it
- too is one. */
- *poff = NULL_TREE;
- if (wi::ltu_p (min, wisiz))
- return wide_int_to_tree (TREE_TYPE (size),
- wi::sub (wisiz, min));
- return size_zero_node;
- }
- }
- }
+ pref->sizrng[0] = pref->sizrng[1] = 0;
+ return true;
}
- else if (code != ADDR_EXPR)
- return NULL_TREE;
- }
- /* Unless computing the largest size (for memcpy and other raw memory
- functions), try to determine the size of the object from its type. */
- if (!ostype)
- return NULL_TREE;
+ if (!size || TREE_CODE (size) != INTEGER_CST)
+ return false;
+
+ pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+ return true;
+ }
- if (TREE_CODE (dest) == ARRAY_REF
- || TREE_CODE (dest) == MEM_REF)
+ if (code == ARRAY_REF || code == MEM_REF)
{
- tree ref = TREE_OPERAND (dest, 0);
+ tree ref = TREE_OPERAND (ptr, 0);
tree reftype = TREE_TYPE (ref);
- if (TREE_CODE (dest) == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE)
+ if (code == ARRAY_REF
+ && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
+ /* Avoid arrays of pointers. FIXME: Hande pointers to arrays
+ of known bound. */
+ return false;
+
+ if (code == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE)
{
/* Give up for MEM_REFs of vector types; those may be synthesized
from multiple assignments to consecutive data members. See PR
MEM_REFs at the time they're created. */
reftype = TREE_TYPE (reftype);
if (TREE_CODE (reftype) == VECTOR_TYPE)
- return NULL_TREE;
+ return false;
}
- tree off = TREE_OPERAND (dest, 1);
- if (tree size = compute_objsize (ref, ostype, pdecl, poff))
- {
- /* If the declaration of the destination object is known
- to have zero size, return zero. */
- if (integer_zerop (size)
- && *pdecl && DECL_P (*pdecl)
- && *poff && integer_zerop (*poff))
- return size_zero_node;
-
- /* A valid offset into a declared object cannot be negative.
- A zero size with a zero "inner" offset is still zero size
- regardless of the "other" offset OFF. */
- if (*poff
- && ((integer_zerop (*poff) && integer_zerop (size))
- || (TREE_CODE (*poff) == INTEGER_CST
- && tree_int_cst_sgn (*poff) < 0)))
- return size_zero_node;
-
- wide_int offrng[2];
- if (!get_range (off, offrng, rvals))
- return NULL_TREE;
- /* Convert to the same precision to keep wide_int from "helpfully"
- crashing whenever it sees other arguments. */
- const unsigned sizprec = TYPE_PRECISION (sizetype);
- offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
- offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
+ if (!compute_objsize (ref, ostype, pref, visited, rvals))
+ return false;
- /* Adjust SIZE either up or down by the sum of *POFF and OFF
- above. */
- if (TREE_CODE (dest) == ARRAY_REF)
- {
- tree lowbnd = array_ref_low_bound (dest);
- if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
- {
- /* Adjust the offset by the low bound of the array
- domain (normally zero but 1 in Fortran). */
- unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
- offrng[0] -= lb;
- offrng[1] -= lb;
- }
+ offset_int orng[2];
+ tree off = TREE_OPERAND (ptr, 1);
+ if (!get_range (off, SIGNED, orng, rvals))
+ /* Fail unless the size of the object is zero. */
+ return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
- /* Convert the array index into a byte offset. */
- tree eltype = TREE_TYPE (dest);
- tree tpsize = TYPE_SIZE_UNIT (eltype);
- if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
- {
- wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ());
- offrng[0] *= wsz;
- offrng[1] *= wsz;
- }
- else
- return NULL_TREE;
+ if (TREE_CODE (ptr) == ARRAY_REF)
+ {
+ /* Convert the array index range determined above to a byte
+ offset. */
+ tree lowbnd = array_ref_low_bound (ptr);
+ if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
+ {
+ /* Adjust the index by the low bound of the array domain
+ (normally zero but 1 in Fortran). */
+ unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
+ orng[0] -= lb;
+ orng[1] -= lb;
}
- wide_int wisize = wi::to_wide (size);
+ tree eltype = TREE_TYPE (ptr);
+ tree tpsize = TYPE_SIZE_UNIT (eltype);
+ if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
+ return false;
+
+ offset_int sz = wi::to_offset (tpsize);
+ orng[0] *= sz;
+ orng[1] *= sz;
- if (!*poff)
+ if (TREE_CODE (eltype) == ARRAY_TYPE)
{
- /* If the "inner" offset is unknown and the "outer" offset
- is either negative or less than SIZE, return the size
- minus the offset. This may be overly optimistic in
- the first case if the inner offset happens to be less
- than the absolute value of the outer offset. */
- if (wi::neg_p (offrng[0]))
- return size;
- if (wi::ltu_p (offrng[0], wisize))
- return build_int_cst (sizetype, (wisize - offrng[0]).to_uhwi ());
- return size_zero_node;
+ pref->sizrng[0] = pref->offrng[0] + orng[0] + sz;
+ pref->sizrng[1] = pref->offrng[1] + orng[1] + sz;
}
+ }
+
+ pref->offrng[0] += orng[0];
+ pref->offrng[1] += orng[1];
- /* Convert to the same precision to keep wide_int from "helpfuly"
- crashing whenever it sees other argumments. */
- offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
- offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
+ return true;
+ }
- tree dstoff = *poff;
- if (integer_zerop (*poff))
- *poff = off;
- else if (!integer_zerop (off))
+ if (TREE_CODE (ptr) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+ 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. */
+ wide_int wr[2];
+ if (gimple_call_alloc_size (stmt, wr, rvals))
{
- *poff = fold_convert (ptrdiff_type_node, *poff);
- off = fold_convert (ptrdiff_type_node, off);
- *poff = size_binop (PLUS_EXPR, *poff, off);
+ pref->ref = ptr;
+ pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+ pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+ return true;
}
+ return false;
+ }
- if (!wi::neg_p (offrng[0]))
- {
- if (TREE_CODE (size) != INTEGER_CST)
- return NULL_TREE;
+ /* TODO: Handle PHI. */
- /* Return the difference between the size and the offset
- or zero if the offset is greater. */
- wide_int wisize = wi::to_wide (size, sizprec);
- if (wi::ltu_p (wisize, offrng[0]))
- return size_zero_node;
+ if (!is_gimple_assign (stmt))
+ return false;
- return wide_int_to_tree (sizetype, wisize - offrng[0]);
- }
+ 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;
- wide_int dstoffrng[2];
- if (TREE_CODE (dstoff) == INTEGER_CST)
- dstoffrng[0] = dstoffrng[1] = wi::to_wide (dstoff);
- else if (TREE_CODE (dstoff) == SSA_NAME)
+ if (code == POINTER_PLUS_EXPR)
+ {
+ /* 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. */
+ offset_int orng[2];
+ tree off = gimple_assign_rhs2 (stmt);
+ if (!get_range (off, SIGNED, orng, rvals)
+ || !wi::les_p (orng[0], orng[1]))
{
- enum value_range_kind rng
- = get_range_info (dstoff, dstoffrng, dstoffrng + 1);
- if (rng != VR_RANGE)
- return NULL_TREE;
+ orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
+ orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
}
- else
- return NULL_TREE;
-
- dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED);
- dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED);
- if (!wi::neg_p (dstoffrng[0]))
- wisize += dstoffrng[0];
-
- offrng[1] += dstoffrng[1];
- if (wi::neg_p (offrng[1]))
- return size_zero_node;
-
- return wide_int_to_tree (sizetype, wisize);
+ pref->offrng[0] += orng[0];
+ pref->offrng[1] += orng[1];
}
+ else if (code != ADDR_EXPR)
+ return false;
- return NULL_TREE;
+ return compute_objsize (ptr, ostype, pref, visited, rvals);
}
- /* Try simple DECLs not handled above. */
- if (tree size = addr_decl_size (dest, pdecl, poff))
- return size;
-
- tree type = TREE_TYPE (dest);
- if (TREE_CODE (type) == POINTER_TYPE)
- type = TREE_TYPE (type);
-
+ tree type = TREE_TYPE (ptr);
type = TYPE_MAIN_VARIANT (type);
- if (TREE_CODE (dest) == ADDR_EXPR)
- dest = TREE_OPERAND (dest, 0);
+ if (TREE_CODE (ptr) == ADDR_EXPR)
+ ptr = TREE_OPERAND (ptr, 0);
if (TREE_CODE (type) == ARRAY_TYPE
- && !array_at_struct_end_p (dest))
+ && !array_at_struct_end_p (ptr))
{
if (tree size = TYPE_SIZE_UNIT (type))
- return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
+ return get_range (size, UNSIGNED, pref->sizrng, rvals);
}
- return NULL_TREE;
+ return false;
+}
+
+/* Convenience wrapper around the above. */
+
+static tree
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+ const vr_values *rvals = NULL)
+{
+ bitmap visited = NULL;
+
+ bool success
+ = compute_objsize (ptr, ostype, pref, &visited, rvals);
+
+ if (visited)
+ BITMAP_FREE (visited);
+
+ if (!success)
+ return NULL_TREE;
+
+ 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]);
+}
+
+/* Transitional wrapper around the above. The function should be removed
+ once callers transition to one of the two above. */
+
+tree
+compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
+ tree *poff /* = NULL */, const vr_values *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)
+ return NULL_TREE;
+
+ if (pdecl)
+ *pdecl = ref.ref;
+
+ if (poff)
+ *poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]);
+
+ return size;
}
/* Helper to determine and check the sizes of the source and the destination
try to determine the size of the largest source and destination
object using type-0 Object Size regardless of the object size
type specified by the option. */
- tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
- tree dstsize = compute_objsize (dest, 0);
+ access_data data;
+ tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
+ tree dstsize = compute_objsize (dest, 0, &data.dst);
return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
- srcsize, dstsize);
+ srcsize, dstsize, true, &data);
}
/* Validate memchr arguments without performing any expansion.
of the object. */
if (warn_stringop_overflow)
{
- tree size = compute_objsize (arg1, 0);
+ access_data data;
+ tree size = compute_objsize (arg1, 0, &data.src);
check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
- /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
+ /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE,
+ true, &data);
}
return NULL_RTX;
just diagnose cases when the souce string is longer than
the destination object. */
- tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+ access_data data;
+ tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
- destsize);
+ destsize, true, &data);
return NULL_RTX;
}
if (warn_stringop_overflow)
{
- tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+ access_data data;
+ tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
+ &data.dst);
check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
- src, destsize);
+ src, destsize, true, &data);
}
if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
if (warn_stringop_overflow)
{
- tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
+ access_data data;
+ tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
+ &data.dst);
check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
- src, destsize);
+ src, destsize, true, &data);
}
/* If return value is ignored, transform stpcpy into strcpy. */
if (!check_nul_terminated_array (exp, src, len))
return NULL_RTX;
+ access_data data;
/* The size of the destination object. */
- tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+ tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
- check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
+ check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize,
+ true, &data);
return NULL_RTX;
}
/* Try to verify that the destination is big enough for the shortest
string. */
+ access_data data;
if (!objsize && warn_stringop_overflow)
{
/* If it hasn't been provided by __strncat_chk, try to determine
the size of the destination object into which the source is
being copied. */
- objsize = compute_objsize (dest, warn_stringop_overflow - 1);
+ objsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
}
/* Add one for the terminating nul. */
&& tree_int_cst_lt (maxread, srclen)))
srclen = maxread;
- /* The number of bytes to write is LEN but check_access will also
+ /* The number of bytes to write is LEN but check_access will alsoa
check SRCLEN if LEN's value isn't known. */
return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
- objsize);
+ objsize, true, &data);
}
/* Similar to expand_builtin_strcat, do some very basic size validation
maxlen = lendata.maxbound;
}
+ access_data data;
/* Try to verify that the destination is big enough for the shortest
string. First try to determine the size of the destination object
into which the source is being copied. */
- tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+ tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
/* Add one for the terminating nul. */
tree srclen = (maxlen
&& tree_int_cst_lt (maxread, srclen)))
srclen = maxread;
- /* The number of bytes to write is SRCLEN. */
- check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
+ check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize,
+ true, &data);
return NULL_RTX;
}
if (warn_stringop_overflow)
{
- tree destsize = compute_objsize (dest,
- warn_stringop_overflow - 1);
+ access_data data;
+ tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
+ &data.dst);
/* The number of bytes to write is LEN but check_access will also
check SLEN if LEN's value isn't known. */
check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
- destsize);
+ destsize, true, &data);
}
/* We must be passed a constant len and src parameter. */
/* Diagnose calls where the specified length exceeds the size of either
object. */
- tree size = compute_objsize (arg1, 0);
+ access_data data;
+ tree size = compute_objsize (arg1, 0, &data.src);
no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
len, /*maxread=*/NULL_TREE, size,
- /*objsize=*/NULL_TREE);
+ /*objsize=*/NULL_TREE, true, &data);
if (no_overflow)
{
- size = compute_objsize (arg2, 0);
+ access_data data;
+ size = compute_objsize (arg2, 0, &data.src);
no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
len, /*maxread=*/NULL_TREE, size,
- /*objsize=*/NULL_TREE);
+ /*objsize=*/NULL_TREE, true, &data);
}
/* If the specified length exceeds the size of either object,
extern internal_fn associated_internal_fn (tree);
extern internal_fn replacement_internal_fn (gcall *);
-bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
+extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
extern void warn_string_no_nul (location_t, const char *, tree, tree);
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree);
-extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
- bool = true);
+/* Describes a reference to an object used in an access. */
+struct access_ref
+{
+ access_ref (): ref ()
+ {
+ /* Set to valid. */
+ offrng[0] = offrng[1] = 0;
+ /* Invalidate. */
+ sizrng[0] = sizrng[1] = -1;
+ }
+
+ /* Reference to the object. */
+ tree ref;
+
+ /* Range of offsets into and sizes of the object(s). */
+ offset_int offrng[2];
+ offset_int sizrng[2];
+};
+
+/* Describes a pair of references used in an access by built-in
+ functions like memcpy. */
+struct access_data
+{
+ /* Destination and source of the access. */
+ access_ref dst, src;
+};
+
+extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
+ bool = true, const access_data * = NULL);
#endif /* GCC_BUILTINS_H */
return generic_overlap ();
}
+/* For a BASE of array type, clamp REFOFF to at most [0, BASE_SIZE]
+ if known, or [0, MAXOBJSIZE] otherwise. */
+
+static void
+clamp_offset (tree base, offset_int refoff[2], offset_int maxobjsize)
+{
+ if (!base || TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE)
+ return;
+
+ if (refoff[0] < 0 && refoff[1] >= 0)
+ refoff[0] = 0;
+
+ if (refoff[1] < refoff[0])
+ {
+ offset_int maxsize = maxobjsize;
+ if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (base)))
+ maxsize = wi::to_offset (size);
+
+ refoff[1] = wi::umin (refoff[1], maxsize);
+ }
+}
/* Return true if DSTREF and SRCREF describe accesses that either overlap
one another or that, in order not to overlap, would imply that the size
/* If the base object is an array adjust the bounds of the offset
to be non-negative and within the bounds of the array if possible. */
- if (dstref->base
- && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
- {
- if (acs.dstoff[0] < 0 && acs.dstoff[1] >= 0)
- acs.dstoff[0] = 0;
-
- if (acs.dstoff[1] < acs.dstoff[0])
- {
- if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (dstref->base)))
- acs.dstoff[1] = wi::umin (acs.dstoff[1], wi::to_offset (size));
- else
- acs.dstoff[1] = wi::umin (acs.dstoff[1], maxobjsize);
- }
- }
+ clamp_offset (dstref->base, acs.dstoff, maxobjsize);
acs.srcoff[0] = srcref->offrange[0];
acs.srcoff[1] = srcref->offrange[1];
- if (srcref->base
- && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
- {
- if (acs.srcoff[0] < 0 && acs.srcoff[1] >= 0)
- acs.srcoff[0] = 0;
-
- if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (srcref->base)))
- acs.srcoff[1] = wi::umin (acs.srcoff[1], wi::to_offset (size));
- else if (acs.srcoff[1] < acs.srcoff[0])
- acs.srcoff[1] = wi::umin (acs.srcoff[1], maxobjsize);
- }
+ clamp_offset (srcref->base, acs.srcoff, maxobjsize);
/* When the upper bound of the offset is less than the lower bound
the former is the result of a negative offset being represented
CPY (dst7, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */
CPY (dst7, s, sizeof dst7); /* { dg-warning "specified bound 7 equals destination size" } */
- CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" { xfail *-*-* } } */
+ CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" } */
CPY (dst2_5[1], s, sizeof dst2_5[1]); /* { dg-warning "specified bound 5 equals destination size" } */
/* Verify that copies that nul-terminate are not diagnosed. */
T (gma.a17, 17); // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
SA (__builtin_offsetof (struct MA17, ax) == 157);
-
- T (gma.ax, 0); // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'ax' with type 'char[]' at offset 157|'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
+ // GCC allows static initialization of flexible array members of
+ // non-local objects. Verify that writing into one that may be
+ // initialized in another translation unit isn't diagnosed. */
+ T (gma.ax, 0); // { dg-bogus "\\\[-Warray-bounds" }
}
/* PR tree-optimization/84095 - false-positive -Wrestrict warnings for
strcpy within array
{ dg-do compile }
- { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+ { dg-options "-O2 -Wrestrict -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
typedef __SIZE_TYPE__ size_t;
T (d + UR (1, 2), 6); /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
- T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+ T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
T (d + SR (-2, -1), 1);
T (d + SR (-2, -1), 2); /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
- T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+ T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " } */
d = ga7 + 7;
T (d + SR (-7, -6), 1);
T (d + SR (-7, -1), 1);
- T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+ T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " } */
T (d + UR (1, 2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
}
T (d + UR (1, 2), 6); /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */
T (d + UR (1, 2), 7); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
- T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+ T (d + SR (-3, -2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
T (d + SR (-2, -1), 1);
T (d + SR (-2, -1), 2); /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
- T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+ T (d + SR (-2, -1), 9); /* { dg-warning "writing 9 bytes into a region of size 7 " } */
d = ga7 + 7;
T (d + SR (-7, -6), 1);
T (d + SR (-7, -1), 1);
- T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+ T (d + SR (-2, -1), 3); /* { dg-warning "writing 3 bytes into a region of size 2 " } */
T (d + UR (1, 2), 1); /* { dg-warning "writing 1 byte into a region of size 0 " } */
}
T (d + SR (-7, 7), 7);
T (d + SR (-1, 1), 7);
- T (d + SR (-1, 1), 9); /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
- T (d + SR (-1, 2), 9); /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
+ T (d + SR (-1, 1), 9); /* { dg-warning "writing 9 bytes into a region of size " } */
+ T (d + SR (-1, 2), 9); /* { dg-warning "writing 9 bytes into a region of size " } */
T (d + SR (1, 2), 1);
T (d + SR (1, 2), 5);
q += n; // N=1 N=2
q[-1] = 0; // p[0] p[1]
q[-2] = 1; // p[-1] p[0]
- q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
+ q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" }
/* The following isn't diagnosed because the warning doesn't recognize
the index below as necessarily having the same value as the size
--- /dev/null
+/* PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing
+ array plus offset
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+struct S0 { char n, a[0]; };
+
+
+void s0_nowarn_cstidx (struct S0 *p)
+{
+ char *q = p->a;
+ q[1] = __LINE__;
+ q[9] = __LINE__;
+}
+
+void s0_nowarn_cstoff_cstidx (struct S0 *p)
+{
+ char *q = p->a + 1;
+ q[1] = __LINE__;
+ q[9] = __LINE__;
+}
+
+void s0_nowarn_varoff_cstdix (struct S0 *p, int i)
+{
+ char *q = p->a + i;
+ q[1] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
+ q[9] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+void s0_nowarn_cstoff_varidx (struct S0 *p, int i)
+{
+ char *q = p->a + 1;
+ q[i] = __LINE__;
+}
+
+void s0_nowarn_varoff_varidx (struct S0 *p, int i, int j)
+{
+ char *q = p->a + i;
+ q[j] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+
+/* Accesses past the end of a trailing array with one element is
+ discouraged but still reluctantly not diagnosed. This should
+ change. */
+
+struct S1 { char n, a[1]; };
+
+
+void s1_nowarn_cstidx (struct S1 *p)
+{
+ char *q = p->a;
+ q[1] = __LINE__;
+ q[9] = __LINE__;
+}
+
+void s1_nowarn_cstoff_cstidx (struct S1 *p)
+{
+ char *q = p->a + 1;
+ q[1] = __LINE__;
+ q[9] = __LINE__;
+}
+
+void s1_nowarn_varoff_cstdix (struct S1 *p, int i)
+{
+ char *q = p->a + i;
+ q[1] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
+ q[9] = __LINE__; // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+void s1_nowarn_cstoff_varidx (struct S1 *p, int i)
+{
+ char *q = p->a + 1;
+ q[i] = __LINE__;
+}
+
+void s1_nowarn_varoff_varidx (struct S1 *p, int i, int j)
+{
+ char *q = p->a + i;
+ q[j] = __LINE__;
+}
+
+
+/* Accesses past the end of a trailing array with more than one
+ element should be diagnosed but aren't yet because the MEM_REF
+ makes the out-of-bounds accesses indistinguishable from valid
+ ones to subsequent elements of the array pointed by P. */
+
+struct S2 { char n, a[2]; };
+
+
+void s2_warn_cstidx (struct S2 *p)
+{
+ char *q = p->a;
+
+ /* The following invalid store is represented as
+ MEM[(char *)p_1(D) + 3B] = __LINE__;
+ which is indistinguishable from the valid
+ q = &p[1].n; q[0] = __LINE__;
+ */
+ q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void s2_warn_cstoff_cstidx (struct S2 *p)
+{
+ char *q = p->a + 1;
+ q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void s2_warn_varoff_cstdix (struct S2 *p, int i)
+{
+ char *q = p->a + i;
+ q[2] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void s2_warn_cstoff_varidx (struct S2 *p, int i)
+{
+ char *q = p->a + 1;
+ q[i] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void s2_warn_varoff_varidx (struct S2 *p, int i, int j)
+{
+ char *q = p->a + i;
+ q[j] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+
+/* Verify that none of these triggers a bogus warning (not tested
+ elsewhere but triggered during bootstrap). */
+
+void s2_nowarn_varidx_int (struct S2 *p, int i)
+{
+ extern struct S2 s2;
+ extern struct S2 s2a[];
+
+ s2.a[i - 1] = __LINE__;
+ s2.a[i] = __LINE__;
+ s2.a[i + 1] = __LINE__;
+
+ s2a[i].a[i - 1] = __LINE__;
+ s2a[i].a[i] = __LINE__;
+ s2a[i].a[i + 1] = __LINE__;
+
+ p[i].a[i - 1] = __LINE__;
+ p[i].a[i] = __LINE__;
+ p[i].a[i + 1] = __LINE__;
+
+ char *q = p->a;
+ q[i - 1] = __LINE__;
+ q[i] = __LINE__;
+ q[i + 1] = __LINE__;
+}
+
+/* Same as above but with a size_t index in range [1, SIZE_MAX]. */
+
+void* s2_nowarn_varidx_size (struct S2 *p, size_t i, size_t j)
+{
+ extern struct S2 s2;
+ extern struct S2 s2a[];
+ struct S2 *ps2 = __builtin_malloc (3 * sizeof *ps2);
+
+ s2.a[i - 1] = __LINE__;
+ s2.a[i] = __LINE__;
+ s2.a[i + 1] = __LINE__;
+
+ s2a[i].a[i - 1] = __LINE__;
+ s2a[i].a[i] = __LINE__;
+ s2a[i].a[i + 1] = __LINE__;
+
+ p[i].a[i - 1] = __LINE__;
+ p[i].a[i] = __LINE__;
+ p[i].a[i + 1] = __LINE__;
+
+ ps2->a[i - 1] = __LINE__;
+ ps2->a[i] = __LINE__;
+ ps2->a[i + 1] = __LINE__;
+
+ char *q = p->a;
+ q[i - 1] = __LINE__;
+ q[i] = __LINE__;
+ q[i + 1] = __LINE__;
+
+ if (j == 0)
+ return ps2;
+
+ s2.a[j - 1] = __LINE__;
+ s2.a[j] = __LINE__;
+ s2.a[j + 1] = __LINE__;
+
+ s2a[j].a[j - 1] = __LINE__;
+ s2a[j].a[j] = __LINE__;
+ s2a[j].a[j + 1] = __LINE__;
+
+ p[j].a[j - 1] = __LINE__;
+ p[j].a[j] = __LINE__;
+ p[j].a[j + 1] = __LINE__;
+
+ ps2->a[j - 1] = __LINE__;
+ ps2->a[j] = __LINE__;
+ ps2->a[j + 1] = __LINE__;
+
+ q = p->a;
+ q[j - 1] = __LINE__;
+ q[j] = __LINE__;
+ q[j + 1] = __LINE__;
+
+ return ps2;
+}
+
+/* Verify that accesses to an interior zero-length array are diagnosed. */
+
+struct Si0 { char c, a[0], d; };
+
+void si0_warn_cstidx (struct Si0 *p)
+{
+ // These are indistinguishable from valid accesses to p->d:
+ // MEM[(char *)p_1(D) + 1B] = 0;
+ char *q = p->a;
+ q[1] = __LINE__; // { dg-warning "writing 1 byte into a region of size 0" "pr?????" { xfail *-*-* } }
+ q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void si0_warn_cstoff_cstidx (struct Si0 *p)
+{
+ // Like those above, these too are indistinguishable from valid accesses
+ // to p->d.
+ char *q = p->a + 1;
+ q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+ q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void si0_warn_varoff_cstdix (struct Si0 *p, int i)
+{
+ char *q = p->a + i;
+ q[1] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
+ q[9] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+void si0_warn_cstoff_varidx (struct Si0 *p, int i)
+{
+ char *q = p->a + 1;
+ q[i] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void si0_warn_varoff_varidx (struct Si0 *p, int i, int j)
+{
+ char *q = p->a + i;
+ q[j] = __LINE__; // { dg-warning "\\\[-Wstringop-overflow" }
+}
--- /dev/null
+/* Verify that calls to strcpy to write to an element of an array of pointers
+ are not diagnosed (due to mistakenly using the size of the array as that
+ of the destination).
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+typedef char A1[1];
+typedef char A2[2];
+typedef char A3[3];
+typedef char A4[4];
+typedef char A5[5];
+typedef char A6[6];
+typedef char A7[7];
+typedef char A8[8];
+typedef char A9[9];
+typedef char A10[10];
+
+A1* pa1[3];
+A2* pa2[3];
+A3* pa3[3];
+A4* pa4[3];
+A5* pa5[3];
+A6* pa6[3];
+A7* pa7[3];
+A8* pa8[3];
+A9* pa9[3];
+A10* pa10[3];
+
+void nowarn_a1_1 (int i)
+{
+ __builtin_strcpy (*pa1[0], "");
+ __builtin_strcpy (*pa1[1], "");
+ __builtin_strcpy (*pa1[i], "");
+}
+
+void nowarn_a2_2 (int i)
+{
+ __builtin_strcpy (*pa2[0], "1");
+ __builtin_strcpy (*pa2[1], "2");
+ __builtin_strcpy (*pa2[i], "3");
+}
+
+void nowarn_a3_3 (int i)
+{
+ __builtin_strcpy (*pa3[0], "12");
+ __builtin_strcpy (*pa3[1], "23");
+ __builtin_strcpy (*pa3[i], "34");
+}
+
+void nowarn_a4_4 (int i)
+{
+ __builtin_strcpy (*pa4[0], "123");
+ __builtin_strcpy (*pa4[1], "234");
+ __builtin_strcpy (*pa4[i], "345");
+}
+
+void nowarn_a5_5 (int i)
+{
+ __builtin_strcpy (*pa5[0], "1234");
+ __builtin_strcpy (*pa5[1], "2345");
+ __builtin_strcpy (*pa5[i], "3456");
+}
+
+void nowarn_a6_6 (int i)
+{
+ __builtin_strcpy (*pa6[0], "12345");
+ __builtin_strcpy (*pa6[1], "23456");
+ __builtin_strcpy (*pa6[1], "34567");
+}
+
+void nowarn_a10_10 (int i)
+{
+ __builtin_strcpy (*pa10[0], "0123456789");
+ __builtin_strcpy (*pa10[1], "1234567890");
+ __builtin_strcpy (*pa10[i], "2345678909");
+}
--- /dev/null
+/* Verify that casts between pointers and integers don't trigger false
+ positives. Test derived from Glibc's _dl_allocate_tls_storage() in
+ dl-tls.c.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+size_t a;
+size_t s;
+
+void* _dl_allocate_tls_storage (void)
+{
+ void *p = __builtin_malloc (s + a + sizeof (void *));
+
+ char *q = (char *)(__builtin_constant_p (a) && (((a - 1) & a) == 0)
+ ? ((((uintptr_t)p) + a - 1) & ~(a - 1))
+ : (((((uintptr_t)p) + (a - 1)) / a) * a));
+
+ char *r = q + s - sizeof (int[4]);
+ __builtin_memset (r, '\0', sizeof (int[4]));
+ return r;
+}
--- /dev/null
+/* Verify that -Wstringop-overflow detects writing past the end of each
+ individual element of a multidimensional array.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -Wno-stringop-truncation" } */
+
+#define CONCAT(x, y) x ## y
+#define CAT(name, line) CONCAT (name, line)
+#define UNIQ_NAME(name) CAT (name, __LINE__)
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* malloc (size_t);
+extern char* strncpy (char*, const char*, size_t);
+
+extern char a2_2_8[2][2][8];
+
+void nowarn_a2_2_8 (const char *s)
+{
+ // The following trigger -Wstringop-truncation.
+ strncpy (a2_2_8[0][0], s, 8);
+ strncpy (a2_2_8[0][1], s, 8);
+ strncpy (a2_2_8[1][0], s, 8);
+ strncpy (a2_2_8[1][1], s, 8);
+}
+
+void warn_a2_2_8 (const char *s)
+{
+ strncpy (a2_2_8[0][0], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
+ strncpy (a2_2_8[0][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
+ strncpy (a2_2_8[1][0], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
+ strncpy (a2_2_8[1][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 8 " }
+}
+
+
+extern char a2_3_5[2][3][5];
+
+void nowarn_a2_3_5 (const char *s)
+{
+ // The following trigger -Wstringop-truncation.
+ strncpy (a2_3_5[0][0], s, 5);
+ strncpy (a2_3_5[0][1], s, 5);
+ strncpy (a2_3_5[0][2], s, 5);
+ strncpy (a2_3_5[1][0], s, 5);
+ strncpy (a2_3_5[1][1], s, 5);
+ strncpy (a2_3_5[1][2], s, 5);
+}
+
+void warn_a2_3_5 (const char *s)
+{
+ strncpy (a2_3_5[0][0], s, 6); // { dg-warning "writing 6 bytes into a region of size 5 " }
+ strncpy (a2_3_5[0][1], s, 7); // { dg-warning "writing 7 bytes into a region of size 5 " }
+ strncpy (a2_3_5[1][0], s, 8); // { dg-warning "writing 8 bytes into a region of size 5 " }
+ strncpy (a2_3_5[1][1], s, 9); // { dg-warning "writing 9 bytes into a region of size 5 " }
+}
+
+
+void* nowarn_malloc_3_5 (const char *s, unsigned n)
+{
+ if (n < 3 || 5 < n)
+ n = 3;
+ char *p = (char*)malloc (n);
+ strncpy (p + 1, s, 4);
+ return p;
+}
+
+void* warn_malloc_3_5 (const char *s, unsigned n)
+{
+ if (n < 3 || 5 < n)
+ n = 3;
+ char *p = (char*)malloc (n); // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" }
+ // The size below should be a range like the one above.
+ strncpy (p + 1, s, 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ return p;
+}
+
+
+typedef __attribute__ ((alloc_size (1, 2))) void* UsrAlloc (int, int);
+
+void* nowarn_use_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
+{
+ if (n < 3 || 5 < n)
+ n = 3;
+ char *p = (char*)usr_alloc (n, 3);
+ strncpy (p + 1, s, 14);
+ return p;
+}
+
+void* warn_usr_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
+{
+ if (n < 3 || 5 < n)
+ n = 3;
+ char *p = (char*)usr_alloc (n, 3); // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" }
+ // The size below should be a range like the one above.
+ strncpy (p + 1, s, 15); // { dg-warning "writing 15 bytes into a region of size 14 " }
+ return p;
+}
+
+struct S
+{
+ char a2_3_4[2][3][4];
+ char a3_4_5[3][4][5];
+};
+
+extern struct S sa[];
+
+void nowarn_sa_cstidx_cstsize (const char* const s[])
+{
+ strncpy (sa[0].a2_3_4[0][0], s[0], 4);
+ strncpy (sa[0].a2_3_4[0][1], s[1], 4);
+ strncpy (sa[0].a2_3_4[0][2], s[2], 4);
+
+ strncpy (sa[0].a2_3_4[1][0], s[3], 4);
+ strncpy (sa[0].a2_3_4[1][1], s[4], 4);
+ strncpy (sa[0].a2_3_4[1][2], s[5], 4);
+
+ strncpy (sa[1].a2_3_4[0][0], s[6], 4);
+ strncpy (sa[1].a2_3_4[0][1], s[7], 4);
+ strncpy (sa[1].a2_3_4[0][2], s[8], 4);
+
+ strncpy (sa[1].a2_3_4[1][0], s[9], 4);
+ strncpy (sa[1].a2_3_4[1][1], s[10], 4);
+ strncpy (sa[1].a2_3_4[1][2], s[11], 4);
+}
+
+void warn_sa_cstidx_cstsize (const char* const s[])
+{
+ strncpy (sa[0].a2_3_4[0][0], s[0], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ strncpy (sa[0].a2_3_4[0][1], s[1], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
+ strncpy (sa[0].a2_3_4[0][2], s[2], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
+
+ strncpy (sa[0].a2_3_4[1][0], s[3], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ strncpy (sa[0].a2_3_4[1][1], s[4], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
+ strncpy (sa[0].a2_3_4[1][2], s[5], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
+
+ strncpy (sa[1].a2_3_4[0][0], s[6], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ strncpy (sa[1].a2_3_4[0][1], s[7], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
+ strncpy (sa[1].a2_3_4[0][2], s[8], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
+
+ strncpy (sa[1].a2_3_4[1][0], s[9], 5); // { dg-warning "writing 5 bytes into a region of size 4 " }
+ strncpy (sa[1].a2_3_4[1][1], s[10], 6); // { dg-warning "writing 6 bytes into a region of size 4 " }
+ strncpy (sa[1].a2_3_4[1][2], s[11], 7); // { dg-warning "writing 7 bytes into a region of size 4 " }
+}
+
+void nowarn_sa_cstidx_varsize (const char* const s[], unsigned n)
+{
+ strncpy (sa[0].a2_3_4[0][0], s[0], n);
+ strncpy (sa[0].a2_3_4[0][1], s[1], n);
+ strncpy (sa[0].a2_3_4[0][2], s[2], n);
+
+ strncpy (sa[0].a2_3_4[1][0], s[3], n);
+ strncpy (sa[0].a2_3_4[1][1], s[4], n);
+ strncpy (sa[0].a2_3_4[1][2], s[5], n);
+
+ strncpy (sa[1].a2_3_4[0][0], s[6], n);
+ strncpy (sa[1].a2_3_4[0][1], s[7], n);
+ strncpy (sa[1].a2_3_4[0][2], s[8], n);
+
+ strncpy (sa[1].a2_3_4[1][0], s[9], n);
+ strncpy (sa[1].a2_3_4[1][1], s[10], n);
+ strncpy (sa[1].a2_3_4[1][2], s[11], n);
+}
+
+void nowarn_sa_loop (const char* const s[], unsigned n)
+{
+ for (unsigned i0 = 0; i0 != 5; ++i0)
+ for (unsigned i1 = 0; i1 != 3; ++i1)
+ for (unsigned i2 = 0; i2 != 2; ++i2)
+ strncpy (sa[i0].a2_3_4[i1][i2], s[i2], n);
+}
+
+
+/* Verify that a note after the warning points to the accessed object
+ and mentions the starting offset of the access. Another alternative
+ might be for the offset to be the starting offset of the overflow.
+ As it is, it's not clear to which of the two the offset refers. */
+
+void test_note (const char *s)
+{
+ extern void sink (void*);
+
+ {
+ char a[1][1][2]; // { dg-message "destination object" }
+ strncpy (a[0][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ sink (a);
+ }
+
+ {
+ char a[1][2][2]; // { dg-message "at offset 2 into " }
+ strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ sink (a);
+ }
+
+ {
+ char a[1][2][2]; // { dg-message "at offset 4 into " }
+ strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ sink (a);
+ }
+
+ {
+ char a[2][1][2]; // { dg-message "at offset 2 into " }
+ strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
+ sink (a);
+ }
+
+ {
+ char a[2][2][3]; // { dg-message "at offset 9 into " }
+ strncpy (a[1][1], s, 4); // { dg-warning "writing 4 bytes into a region of size 3 " }
+ sink (a);
+ }
+
+ {
+ char a[2][3][3]; // { dg-message "at offset 12 into " }
+ strncpy (a[1][1], s, 5); // { dg-warning "writing 5 bytes into a region of size 3 " }
+ sink (a);
+ }
+
+ {
+ char a[2][3][3]; // { dg-message "at offset 12 into " }
+ strncpy (a[1][1], s, 6); // { dg-warning "writing 6 bytes into a region of size 3 " }
+ sink (a);
+ }
+
+ {
+ char a[2][3][3]; // { dg-message "at offset 15 into " }
+ strncpy (a[1][2], s, 7); // { dg-warning "writing 7 bytes into a region of size 3 " }
+ sink (a);
+ }
+
+}
--- /dev/null
+/* Verify that non-constant offsets don't suppress warnings in cases
+ when the size of the access alone makes it invalid, regardless of
+ the offset value.
+ Also verify that writing into unknown objects through pointers
+ (or pointer members) doesn't trigger warnings.
+ { dg-do compile }
+ { dg-options "-O1 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+
+
+struct Xp { char *p; } xp;
+struct Xa { char *a[2]; } xa;
+
+void nowarn_copy_read_pointed_obj_plus_cst (void *d)
+{
+ int i = 0;
+ memcpy (d, xp.p + i, 9);
+ memcpy (d, xa.a[i], 9);
+}
+
+void nowarn_copy_read_pointed_obj_plus_var (void *d, int i)
+{
+ memcpy (d, xp.p + i, 9);
+ memcpy (d, xa.a[i], 9);
+}
+
+
+void warn_copy_read_pointer_plus_cst (void *d)
+{
+ int i = 0;
+ memcpy (d, &xp.p + i, 9); // { dg-warning "reading 9 bytes from a region of size . " }
+}
+
+void warn_copy_read_pointer_plus_var (void *d, int i)
+{
+ memcpy (d, &xp.p + i, 9); // { dg-warning "reading 9 bytes from a region of size . " }
+}
+
+
+void nowarn_copy_write_pointed_obj_plus_cst (const void *s)
+{
+ int i = 0;
+ memcpy (xp.p + i, s, 9);
+ memcpy (xa.a[i], s, 9);
+}
+
+void nowarn_copy_write_pointed_obj_plus_var (const void *s, int i)
+{
+ memcpy (xp.p + i, s, 9);
+ memcpy (xa.a[i], s, 9);
+}
+
+
+void warn_copy_write_pointer_plus_cst (const void *s)
+{
+ int i = 0;
+ memcpy (&xp.p + i, s, 9); // { dg-warning "writing 9 bytes into a region of size . " }
+}
+
+void warn_copy_write_pointer_plus_var (const void *s, int i)
+{
+ memcpy (&xp.p + i, s, 9); // { dg-warning "writing 9 bytes into a region of size . " }
+}
+
+
+void nowarn_set_pointed_obj_plus_cst (void)
+{
+ int i = 0;
+ memset (xp.p + i, 0, 9);
+ memset (xa.a[i], 0, 9);
+}
+
+void nowarn_set_pointed_obj_plus_var (int i)
+{
+ memset (xp.p + i, 0, 9);
+ memset (xa.a[i], 0, 9);
+}
+
+
+void warn_set_pointer_plus_cst (void)
+{
+ int i = 0;
+ memset (&xp.p + i, 0, 9); // { dg-warning "writing 9 bytes into a region of size . " }
+}
+
+void warn_set_pointer_plus_var (int i)
+{
+ memset (&xp.p + i, 0, 9); // { dg-warning "writing 9 bytes into a region of size . " }
+ memset (&xa.a[i], 0, 17); // { dg-warning "writing 17 bytes into a region of size \[0-9\]+ " }
+}
memcpy (buf + 5, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" } */
- memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */
+ memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" } */
memcpy (buf, s, UR (ssize_max, size_max)); /* { dg-warning "writing \[0-9\]+ or more bytes into a region of size 5 overflows the destination" } */
memcpy (buf, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified \(bound|size\) between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
/* Test exercising -Wstringop-overflow warnings. */
/* { dg-do compile } */
-/* { dg-options "-O2 -Wstringop-overflow=1" } */
+/* { dg-options "-O2 -Wstringop-overflow=1 -Wno-array-bounds" } */
#define offsetof(type, mem) __builtin_offsetof (type, mem)
/* Verify the same as above but by writing into the first mmeber
of the first element of the array. */
- memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+ memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size " "memcpy into allocated" } */
escape (a, src);
n = range (12, 32);
/* The following idiom of clearing multiple members of a struct is
used in a few places in the Linux kernel. Verify that a warning
is issued for it when it writes past the end of the array object. */
- memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
escape (b);
/* Same as above but clearing just elements of the second element
of the array. */
- memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size " "memcpy into allocated" } */
escape (b);
- memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
+ memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" } */
escape (b);
}
memcpy (p, &a[0], n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */
- memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+ memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size " "memcpy from allocated" } */
n = range (12, 32);
VERIFY (i0 ? (a8 + 0) : (c4 + 0), 9, 4);
VERIFY (i0 ? (a8 + 0) : (c4 + 1), 9, 3);
VERIFY (i0 ? (a8 + 0) : (c4 + 3), 9, 1);
-VERIFY (i0 ? (a8 + 0) : (c4 + 4), 9, 0);
+VERIFY (i0 ? (a8 + 0) : (c4 + 4), 8, 0);
VERIFY (i0 ? (a8 + 1) : (c4 + 0), 8, 4);
VERIFY (i0 ? (a8 + 1) : (c4 + 1), 8, 3);
VERIFY (i0 ? (a8 + 1) : (c4 + 2), 8, 2);
is true, else null. An object's initializer affects the object's
size if it's a struct type with a flexible array member. */
-static tree
+tree
decl_init_size (tree decl, bool min)
{
tree size = DECL_SIZE_UNIT (decl);
offset_int mem_offset;
if (mem_ref_offset (pt_var).is_constant (&mem_offset))
{
+ if (*poff)
+ *poff = wide_int_to_tree (ptrdiff_type_node,
+ mem_offset + wi::to_offset (*poff));
+ else
+ *poff = wide_int_to_tree (ptrdiff_type_node, mem_offset);
offset_int dsz = wi::sub (sz, mem_offset);
if (wi::neg_p (dsz))
sz = 0;
bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
if (bytes != error_mark_node)
{
+ *poff = bytes;
if (TREE_CODE (bytes) == INTEGER_CST
&& tree_int_cst_lt (var_size, bytes))
bytes = size_zero_node;
else
bytes = size_binop (MINUS_EXPR, var_size, bytes);
- *poff = bytes;
}
if (var != pt_var
&& pt_var_size
else if (!pt_var_size)
return false;
else
- bytes = pt_var_size;
+ {
+ bytes = pt_var_size;
+ if (!*poff)
+ *poff = size_zero_node;
+ }
if (tree_fits_uhwi_p (bytes))
{
extern void fini_object_sizes (void);
extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *,
tree * = NULL, tree * = NULL);
+extern tree decl_init_size (tree, bool);
#endif // GCC_TREE_OBJECT_SIZE_H
set_strinfo (idx, si);
si->writable = true;
si->dont_invalidate = true;
- maybe_warn_overflow (stmt, lenrange[2], rvals);
}
}
}