+2019-12-13 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/91582
+ PR middle-end/92868
+ * builtins.c (addr_decl_size): New function.
+ (gimple_call_alloc_size): Add arguments.
+ (compute_objsize): Add an argument. Set *PDECL even for allocated
+ objects.
+ Correct checking for negative wide_int.
+ Correct handling of negative outer offsets into unknown regions
+ or with unknown inner offsets.
+ Extend offsets to at most sizetype precision.
+ Only handle constant subobject sizes.
+ * builtins.h (gimple_call_alloc_size): Add arguments.
+ * tree.c (component_ref_size): Always return sizetype.
+ * tree-ssa-strlen.c (strinfo::alloc): New member.
+ (get_addr_stridx): Add argument.
+ (get_stridx): Use ptrdiff_t. Add argument.
+ (new_strinfo): Set new member.
+ (get_string_length): Handle alloca and VLA.
+ (dump_strlen_info): Dump more state.
+ (maybe_invalidate): Print more info. Decrease indentation.
+ (unshare_strinfo): Set new member.
+ (valid_builtin_call): Handle alloca and VLA.
+ (maybe_warn_overflow): Check and set no-warning bit. Improve
+ handling of offsets. Print allocated objects.
+ (handle_builtin_strlen): Handle strinfo records with null lengths.
+ (handle_builtin_strcpy): Add argument. Call maybe_warn_overflow.
+ (is_strlen_related_p): Handle dynamically allocated objects.
+ (get_range): Add argument.
+ (handle_builtin_malloc): Rename...
+ (handle_alloc): ...to this and handle all allocation functions.
+ (handle_builtin_memset): Call maybe_warn_overflow.
+ (count_nonzero_bytes): Handle more MEM_REF forms.
+ (strlen_check_and_optimize_call): Call handle_alloc_call. Pass
+ arguments to more callees.
+ (handle_integral_assign): Add argument. Create strinfo entries
+ for MEM_REF assignments.
+ (check_and_optimize_stmt): Handle more MEM_REF forms.
+
2019-12-13 Iain Sandoe <iain@sandoe.co.uk>
* config/rs6000/darwin.h (DARWIN_DYLIB1_SPEC): New.
#include "calls.h"
#include "varasm.h"
#include "tree-object-size.h"
+#include "tree-ssa-strlen.h"
#include "realmpfr.h"
#include "cfgrtl.h"
#include "except.h"
return true;
}
-/* If STMT is a call to an allocation function, returns the size
- of the object allocated by the call. */
+/* If STMT is a call to an allocation function, returns the constant
+ size of the object allocated by the call represented as sizetype.
+ If nonnull, sets RNG1[] to the range of the size. */
tree
-gimple_call_alloc_size (gimple *stmt)
+gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
+ const vr_values *rvals /* = NULL */)
{
if (!stmt)
return NULL_TREE;
tree size = gimple_call_arg (stmt, argidx1);
- wide_int rng1[2];
- if (TREE_CODE (size) == INTEGER_CST)
- rng1[0] = rng1[1] = wi::to_wide (size);
- else if (TREE_CODE (size) != SSA_NAME
- || get_range_info (size, rng1, rng1 + 1) != VR_RANGE)
+ wide_int rng1_buf[2];
+ /* If RNG1 is not set, use the buffer. */
+ if (!rng1)
+ rng1 = rng1_buf;
+
+ if (!get_range (size, rng1, rvals))
return NULL_TREE;
if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
of the upper bounds as a constant. Ignore anti-ranges. */
tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
wide_int rng2[2];
- if (TREE_CODE (n) == INTEGER_CST)
- rng2[0] = rng2[1] = wi::to_wide (n);
- else if (TREE_CODE (n) != SSA_NAME
- || get_range_info (n, rng2, rng2 + 1) != VR_RANGE)
+ if (!get_range (n, rng2, rvals))
return NULL_TREE;
- /* Extend to the maximum precsion to avoid overflow. */
+ /* Extend to the maximum precision to avoid overflow. */
const int prec = ADDR_MAX_PRECISION;
rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
rng2[1] = wide_int::from (rng2[1], prec, UNSIGNED);
- /* Return the lesser of SIZE_MAX and the product of the upper bounds. */
+ /* Compute products of both bounds for the caller but return the lesser
+ of SIZE_MAX and the product of the upper bounds as a constant. */
rng1[0] = rng1[0] * rng2[0];
rng1[1] = rng1[1] * rng2[1];
tree size_max = TYPE_MAX_VALUE (sizetype);
return wide_int_to_tree (sizetype, rng1[1]);
}
+/* Helper for compute_objsize. Returns the constant size of the DEST
+ if it refers to a variable or field and sets *PDECL to the DECL and
+ *POFF to zero. Otherwise returns null for other nodes. */
+
+static tree
+addr_decl_size (tree dest, tree *pdecl, tree *poff)
+{
+ if (TREE_CODE (dest) == ADDR_EXPR)
+ dest = TREE_OPERAND (dest, 0);
+
+ if (DECL_P (dest))
+ {
+ *pdecl = dest;
+ *poff = integer_zero_node;
+ if (tree size = DECL_SIZE_UNIT (dest))
+ return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
+ }
+
+ if (TREE_CODE (dest) == COMPONENT_REF)
+ {
+ *pdecl = TREE_OPERAND (dest, 1);
+ *poff = integer_zero_node;
+ /* Only return constant sizes for now while callers depend on it. */
+ if (tree size = component_ref_size (dest))
+ return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
+ }
+
+ return NULL_TREE;
+}
+
/* Helper to compute the size of the object referenced by the DEST
expression which must have pointer type, using Object Size type
- OSTYPE (only the least significant 2 bits are used). Return
- an estimate of the size of the object if successful or NULL when
- the size cannot be determined. When the referenced object involves
- a non-constant offset in some range the returned value represents
- the largest size given the smallest non-negative offset in the
- range. If nonnull, set *PDECL to the decl of the referenced
- subobject if it can be determined, or to null otherwise. Likewise,
- when POFF is nonnull *POFF is set to the offset into *PDECL.
+ OSTYPE (only the least significant 2 bits are used).
+ Returns an estimate of the size of the object represented as
+ a sizetype constant if successful or NULL when the size cannot
+ be determined.
+ When the referenced object involves a non-constant offset in some
+ range the returned value represents the largest size given the
+ smallest non-negative offset in the range.
+ If nonnull, sets *PDECL to the decl of the referenced subobject
+ if it can be determined, or to null otherwise. Likewise, when
+ POFF is nonnull *POFF is set to the offset into *PDECL.
+
The function is intended for diagnostics and should not be used
to influence code generation or optimization. */
tree
compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
- tree *poff /* = NULL */)
+ tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
{
tree dummy_decl = NULL_TREE;
if (!pdecl)
pdecl = &dummy_decl;
- tree dummy_off = size_zero_node;
+ tree dummy_off = NULL_TREE;
if (!poff)
poff = &dummy_off;
- unsigned HOST_WIDE_INT size;
-
/* Only the two least significant bits are meaningful. */
ostype &= 3;
+ if (ostype)
+ /* Except for overly permissive calls to memcpy and other raw
+ memory functions with zero OSTYPE, detect the size from simple
+ DECLs first to more reliably than compute_builtin_object_size
+ set *PDECL and *POFF. */
+ if (tree size = addr_decl_size (dest, pdecl, poff))
+ return size;
+
+ unsigned HOST_WIDE_INT size;
if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff))
return build_int_cst (sizetype, size);
if (is_gimple_call (stmt))
{
/* If STMT is a call to an allocation function get the size
- from its argument(s). */
- return gimple_call_alloc_size (stmt);
+ from its argument(s). If successful, also set *PDECL to
+ DEST for the caller to include in diagnostics. */
+ if (tree size = gimple_call_alloc_size (stmt))
+ {
+ *pdecl = dest;
+ *poff = integer_zero_node;
+ return size;
+ }
+ return NULL_TREE;
}
if (!is_gimple_assign (stmt))
/* Ignore negative offsets for now. For others,
use the lower bound as the most optimistic
estimate of the (remaining) size. */
- if (wi::sign_mask (wioff))
+ if (wi::neg_p (wioff))
;
- else if (wi::ltu_p (wioff, wisiz))
- {
- *poff = size_binop (PLUS_EXPR, *poff, off);
- return wide_int_to_tree (TREE_TYPE (size),
- wi::sub (wisiz, wioff));
- }
else
{
- *poff = size_binop (PLUS_EXPR, *poff, off);
+ if (*poff)
+ {
+ *poff = fold_convert (ptrdiff_type_node, *poff);
+ off = fold_convert (ptrdiff_type_node, *poff);
+ *poff = size_binop (PLUS_EXPR, *poff, off);
+ }
+ else
+ *poff = off;
+ if (wi::ltu_p (wioff, wisiz))
+ return wide_int_to_tree (TREE_TYPE (size),
+ wi::sub (wisiz, wioff));
return size_zero_node;
}
}
enum value_range_kind rng = get_range_info (off, &min, &max);
if (rng == VR_RANGE)
- {
- if (tree size = compute_objsize (dest, ostype, pdecl))
- {
- wide_int wisiz = wi::to_wide (size);
-
- /* Ignore negative offsets for now. For others,
- use the lower bound as the most optimistic
- estimate of the (remaining)size. */
- if (wi::sign_mask (min)
- || wi::sign_mask (max))
- ;
- else if (wi::ltu_p (min, wisiz))
- {
- *poff = size_binop (PLUS_EXPR, *poff,
- wide_int_to_tree (sizetype, min));
+ 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));
- }
- else
- {
- *poff = size_binop (PLUS_EXPR, *poff,
- wide_int_to_tree (sizetype, min));
- return size_zero_node;
- }
- }
- }
+ return size_zero_node;
+ }
+ }
}
}
else if (code != ADDR_EXPR)
&& *poff && integer_zerop (*poff))
return size_zero_node;
- /* A valid offset into a declared object cannot be negative. */
- if (tree_int_cst_sgn (*poff) < 0)
+ /* A valid offset into a declared object cannot be negative.
+ A zero size with a zero "inner" offset is still zero size
+ regardless of the "other" offset OFF. */
+ if (*poff
+ && ((integer_zerop (*poff) && integer_zerop (size))
+ || (TREE_CODE (*poff) == INTEGER_CST
+ && tree_int_cst_sgn (*poff) < 0)))
return size_zero_node;
+ wide_int offrng[2];
+ if (!get_range (off, offrng, rvals))
+ return NULL_TREE;
+
+ /* Convert to the same precision to keep wide_int from "helpfully"
+ crashing whenever it sees other arguments. */
+ const unsigned sizprec = TYPE_PRECISION (sizetype);
+ offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
+ offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
+
/* Adjust SIZE either up or down by the sum of *POFF and OFF
above. */
if (TREE_CODE (dest) == ARRAY_REF)
tree eltype = TREE_TYPE (dest);
tree tpsize = TYPE_SIZE_UNIT (eltype);
if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
- off = fold_build2 (MULT_EXPR, size_type_node, off, tpsize);
+ {
+ wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ());
+ offrng[0] *= wsz;
+ offrng[1] *= wsz;
+ }
else
return NULL_TREE;
}
- wide_int offrng[2];
- if (TREE_CODE (off) == INTEGER_CST)
- offrng[0] = offrng[1] = wi::to_wide (off);
- else if (TREE_CODE (off) == SSA_NAME)
+ wide_int wisize = wi::to_wide (size);
+
+ if (!*poff)
{
- wide_int min, max;
- enum value_range_kind rng
- = get_range_info (off, offrng, offrng + 1);
- if (rng != VR_RANGE)
- return NULL_TREE;
+ /* If the "inner" offset is unknown and the "outer" offset
+ is either negative or less than SIZE, return the size
+ minus the offset. This may be overly optimistic in
+ the first case if the inner offset happens to be less
+ than the absolute value of the outer offset. */
+ if (wi::neg_p (offrng[0]))
+ return size;
+ if (wi::ltu_p (offrng[0], wisize))
+ return build_int_cst (sizetype, (wisize - offrng[0]).to_uhwi ());
+ return size_zero_node;
}
- else
- return NULL_TREE;
/* Convert to the same precision to keep wide_int from "helpfuly"
crashing whenever it sees other argumments. */
- offrng[0] = wide_int::from (offrng[0], ADDR_MAX_BITSIZE, SIGNED);
- offrng[1] = wide_int::from (offrng[1], ADDR_MAX_BITSIZE, SIGNED);
+ offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
+ offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
tree dstoff = *poff;
if (integer_zerop (*poff))
*poff = size_binop (PLUS_EXPR, *poff, off);
}
- if (wi::sign_mask (offrng[0]) >= 0)
+ if (!wi::neg_p (offrng[0]))
{
if (TREE_CODE (size) != INTEGER_CST)
return NULL_TREE;
/* Return the difference between the size and the offset
or zero if the offset is greater. */
- wide_int wisize = wi::to_wide (size, ADDR_MAX_BITSIZE);
+ wide_int wisize = wi::to_wide (size, sizprec);
if (wi::ltu_p (wisize, offrng[0]))
return size_zero_node;
else
return NULL_TREE;
- dstoffrng[0] = wide_int::from (dstoffrng[0], ADDR_MAX_BITSIZE, SIGNED);
- dstoffrng[1] = wide_int::from (dstoffrng[1], ADDR_MAX_BITSIZE, SIGNED);
+ dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED);
+ dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED);
- wide_int declsize = wi::to_wide (size);
- if (wi::sign_mask (dstoffrng[0]) > 0)
- declsize += dstoffrng[0];
+ if (!wi::neg_p (dstoffrng[0]))
+ wisize += dstoffrng[0];
offrng[1] += dstoffrng[1];
- if (wi::sign_mask (offrng[1]) < 0)
+ if (wi::neg_p (offrng[1]))
return size_zero_node;
- return wide_int_to_tree (sizetype, declsize);
+ return wide_int_to_tree (sizetype, wisize);
}
return NULL_TREE;
}
- if (TREE_CODE (dest) == COMPONENT_REF)
- {
- *pdecl = TREE_OPERAND (dest, 1);
- return component_ref_size (dest);
- }
-
- if (TREE_CODE (dest) != ADDR_EXPR)
- return NULL_TREE;
-
- tree ref = TREE_OPERAND (dest, 0);
- if (DECL_P (ref))
- {
- *pdecl = ref;
- if (tree size = DECL_SIZE_UNIT (ref))
- return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
- }
+ /* Try simple DECLs not handled above. */
+ if (tree size = addr_decl_size (dest, pdecl, poff))
+ return size;
tree type = TREE_TYPE (dest);
if (TREE_CODE (type) == POINTER_TYPE)
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
extern bool is_simple_builtin (tree);
extern bool is_inexpensive_builtin (tree);
-extern tree gimple_call_alloc_size (gimple *);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL);
+
+class vr_values;
+tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
+ const vr_values * = NULL);
+extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
+ const vr_values * = NULL);
extern bool readonly_data_expr (tree exp);
extern bool init_target_chars (void);
+2019-12-13 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/91582
+ * c-c++-common/Wrestrict.c: Adjust expected warnings.
+ * c-c++-common/Wstringop-truncation-4.c: Enable more
+ warnings.
+ * c-c++-common/Wstringop-truncation.c: Remove an xfail.
+ * gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
+ * gcc.dg/Warray-bounds-47.c: Same.
+ * gcc.dg/Warray-bounds-52.c: New test.
+ * gcc.dg/Wstringop-overflow-27.c: New test.
+ * gcc.dg/Wstringop-overflow-28.c: New test.
+ * gcc.dg/Wstringop-overflow-29.c: New test.
+ * gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
+ * gcc.dg/attr-copy-2.c: Adjust expected warnings.
+ * gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
+ * gcc.dg/strlenopt-86.c: Relax test.
+ * gcc.target/i386/pr82002-1.c: Prune expected warnings.
+
2019-12-13 Roman Zhuykov <zhroma@ispras.ru>
PR rtl-optimization/92591
r = SR (3, DIFF_MAX - 3);
T (8, "01", a + r, a);
- T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+ /* The accesses below might trigger either
+ -Wrestrict: accessing 4 bytes at offsets [3, \[0-9\]+] and 0 may overlap 1 byte at offset 3
+ or
+ -Wstringop-overflow: writing 4 bytes into a region of size 0
+ Either of the two is appropriate. */
+ T (8, "012", a + r, a); /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
- T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+ T (8, "012", a + r, a); /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
/* Exercise the full range of ptrdiff_t. */
r = signed_value ();
void test_arrays (struct Arrays *p, const char *s)
{
+ /* Expect accesses to all three arrays to trigger the warning,
+ including the trailing one. The size argument is a good
+ enough indication that it is not being used as a "legacy"
+ flexible array member. */
strncpy (p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
- strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+ strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct Pointers
void test_const_arrays (struct ConstArrays *p, const char *s)
{
+ /* Expect accesses to all three arrays to trigger the warning,
+ including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
- strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+ strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct ConstPointers
void test_volatile_arrays (struct VolatileArrays *p, const char *s)
{
+ /* Expect accesses to all three arrays to trigger the warning,
+ including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
- strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+ strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct VolatilePointers
void test_const_volatile_arrays (struct ConstVolatileArrays *p, const char *s)
{
+ /* Expect accesses to all three arrays to trigger the warning,
+ including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
- strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+ strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct ConstVolatilePointers
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */
CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */
- /* The following is not yet handled. */
- CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" { xfail *-*-* } } */
+ CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" } */
/* Verify that a copy that nul-terminates is not diagnosed. */
CPY (pd->a5, "1234", sizeof pd->a5);
struct Ax
{
char n;
- char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" }
+ char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" "note: flexarray" }
};
// Verify warning for a definition with no initializer.
struct A0
{
char n;
- char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" }
+ char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" "note: trailing zero-length array" }
};
// Verify warning for a definition with no initializer.
struct A1
{
char n;
- char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" }
+ char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" "note: trailing one-element array" }
};
// Verify warning for a definition with no initializer.
struct A1i
{
char n;
- char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" }
+ char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" "note: interior one-element array" }
char x;
};
struct Bx
{
char n;
- char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" }
+ char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" "note: flexarray class member" }
// Verify the warning for a constant.
Bx () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
struct B0
{
char n;
- char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" }
+ char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" "note: zero-length trailing array class member" }
B0 () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
struct B1
{
char n;
- char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" }
+ char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" "note: one-element trailing array class member" }
B1 () { a[1] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
struct B123
{
- char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" }
+ char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" "note: large trailing array class member" }
B123 () { a[123] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
struct B234
{
- char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" }
+ char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" "note: large trailing array class member" }
B234 (int i) { a[i] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
Test to verify that past-the-end accesses by string functions to member
arrays by-reference objects are diagnosed.
{ dg-do compile }
- { dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" } */
+ { dg-options "-O2 -Wall -Wno-unused-local-typedefs -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
#define SA(expr) typedef int StaticAssert [2 * !!(expr) - 1]
/* PR middle-end/91830 - Bogus -Warray-bounds on strcpy into a member
of a subobject compiling binutils
{ dg-do compile }
- { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+ { dg-options "-O2 -Wall -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
extern char* strcpy (char*, const char*);
extern void sink (void*);
--- /dev/null
+/* PR middle-end/92341 - missing -Warray-bounds indexing past the end
+ of a compound literal
+ { dg-do compile }
+ { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-__INT_MAX__ - 1)
+
+void sink (int, ...);
+
+
+#define T(...) sink (__LINE__, (__VA_ARGS__))
+
+
+void direct_idx_cst (void)
+{
+ T ((int[]){ }[-1]); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
+ T ((int[]){ }[0]); // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
+ T ((int[]){ }[1]); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
+
+ T ((int[]){ 1 }[-1]); // { dg-warning "array subscript -1 is below array bounds of 'int\\\[1]'" }
+ T ((int[]){ 1 }[0]);
+ T ((int[]){ 1 }[1]); // { dg-warning "array subscript 1 is above array bounds of 'int\\\[1]'" }
+ T ((int[]){ 1 }[INT_MIN]); // { dg-warning "array subscript -\[0-9\]+ is below array bounds of 'int\\\[1]'" }
+ T ((int[]){ 1 }[INT_MAX]); // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
+ T ((int[]){ 1 }[SIZE_MAX]); // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
+}
+
+
+void direct_idx_var (int i)
+{
+ T ((char[]){ }[i]); // { dg-warning "array subscript i is outside array bounds of 'char\\\[0]'" }
+ T ((int[]){ }[i]); // { dg-warning "array subscript i is outside array bounds of 'int\\\[0]'" }
+}
+
+
+void direct_idx_range (void)
+{
+ ptrdiff_t i = SR (-2, -1);
+
+ T ((int[]){ 1 }[i]); // { dg-warning "array subscript \[ \n\r]+ is outside array bounds of 'int\\\[0]'" "pr?????" { xfail *-*-* } }
+}
+
+
+#undef T
+#define T(idx, ...) do { \
+ int *p = (__VA_ARGS__); \
+ sink (p[idx]); \
+ } while (0)
+
+void ptr_idx_cst (void)
+{
+ T (-1, (int[]){ }); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
+ T ( 0, (int[]){ }); // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
+ T (+1, (int[]){ }); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
+
+ T (-1, (int[]){ 1 }); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" }
+ T ( 0, (int[]){ 1 });
+ T (+1, (int[]){ 1 }); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[1]'" }
+ T (INT_MIN, (int[]){ 1 }); // { dg-warning "array subscript -\[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { xfail ilp32 } }
+ T (INT_MAX, (int[]){ 1 }); // { dg-warning "array subscript \[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { target lp64 } }
+ // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" "ilp32" { target ilp32 } .-1 }
+ T (SIZE_MAX, (int[]){ 1 }); // { dg-warning "array subscript -?\[0-9\]+ is outside array bounds of 'int\\\[1]'" }
+}
+
+
+void ptr_idx_var (int i)
+{
+ T (i, (int[]){ }); // { dg-warning "array subscript \[^\n\r\]+ is outside array bounds of 'int\\\[0]'" }
+ T (i, (int[]){ 1 });
+ T (i, (int[]){ i, 1 });
+}
+
+void ptr_idx_range (void)
+{
+ ptrdiff_t i = SR (-2, -1);
+
+ T (i, (int[]){ }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[0]'" }
+ T (i, (int[]){ 1 }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
+ T (i, (int[]){ i }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
+
+ i = SR (0, 1);
+
+ T (i, (int[]){ }); // { dg-warning "array subscript \\\[0, 1] is outside array bounds of 'int\\\[0]'" }
+ T (i, (int[]){ 1 });
+
+ i = SR (1, 2);
+ T (i, (int[]){ 1 }); // { dg-warning "array subscript \\\[1, 2] is outside array bounds of 'int\\\[1]'" }
+
+ i = SR (2, 3);
+ T (i, (int[]){ 1, 2, 3 });
+
+ i = SR (3, 4);
+ T (i, (int[]){ 2, 3, 4 }); // { dg-warning "array subscript \\\[3, 4] is outside array bounds of 'int\\\[3]'" }
+}
--- /dev/null
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+ PR middle-end/85484 - missing -Wstringop-overflow for strcpy with
+ a string of non-const length
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* calloc (size_t, size_t);
+extern void* malloc (size_t);
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+extern char* strcpy (char*, const char*);
+extern size_t strlen (const char*);
+
+void sink (void*);
+
+
+void test_memcpy_nowarn (const void *s, int i, size_t n)
+{
+ sink (memcpy (calloc (1, 1), s, 1));
+ sink (memcpy (calloc (1, 2), s, 1));
+ sink (memcpy (calloc (2, 1), s, 1));
+ sink (memcpy (calloc (3, 1), s, 2));
+ sink (memcpy (calloc (3, 1), "12", 2));
+ sink (memcpy (calloc (3, 1), s, 3));
+ sink (memcpy (calloc (3, 1), "12", 3));
+ sink (memcpy (calloc (i, 1), s, 1));
+ sink (memcpy (calloc (n, 1), s, 1));
+ sink (memcpy (calloc (1, n), "", 1));
+ sink (memcpy (calloc (1, i), "", 1));
+ sink (memcpy (calloc (i, 1), "123", 3));
+ sink (memcpy (calloc (n, 1), "123", 3));
+ sink (memcpy (calloc (1, i), "123456", 7));
+ sink (memcpy (calloc (1, n), "123456", 7));
+ sink (memcpy (calloc (n, 1), s, 12345));
+ sink (memcpy (calloc (1, n), s, n - 1));
+ sink (memcpy (calloc (n, 1), s, n));
+
+ sink (memcpy ((char*)calloc (1, 1) + i, "123", 1));
+ sink (memcpy ((char*)calloc (n, 1) + i, "123", n));
+
+ sink (memcpy ((char*)calloc (1, 1) + i, s, 1));
+ sink (memcpy ((char*)calloc (n, 1) + i, s, n));
+
+ sink (memcpy (malloc (1), s, 1));
+ sink (memcpy (malloc (2), s, 1));
+ sink (memcpy (malloc (3), s, 2));
+ sink (memcpy (malloc (3), "12", 2));
+ sink (memcpy (malloc (3), s, 3));
+ sink (memcpy (malloc (3), "12", 3));
+ sink (memcpy (malloc (n), s, 1));
+ sink (memcpy (malloc (n), "", 1));
+ sink (memcpy (malloc (n), "123", 3));
+ sink (memcpy (malloc (n), "123456", 7));
+ sink (memcpy (malloc (n), s, 12345));
+ sink (memcpy (malloc (n), s, n - 1));
+ sink (memcpy (malloc (n), s, n));
+
+ {
+ const int a[] = { 1, 2, 3, 4 };
+ void *p = (char*)malloc (sizeof a);
+ memcpy (p, a, sizeof a);
+ sink (p);
+ }
+
+ {
+ const int a[] = { 1, 2, 3, 4, 5 };
+ size_t nelts = sizeof a / sizeof *a;
+ int vla[nelts];
+ memcpy (vla, a, nelts * sizeof *vla);
+ sink (vla);
+ }
+}
+
+
+void test_memcpy_warn (const int *s, size_t n)
+{
+ {
+ void *p = (char*)malloc (0);
+ memcpy (p, s, 1); // { dg-warning "writing 1 byte into a region of size 0" }
+ sink (p);
+ }
+
+ {
+ void *p = (char*)malloc (1);
+ memcpy (p, s, 2); // { dg-warning "writing 2 bytes into a region of size 1" }
+ sink (p);
+ }
+
+ {
+ void *p = (char*)malloc (2);
+ memcpy (p, s, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
+ sink (p);
+ }
+
+ {
+ void *p = (char*)malloc (3);
+ memcpy (p, s, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
+ sink (p);
+ }
+
+ {
+ const int a[] = { 1, 2, 3, 4 };
+ void *p = (char*)malloc (sizeof *a);
+ memcpy (p, a, sizeof a); // { dg-warning "" }
+ sink (p);
+ }
+
+ {
+ const int a[] = { 1, 2, 3, 4, 5 };
+ size_t nelts = sizeof a / sizeof *a;
+ char vla[nelts];
+ memcpy (vla, a, nelts * sizeof *a); // { dg-warning "" }
+ sink (vla);
+ }
+
+ {
+ void *p = malloc (n);
+ memcpy (p, s, n * sizeof *s); // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
+ sink (p);
+ }
+}
+
+void test_memset_nowarn (int x, size_t n)
+{
+ sink (memset (calloc (1, 1), x, 1));
+ sink (memset (calloc (1, 2), x, 1));
+ sink (memset (calloc (2, 1), x, 1));
+ sink (memset (calloc (3, 1), x, 2));
+ sink (memset (calloc (3, 1), x, 3));
+ sink (memset (calloc (n, 1), x, 1));
+ sink (memset (calloc (n, 1), x, 12345));
+ sink (memset (calloc (1, n), x, n - 1));
+ sink (memset (calloc (n, 1), x, n));
+
+ sink (memset (malloc (1), x, 1));
+ sink (memset (malloc (2), x, 1));
+ sink (memset (malloc (3), x, 2));
+ sink (memset (malloc (3), x, 3));
+ sink (memset (malloc (n), x, 1));
+ sink (memset (malloc (n), x, 12345));
+ sink (memset (malloc (n), x, n - 1));
+ sink (memset (malloc (n), x, n));
+
+ {
+ const int a[] = { 1, 2, 3, 4 };
+ void *p = (char*)malloc (sizeof a);
+ memset (p, x, sizeof a);
+ sink (p);
+ }
+
+ {
+ const int a[] = { 1, 2, 3, 4, 5 };
+ size_t nelts = sizeof a / sizeof *a;
+ int vla[nelts];
+ memset (vla, x, nelts * sizeof *vla);
+ sink (vla);
+ }
+}
+
+
+void test_memset_warn (int x, size_t n)
+{
+ {
+ void *p = (char*)malloc (0);
+ memset (p, x, 1); // { dg-warning "writing 1 byte into a region of size 0" }
+ sink (p);
+ }
+
+ {
+ void *p = (char*)malloc (1);
+ memset (p, x, 2); // { dg-warning "writing 2 bytes into a region of size 1" }
+ sink (p);
+ }
+
+ {
+ void *p = (char*)malloc (2);
+ memset (p, x, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
+ sink (p);
+ }
+
+ {
+ void *p = (char*)malloc (3);
+ memset (p, x, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
+ sink (p);
+ }
+
+ {
+ const int a[] = { 1, 2, 3, 4 };
+ void *p = (char*)malloc (sizeof *a);
+ memset (p, 0, sizeof a); // { dg-warning "" }
+ sink (p);
+ }
+
+ {
+ const int a[] = { 1, 2, 3, 4, 5 };
+ size_t nelts = sizeof a / sizeof *a;
+ char vla[nelts];
+ memset (vla, 0, nelts * sizeof *a); // { dg-warning "" }
+ sink (vla);
+ }
+
+ {
+ void *p = malloc (n);
+ memset (p, x, n * sizeof (int)); // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
+ sink (p);
+ }
+}
+
+
+void test_strcpy_nowarn (const char *s)
+{
+ {
+ const char a[] = "12";
+ int n = strlen (a);
+ char *t = (char*)calloc (2, n);
+ strcpy (t, a);
+ sink (t);
+ }
+
+ {
+ const char a[] = "123";
+ unsigned n = strlen (a) + 1;
+ char *t = (char*)calloc (n, 1);
+ strcpy (t, a);
+ sink (t);
+ }
+
+ {
+ const char a[] = "1234";
+ size_t n = strlen (a) * 2;
+ char *t = (char*)malloc (n);
+ strcpy (t, a);
+ sink (t);
+ }
+
+ {
+ const char a[] = "1234";
+ size_t len = strlen (a) + 1;
+ char vla[len];
+ strcpy (vla, a);
+ sink (vla);
+ }
+
+ {
+ size_t n = strlen (s) + 1;
+ char *t = (char*)malloc (n);
+ strcpy (t, s);
+ sink (t);
+ }
+}
+
+
+void test_strcpy_warn (const char *s)
+{
+ {
+ const char a[] = "123";
+ /* Verify that using signed int for the strlen result works (i.e.,
+ that the conversion from signed int to size_t doesn't prevent
+ the detection. */
+ int n = strlen (a);
+ char *t = (char*)calloc (n, 1); // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note" { xfail *-*-* } }
+ // { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note" { target *-*-* } .-1 }
+ strcpy (t, a); // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
+
+ sink (t);
+ }
+
+ {
+ const char a[] = "1234";
+ size_t n = strlen (a);
+ char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note" { xfail *-*-* } }
+ // { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note" { target *-*-* } .-1 }
+ strcpy (t, a); // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
+ sink (t);
+ }
+
+ // Exercise PR middle-end/85484.
+ {
+ size_t len = strlen (s);
+ char vla[len]; // { dg-message "at offset 0 to an object declared here" "vla note" }
+ strcpy (vla, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
+ sink (vla);
+ }
+
+ {
+ size_t n = strlen (s);
+ char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object allocated by 'malloc' here" "malloc note" }
+ strcpy (t, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
+ sink (t);
+ }
+}
--- /dev/null
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+#define NOIPA ATTR (noipa)
+
+extern void* alloca (size_t);
+extern void* calloc (size_t, size_t);
+extern void* malloc (size_t);
+
+extern ATTR (alloc_size (1), malloc) char* alloc1 (size_t);
+extern ATTR (alloc_size (1, 2), malloc) char* alloc2 (size_t, size_t);
+
+extern char* strcpy (char*, const char*);
+
+void sink (void*, ...);
+
+
+/* Verify warning in stores to an object of variable size N in a known
+ range, at an offset (N + I) with a constant I. */
+
+void same_size_and_offset_idx_cst (void)
+{
+#define T(size, off, idx) do { \
+ size_t n_ = size; \
+ ptrdiff_t i_ = idx; \
+ char *p_ = alloc1 (n_); \
+ p_ += off; \
+ p_[i_] = 0; \
+ sink (p_); \
+ } while (0)
+
+ {
+ const size_t n = UR (2, 3);
+
+ T (n, n, -4); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ T (n, n, -3);
+ T (n, n, -2);
+ T (n, n, -1);
+ T (n, n, 0);
+ T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ }
+
+ {
+ const size_t n = UR (3, 4);
+
+ T (n, n, -5); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ T (n, n, -4);
+ T (n, n, -3);
+ T (n, n, -2);
+ T (n, n, -1);
+ T (n, n, 0);
+ T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ }
+
+ {
+ const size_t n = UR (5, SIZE_MAX - 2);
+ T (n, n, -1);
+ T (n, n, -1);
+ T (n, n, -1);
+ T (n, n, -1);
+ }
+}
+
+
+/* Verify warning in stores to an object of variable size N in a known
+ range, at an offset (M + I) with a variable M in some range and
+ constant I. */
+
+void different_size_and_offset_idx_cst (void)
+{
+ {
+ const size_t n = UR (2, 3);
+ const size_t i = UR (1, 2);
+
+ T (n, i, -4); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ T (n, i, -3); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ T (n, i, -2);
+ T (n, i, -1);
+ T (n, i, 0);
+ T (n, i, 1);
+ T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ }
+
+ {
+ const size_t n = UR (3, 4);
+ const size_t i = UR (2, 5);
+
+ T (n, i, -6); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+
+ /* The offsets -5 and -4 are both necessarily invalid even if the sum
+ (i - 5) and (i - 4) are (or could be) in bounds because they imply
+ that the intermediate offset (p + i) is out of bounds. */
+ T (n, i, -5); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
+ T (n, i, -4); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
+ T (n, i, -3);
+ T (n, i, -2);
+ T (n, i, -1);
+ T (n, i, 0);
+ T (n, i, 1);
+ T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
+ // { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+ }
+}
+
+
+/* Verify warning in stores to an object of variable size N in a known
+ range, at an offset (M + I) with a variable M in some range and
+ constant I. */
+void different_size_and_offset_idx_var (void)
+{
+ {
+ const size_t n = UR (3, 4);
+ const size_t i = UR (1, 2);
+
+ T (n, i, SR (DIFF_MIN, 0));
+ T (n, i, SR ( -3, 0));
+ T (n, i, SR ( -1, 0));
+ T (n, i, SR ( 0, 1));
+ T (n, i, SR ( 1, 2));
+ T (n, i, SR ( 2, 3));
+ /* The warning is issued below but the offset and the size in
+ the note are wrong. See the FIXME in compute_objsize(). */
+ T (n, i, SR ( 3, 4)); // { dg-warning "\\\[-Wstringop-overflow" }
+ // { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
+ // { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
+ }
+}
+
+
+void ptr_add_2 (int n, int i0, int i1)
+{
+ if (n < 1 || 2 < n) n = 2;
+
+ if (i0 < 0 || 1 < i0) i0 = 0;
+ if (i1 < 1 || 2 < i1) i1 = 1;
+
+ char *p = (char*)__builtin_malloc (n);
+ char *q = p;
+
+ q += i0;
+ q[0] = 0; // p[0]
+ q += i1;
+ q[0] = 1; // p[1]
+ q[1] = 2; // p[2] // { dg-warning "\\\[-Wstringop-overflow" }
+
+ sink (p, q);
+}
+
+void ptr_add_3 (int n, int i0, int i1, int i2)
+{
+ if (n < 3 || 4 < n) n = 3;
+
+ if (i0 < 0 || 1 < i0) i0 = 0;
+ if (i1 < 1 || 2 < i1) i1 = 1;
+ if (i2 < 2 || 3 < i2) i2 = 2;
+
+ char *p = (char*)__builtin_malloc (n);
+ char *q = p;
+
+ q += i0;
+ q[0] = 0; // p[0]
+ q += i1;
+ q[0] = 1; // p[1]
+ q[1] = 2; // p[2]
+ q += i2;
+ q[0] = 3; // p[3]
+ q[1] = 4; // p[4] // { dg-warning "\\\[-Wstringop-overflow" }
+
+ sink (p, q);
+}
+
+void ptr_add_4 (int n, int i0, int i1, int i2, int i3)
+{
+ if (n < 7 || 8 < n) n = 7;
+
+ if (i0 < 0 || 1 < i0) i0 = 0;
+ if (i1 < 1 || 2 < i1) i1 = 1;
+ if (i2 < 2 || 3 < i2) i2 = 2;
+ if (i3 < 3 || 4 < i3) i3 = 3;
+
+ char *p = (char*)__builtin_malloc (n);
+ char *q = p;
+
+ q += i0;
+ q[0] = 0; // p[0]
+ q += i1;
+ q[0] = 1; // p[1]
+ q[1] = 2; // p[2]
+ q += i2;
+ q[0] = 3; // p[3]
+ q[1] = 4; // p[4]
+ q[2] = 5; // p[5]
+ q += i3;
+ q[0] = 6; // p[6]
+ q[1] = 7; // p[7]
+ q[2] = 8; // p[8] // { dg-warning "\\\[-Wstringop-overflow" }
+
+ sink (p, q);
+}
+
+void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
+{
+ if (n < 1 || 2 < n) n = 2;
+
+ char *p = (char*)__builtin_malloc (n);
+ char *q = p;
+
+ // The following isn't diagnosed due to a bug/limitation.
+ q += n; // N=1 N=2
+ q[-1] = 0; // p[0] p[1]
+ q[-2] = 1; // p[-1] p[0]
+ q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
+
+ /* The following isn't diagnosed because the warning doesn't recognize
+ the index below as necessarily having the same value as the size
+ argument to malloc. All it considers is the range. */
+ q[0] = 2; // { dg-warning "\\\[-Wstringop-overflow" "pr92937: store just past the end" { xfail *-*-* } }
+ q[1] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
+
+ sink (p, q);
+}
--- /dev/null
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+ Verify calls via function pointers.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+typedef __attribute__ ((alloc_size (1))) char* allocfn_t (unsigned);
+
+extern allocfn_t allocfn;
+
+void sink (void*);
+
+void direct_call (void)
+{
+ char *q = allocfn (0); // { dg-message "at offset 0 to an object with size 0 allocated by 'allocfn'" }
+ q[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
+ sink (q);
+}
+
+
+void local_ptr_call (void)
+{
+ allocfn_t *ptr = allocfn;
+ char *q = ptr (1); // { dg-message "at offset -1 to an object with size 1 allocated by 'allocfn'" }
+ q[0] = 0;
+ q[-1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
+ sink (q);
+}
+
+
+void global_ptr_call (void)
+{
+ extern allocfn_t *ptralloc;
+
+ allocfn_t *ptr = ptralloc;
+ char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptralloc'" }
+ q[0] = 0;
+ q[1] = 1;
+ q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
+ sink (q);
+}
+
+void global_ptr_array_call (void)
+{
+ extern allocfn_t * (arralloc[]);
+
+ allocfn_t *ptr = arralloc[0];
+ char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptr'" }
+ q[0] = 1;
+ q[1] = 2;
+ q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
+ sink (q);
+}
+
+
+struct S { allocfn_t *ptralloc; };
+
+void member_ptr_call (struct S *p)
+{
+ char *q = p->ptralloc (3); // { dg-message "at offset 5 to an object with size 3 allocated by 'ptralloc' here" }
+ q[0] = 0;
+ q[1] = 1;
+ q[2] = 2;
+ q[5] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
+ sink (q);
+}
+
strcpy (p, "Hello");
p = malloc1 (6);
strcpy (p, "Hello");
- strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+ strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6);
strcpy (p, "World");
- strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+ strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = calloc1 (2, 5);
strcpy (p, "World");
- strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+ strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
strcpy (p, "World");
- strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+ strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
}
void* call_xref12 (void)
{
void *p = xref12 (3);
- __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+ __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
return p;
}
void* call_falias_malloc (void)
{
char *p = falias_malloc ();
- __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+ __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
return p;
}
struct A *a = __builtin_malloc (sizeof *a * 2);
- memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
+ memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
escape (a, src);
/* At -Wstringop-overflow=1 the destination is considered to be
struct B *b = __builtin_malloc (sizeof *b * 2);
- memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" } */
+ memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
escape (b);
/* The following idiom of clearing multiple members of a struct is
unsigned n0, n1;
void*
-keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
+keep_strlen_calloc_store_cst_memset (int i, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
- p[1] = 'x';
+ p[i] = 'x';
__builtin_memset (p, 0, b);
}
void*
-keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
+keep_strlen_calloc_store_var_memset (int i, int x, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
- p[1] = x;
+ p[i] = x;
__builtin_memset (p, 0, b);
}
void*
-keep_strlen_calloc_store_memset_2 (int x, unsigned a, unsigned b, unsigned c)
+keep_strlen_calloc_store_memset_2 (int i, int x, unsigned a, unsigned b, unsigned c)
{
char *p = __builtin_calloc (a, 1);
- p[1] = x;
+ p[i] = x;
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
a (c);
a (c);
}
+
+// { dg-prune-output "\\\[-Wstringop-overflow" }
tree nonzero_chars;
/* Any of the corresponding pointers for querying alias oracle. */
tree ptr;
- /* This is used for two things:
+ /* STMT is used for two things:
- To record the statement that should be used for delayed length
computations. We maintain the invariant that all related strinfos
have delayed lengths or none do.
- - To record the malloc or calloc call that produced this result. */
+ - To record the malloc or calloc call that produced this result
+ to optimize away malloc/memset sequences. STMT is reset after
+ a calloc-allocated object has been stored a non-zero value into. */
gimple *stmt;
+ /* Set to the dynamic allocation statement for the object (alloca,
+ calloc, malloc, or VLA). Unlike STMT, once set for a strinfo
+ object, ALLOC doesn't change. */
+ gimple *alloc;
/* Pointer to '\0' if known, if NULL, it can be computed as
ptr + length. */
tree endptr;
static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
/* Sets MINMAX to either the constant value or the range VAL is in
- and returns true on success. When nonnull, uses RVALS to get
- VAL's range. Otherwise uses get_range_info. */
+ and returns either the constant value or VAL on success or null
+ when the range couldn't be determined. Uses RVALS when nonnull
+ to determine the range, otherwise get_range_info. */
-static bool
-get_range (tree val, wide_int minmax[2], const vr_values *rvals = NULL)
+tree
+get_range (tree val, wide_int minmax[2], const vr_values *rvals /* = NULL */)
{
- if (tree_fits_uhwi_p (val))
+ if (TREE_CODE (val) == INTEGER_CST)
{
minmax[0] = minmax[1] = wi::to_wide (val);
- return true;
+ return val;
}
if (TREE_CODE (val) != SSA_NAME)
- return false;
+ return NULL_TREE;
if (rvals)
{
= (CONST_CAST (class vr_values *, rvals)->get_value_range (val));
value_range_kind rng = vr->kind ();
if (rng != VR_RANGE || !range_int_cst_p (vr))
- return false;
+ return NULL_TREE;
minmax[0] = wi::to_wide (vr->min ());
minmax[1] = wi::to_wide (vr->max ());
- return true;
+ return val;
}
value_range_kind rng = get_range_info (val, minmax, minmax + 1);
if (rng == VR_RANGE)
- return true;
+ return val;
/* Do not handle anti-ranges and instead make use of the on-demand
VRP if/when it becomes available (hopefully in GCC 11). */
- return false;
+ return NULL_TREE;
}
/* Return:
/* Helper function for get_stridx. Return the strinfo index of the address
of EXP, which is available in PTR if nonnull. If OFFSET_OUT, it is
OK to return the index for some X <= &EXP and store &EXP - X in
- *OFFSET_OUT. */
+ *OFFSET_OUT. When nonnull uses RVALS to determine range information. */
static int
get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out,
to a known strinfo with an offset and OFFRNG is non-null, sets
both elements of the OFFRNG array to the range of the offset and
returns the index of the known strinfo. In this case the result
- must not be used in for functions that modify the string. */
+ must not be used in for functions that modify the string.
+ When nonnull, uses RVALS to determine range information. */
static int
-get_stridx (tree exp, wide_int offrng[2] = NULL)
+get_stridx (tree exp, wide_int offrng[2] = NULL, const vr_values *rvals = NULL)
{
if (offrng)
- offrng[0] = offrng[1] = wi::zero (TYPE_PRECISION (sizetype));
+ offrng[0] = offrng[1] = wi::zero (TYPE_PRECISION (ptrdiff_type_node));
if (TREE_CODE (exp) == SSA_NAME)
{
return the index corresponding to the SSA_NAME.
Do this irrespective of the whether the offset
is known. */
- if (get_range (off, offrng))
+ if (get_range (off, offrng, rvals))
{
/* When the offset range is known, increment it
it by the constant offset computed in prior
si->nonzero_chars = nonzero_chars;
si->ptr = ptr;
si->stmt = NULL;
+ si->alloc = NULL;
si->endptr = NULL_TREE;
si->refcount = 1;
si->idx = idx;
if (chainsi->nonzero_chars == NULL)
set_endptr_and_length (loc, chainsi, lhs);
break;
+ case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
case BUILT_IN_MALLOC:
break;
/* BUILT_IN_CALLOC always has si->nonzero_chars set. */
fprintf (fp, ", ptr = ");
print_generic_expr (fp, si->ptr);
}
- fprintf (fp, ", nonzero_chars = ");
- print_generic_expr (fp, si->nonzero_chars);
- if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+
+ if (si->nonzero_chars)
{
- value_range_kind rng = VR_UNDEFINED;
- wide_int min, max;
- if (rvals)
+ fprintf (fp, ", nonzero_chars = ");
+ print_generic_expr (fp, si->nonzero_chars);
+ if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
{
- const value_range_equiv *vr
- = CONST_CAST (class vr_values *, rvals)
- ->get_value_range (si->nonzero_chars);
- rng = vr->kind ();
- if (range_int_cst_p (vr))
+ value_range_kind rng = VR_UNDEFINED;
+ wide_int min, max;
+ if (rvals)
{
- min = wi::to_wide (vr->min ());
- max = wi::to_wide (vr->max ());
+ const value_range *vr
+ = CONST_CAST (class vr_values *, rvals)
+ ->get_value_range (si->nonzero_chars);
+ rng = vr->kind ();
+ if (range_int_cst_p (vr))
+ {
+ min = wi::to_wide (vr->min ());
+ max = wi::to_wide (vr->max ());
+ }
+ else
+ rng = VR_UNDEFINED;
}
else
- rng = VR_UNDEFINED;
- }
- else
- rng = get_range_info (si->nonzero_chars, &min, &max);
+ rng = get_range_info (si->nonzero_chars, &min, &max);
- if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
- {
- fprintf (fp, " %s[%llu, %llu]",
- rng == VR_RANGE ? "" : "~",
- (long long) min.to_uhwi (),
- (long long) max.to_uhwi ());
+ if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
+ {
+ fprintf (fp, " %s[%llu, %llu]",
+ rng == VR_RANGE ? "" : "~",
+ (long long) min.to_uhwi (),
+ (long long) max.to_uhwi ());
+ }
}
}
- fprintf (fp, " , refcount = %i", si->refcount);
+
+ fprintf (fp, ", refcount = %i", si->refcount);
if (si->stmt)
{
fprintf (fp, ", stmt = ");
print_gimple_expr (fp, si->stmt, 0);
}
+ if (si->alloc)
+ {
+ fprintf (fp, ", alloc = ");
+ print_gimple_expr (fp, si->alloc, 0);
+ }
if (si->writable)
fprintf (fp, ", writable");
+ if (si->dont_invalidate)
+ fprintf (fp, ", dont_invalidate");
if (si->full_string_p)
fprintf (fp, ", full_string_p");
if (strinfo *next = get_next_strinfo (si))
BITMAP_FREE (visited);
}
-/* Invalidate string length information for strings whose length
- might change due to stores in stmt, except those marked DON'T
- INVALIDATE. For string-modifying statements, ZERO_WRITE is
- set when the statement wrote only zeros. */
+/* Invalidate string length information for strings whose length might
+ change due to stores in STMT, except those marked DONT_INVALIDATE.
+ For string-modifying statements, ZERO_WRITE is set when the statement
+ wrote only zeros.
+ Returns true if any STRIDX_TO_STRINFO entries were considered
+ for invalidation. */
static bool
maybe_invalidate (gimple *stmt, bool zero_write = false)
{
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " %s()\n", __func__);
+ {
+ fprintf (dump_file, "%s called for ", __func__);
+ print_gimple_stmt (dump_file, stmt, TDF_LINENO);
+ }
strinfo *si;
- unsigned int i;
bool nonempty = false;
- for (i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i)
- if (si != NULL)
- {
- if (!si->dont_invalidate)
- {
- ao_ref r;
- tree size = NULL_TREE;
- if (si->nonzero_chars)
- {
- /* Include the terminating nul in the size of the string
- to consider when determining possible clobber. */
- tree type = TREE_TYPE (si->nonzero_chars);
- size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
- build_int_cst (type, 1));
- }
- ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
- if (stmt_may_clobber_ref_p_1 (stmt, &r))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- if (size && tree_fits_uhwi_p (size))
- fprintf (dump_file,
- " statement may clobber string "
- HOST_WIDE_INT_PRINT_UNSIGNED " long\n",
- tree_to_uhwi (size));
- else
- fprintf (dump_file,
- " statement may clobber string\n");
- }
+ for (unsigned i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i)
+ {
+ if (si == NULL || !POINTER_TYPE_P (TREE_TYPE (si->ptr)))
+ continue;
- set_strinfo (i, NULL);
- free_strinfo (si);
- continue;
- }
+ nonempty = true;
- if (size
- && !zero_write
- && si->stmt
- && is_gimple_call (si->stmt)
- && (DECL_FUNCTION_CODE (gimple_call_fndecl (si->stmt))
- == BUILT_IN_CALLOC))
- {
- /* If the clobber test above considered the length of
- the string (including the nul), then for (potentially)
- non-zero writes that might modify storage allocated by
- calloc consider the whole object and if it might be
- clobbered by the statement reset the allocation
- statement. */
- ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
- if (stmt_may_clobber_ref_p_1 (stmt, &r))
- si->stmt = NULL;
- }
- }
- si->dont_invalidate = false;
- nonempty = true;
- }
+ /* Unconditionally reset DONT_INVALIDATE. */
+ bool dont_invalidate = si->dont_invalidate;
+ si->dont_invalidate = false;
+
+ if (dont_invalidate)
+ continue;
+
+ ao_ref r;
+ tree size = NULL_TREE;
+ if (si->nonzero_chars)
+ {
+ /* Include the terminating nul in the size of the string
+ to consider when determining possible clobber. */
+ tree type = TREE_TYPE (si->nonzero_chars);
+ size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
+ build_int_cst (type, 1));
+ }
+ ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
+ if (stmt_may_clobber_ref_p_1 (stmt, &r))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fputs (" statement may clobber object ", dump_file);
+ print_generic_expr (dump_file, si->ptr);
+ if (size && tree_fits_uhwi_p (size))
+ fprintf (dump_file, " " HOST_WIDE_INT_PRINT_UNSIGNED
+ " bytes in size", tree_to_uhwi (size));
+ fputc ('\n', dump_file);
+ }
+
+ set_strinfo (i, NULL);
+ free_strinfo (si);
+ continue;
+ }
+
+ if (size
+ && !zero_write
+ && si->stmt
+ && is_gimple_call (si->stmt)
+ && (DECL_FUNCTION_CODE (gimple_call_fndecl (si->stmt))
+ == BUILT_IN_CALLOC))
+ {
+ /* If the clobber test above considered the length of
+ the string (including the nul), then for (potentially)
+ non-zero writes that might modify storage allocated by
+ calloc consider the whole object and if it might be
+ clobbered by the statement reset the statement. */
+ ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
+ if (stmt_may_clobber_ref_p_1 (stmt, &r))
+ si->stmt = NULL;
+ }
+ }
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " %s() ==> %i\n", __func__, nonempty);
+ fprintf (dump_file, "%s returns %i\n", __func__, nonempty);
return nonempty;
}
nsi = new_strinfo (si->ptr, si->idx, si->nonzero_chars, si->full_string_p);
nsi->stmt = si->stmt;
+ nsi->alloc = si->alloc;
nsi->endptr = si->endptr;
nsi->first = si->first;
nsi->prev = si->prev;
return false;
break;
+ case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
case BUILT_IN_CALLOC:
case BUILT_IN_MALLOC:
case BUILT_IN_MEMCPY:
}
/* Diagnose buffer overflow by a STMT writing LEN + PLUS_ONE bytes,
- into an object designated by the LHS of STMT otherise. */
+ either into a region allocated for the object SI when non-null,
+ or into an object designated by the LHS of STMT otherwise.
+ When nonnull uses RVALS to determine range information.
+ RAWMEM may be set by memcpy and other raw memory functions
+ to allow accesses across subobject boundaries. */
static void
maybe_warn_overflow (gimple *stmt, tree len,
const vr_values *rvals = NULL,
- strinfo *si = NULL, bool plus_one = false)
+ strinfo *si = NULL, bool plus_one = false,
+ bool rawmem = false)
{
if (!len || gimple_no_warning_p (stmt))
return;
+ /* The DECL of the function performing the write if it is done
+ by one. */
tree writefn = NULL_TREE;
- tree destdecl = NULL_TREE;
- tree destsize = NULL_TREE;
+ /* The destination expression involved in the store STMT. */
tree dest = NULL_TREE;
- /* The offset into the destination object set by compute_objsize
- but already reflected in DESTSIZE. */
- tree destoff = NULL_TREE;
-
if (is_gimple_assign (stmt))
- {
- dest = gimple_assign_lhs (stmt);
- if (TREE_NO_WARNING (dest))
- return;
-
- /* For assignments try to determine the size of the destination
- first. Set DESTOFF to the the offset on success. */
- tree off = size_zero_node;
- destsize = compute_objsize (dest, 1, &destdecl, &off);
- if (destsize)
- destoff = off;
- }
+ dest = gimple_assign_lhs (stmt);
else if (is_gimple_call (stmt))
{
- writefn = gimple_call_fndecl (stmt);
dest = gimple_call_arg (stmt, 0);
+ writefn = gimple_call_fndecl (stmt);
}
+ if (TREE_NO_WARNING (dest))
+ return;
+
/* The offset into the destination object computed below and not
- reflected in DESTSIZE. Either DESTOFF is set above or OFFRNG
- below. */
+ reflected in DESTSIZE. */
wide_int offrng[2];
- offrng[0] = wi::zero (TYPE_PRECISION (sizetype));
- offrng[1] = offrng[0];
+ const int off_prec = TYPE_PRECISION (ptrdiff_type_node);
+ offrng[0] = offrng[1] = wi::zero (off_prec);
- if (!destsize && !si && dest)
+ if (!si)
{
- /* For both assignments and calls, if no destination STRINFO was
- provided, try to get it from the DEST. */
+ /* If no destination STRINFO was provided try to get it from
+ the DEST argument. */
tree ref = dest;
- tree off = NULL_TREE;
if (TREE_CODE (ref) == ARRAY_REF)
{
/* Handle stores to VLAs (represented as
ARRAY_REF (MEM_REF (vlaptr, 0), N]. */
- off = TREE_OPERAND (ref, 1);
+ tree off = TREE_OPERAND (ref, 1);
ref = TREE_OPERAND (ref, 0);
+ if (get_range (off, offrng, rvals))
+ {
+ offrng[0] = offrng[0].from (offrng[0], off_prec, SIGNED);
+ offrng[1] = offrng[1].from (offrng[1], off_prec, SIGNED);
+ }
+ else
+ {
+ offrng[0] = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node));
+ offrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+ }
}
if (TREE_CODE (ref) == MEM_REF)
{
tree mem_off = TREE_OPERAND (ref, 1);
- if (off)
+ ref = TREE_OPERAND (ref, 0);
+ wide_int memoffrng[2];
+ if (get_range (mem_off, memoffrng, rvals))
{
- if (!integer_zerop (mem_off))
- return;
+ offrng[0] += memoffrng[0];
+ offrng[1] += memoffrng[1];
}
else
- off = mem_off;
- ref = TREE_OPERAND (ref, 0);
+ {
+ offrng[0] = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node));
+ offrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+ }
}
- if (int idx = get_stridx (ref, offrng))
+ wide_int stroffrng[2];
+ if (int idx = get_stridx (ref, stroffrng, rvals))
{
si = get_strinfo (idx);
- if (off && TREE_CODE (off) == INTEGER_CST)
+ offrng[0] += stroffrng[0];
+ offrng[1] += stroffrng[1];
+ }
+ }
+
+ /* The allocation call if the destination object was allocated
+ by one. */
+ gimple *alloc_call = NULL;
+ /* The DECL of the destination object if known and not dynamically
+ allocated. */
+ tree destdecl = NULL_TREE;
+ /* The offset into the destination object set by compute_objsize
+ but already reflected in DESTSIZE. */
+ tree destoff = NULL_TREE;
+ /* The size of the destination region (which is smaller than
+ the destination object for stores at a non-zero offset). */
+ tree destsize = NULL_TREE;
+
+ /* Compute the range of sizes of the destination object. The range
+ is constant for declared objects but may be a range for allocated
+ objects. */
+ const int siz_prec = TYPE_PRECISION (size_type_node);
+ wide_int sizrng[2];
+ if (si)
+ {
+ destsize = gimple_call_alloc_size (si->alloc, sizrng, rvals);
+ alloc_call = si->alloc;
+ }
+ else
+ offrng[0] = offrng[1] = wi::zero (off_prec);
+
+ if (!destsize)
+ {
+ /* If there is no STRINFO for DEST, fall back on compute_objsize. */
+ tree off = NULL_TREE;
+ destsize = compute_objsize (dest, rawmem ? 0 : 1, &destdecl, &off, rvals);
+ if (destsize)
+ {
+ /* Remember OFF but clear OFFRNG that may have been set above. */
+ destoff = off;
+ offrng[0] = offrng[1] = wi::zero (off_prec);
+
+ if (destdecl && TREE_CODE (destdecl) == SSA_NAME)
{
- wide_int wioff = wi::to_wide (off, offrng->get_precision ());
- offrng[0] += wioff;
- offrng[1] += wioff;
+ gimple *stmt = SSA_NAME_DEF_STMT (destdecl);
+ if (is_gimple_call (stmt))
+ alloc_call = stmt;
+ destdecl = NULL_TREE;
+ }
+
+ if (!get_range (destsize, sizrng, rvals))
+ {
+ /* On failure, rather than failing, set the maximum range
+ so that overflow in allocated objects whose size depends
+ on the strlen of the source can still be diagnosed
+ below. */
+ sizrng[0] = wi::zero (siz_prec);
+ sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (sizetype));
}
}
- else
- return;
}
+ if (!destsize)
+ {
+ sizrng[0] = wi::zero (siz_prec);
+ sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (sizetype));
+ };
+
+ sizrng[0] = sizrng[0].from (sizrng[0], siz_prec, UNSIGNED);
+ sizrng[1] = sizrng[1].from (sizrng[1], siz_prec, UNSIGNED);
+
/* Return early if the DESTSIZE size expression is the same as LEN
and the offset into the destination is zero. This might happen
in the case of a pair of malloc and memset calls to allocate
lenrng[1] += 1;
}
- /* Compute the range of sizes of the destination object. The range
- is constant for declared objects but may be a range for allocated
- objects. */
- wide_int sizrng[2];
- if (!destsize || !get_range (destsize, sizrng, rvals))
- {
- /* On failure, rather than bailing outright, use the maximum range
- so that overflow in allocated objects whose size depends on
- the strlen of the source can still be diagnosed below. */
- sizrng[0] = wi::zero (lenrng->get_precision ());
- sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
- }
-
- /* The size of the remaining space in the destination computed as
- the size of the latter minus the offset into it. */
+ /* The size of the remaining space in the destination computed
+ as the size of the latter minus the offset into it. */
wide_int spcrng[2] = { sizrng[0], sizrng[1] };
- if (wi::sign_mask (offrng[0]))
+ if (wi::neg_p (offrng[0]) && wi::neg_p (offrng[1]))
{
- /* FIXME: Handle negative offsets into allocated objects. */
- if (destdecl)
- spcrng[0] = spcrng[1] = wi::zero (spcrng->get_precision ());
- else
+ /* When the offset is negative and the size of the destination
+ object unknown there is little to do.
+ FIXME: Detect offsets that are necessarily invalid regardless
+ of the size of the object. */
+ if (!destsize)
return;
+
+ /* The remaining space is necessarily zero. */
+ spcrng[0] = spcrng[1] = wi::zero (spcrng->get_precision ());
+ }
+ else if (wi::neg_p (offrng[0]))
+ {
+ /* When the lower bound of the offset is negative but the upper
+ bound is not, reduce the upper bound of the remaining space
+ by the upper bound of the offset but leave the lower bound
+ unchanged. If that makes the upper bound of the space less
+ than the lower bound swap the two. */
+ spcrng[1] -= wi::ltu_p (offrng[1], spcrng[1]) ? offrng[1] : spcrng[1];
+ if (wi::ltu_p (spcrng[1], spcrng[0]))
+ std::swap (spcrng[1], spcrng[0]);
}
else
{
+ /* When the offset is positive reduce the remaining space by
+ the lower bound of the offset or clear it if the offset is
+ greater. */
spcrng[0] -= wi::ltu_p (offrng[0], spcrng[0]) ? offrng[0] : spcrng[0];
spcrng[1] -= wi::ltu_p (offrng[0], spcrng[1]) ? offrng[0] : spcrng[1];
}
- if (wi::leu_p (lenrng[0], spcrng[0]))
+ if (wi::leu_p (lenrng[0], spcrng[0])
+ && wi::leu_p (lenrng[1], spcrng[1]))
return;
if (lenrng[0] == spcrng[1]
if (!warned)
return;
+ gimple_set_no_warning (stmt, true);
+
/* If DESTOFF is not null, use it to format the offset value/range. */
if (destoff)
get_range (destoff, offrng);
offstr, destdecl);
return;
}
+
+ if (!alloc_call)
+ return;
+
+ tree allocfn = gimple_call_fndecl (alloc_call);
+ if (!allocfn)
+ {
+ /* For an ALLOC_CALL via a function pointer make a small effort
+ to determine the destination of the pointer. */
+ allocfn = gimple_call_fn (alloc_call);
+ if (TREE_CODE (allocfn) == SSA_NAME)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (allocfn);
+ if (gimple_assign_single_p (def))
+ {
+ tree rhs = gimple_assign_rhs1 (def);
+ if (DECL_P (rhs))
+ allocfn = rhs;
+ else if (TREE_CODE (rhs) == COMPONENT_REF)
+ allocfn = TREE_OPERAND (rhs, 1);
+ }
+ }
+ }
+
+ if (gimple_call_builtin_p (alloc_call, BUILT_IN_ALLOCA_WITH_ALIGN))
+ {
+ if (sizrng[0] == sizrng[1])
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object with size %wu declared here",
+ offstr, sizrng[0].to_uhwi ());
+ else if (sizrng[0] == 0)
+ {
+ /* Avoid printing impossible sizes. */
+ if (wi::ltu_p (sizrng[1],
+ wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2))
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object with size at most %wu "
+ "declared here",
+ offstr, sizrng[1].to_uhwi ());
+ else
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object declared here", offstr);
+ }
+ else
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object with size between %wu and %wu "
+ "declared here",
+ offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi ());
+ return;
+ }
+
+ if (sizrng[0] == sizrng[1])
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object with size %wu allocated by %qE here",
+ offstr, sizrng[0].to_uhwi (), allocfn);
+ else if (sizrng[0] == 0)
+ {
+ /* Avoid printing impossible sizes. */
+ if (wi::ltu_p (sizrng[1],
+ wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2))
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object with size at most %wu allocated "
+ "by %qD here",
+ offstr, sizrng[1].to_uhwi (), allocfn);
+ else
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object allocated by %qE here",
+ offstr, allocfn);
+ }
+ else
+ inform (gimple_location (alloc_call),
+ "at offset %s to an object with size between %wu and %wu "
+ "allocated by %qE here",
+ offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi (), allocfn);
}
/* Convenience wrapper for the above. */
static inline void
maybe_warn_overflow (gimple *stmt, unsigned HOST_WIDE_INT len,
- const vr_values *rvals = NULL,
- strinfo *si = NULL, bool plus_one = false)
+ const vr_values *rvals = NULL, strinfo *si = NULL,
+ bool plus_one = false, bool rawmem = false)
{
maybe_warn_overflow (stmt, build_int_cst (size_type_node, len), rvals,
- si, plus_one);
+ si, plus_one, rawmem);
}
/* Handle a strlen call. If strlen of the argument is known, replace
tree old = si->nonzero_chars;
si->nonzero_chars = lhs;
si->full_string_p = true;
- if (TREE_CODE (old) == INTEGER_CST)
+ if (old && TREE_CODE (old) == INTEGER_CST)
{
old = fold_convert_loc (loc, TREE_TYPE (lhs), old);
tree adj = fold_build2_loc (loc, MINUS_EXPR,
/* Handle a strcpy-like ({st{r,p}cpy,__st{r,p}cpy_chk}) call.
If strlen of the second argument is known, strlen of the first argument
is the same after this call. Furthermore, attempt to convert it to
- memcpy. */
+ memcpy. Uses RVALS to determine range information. */
static void
-handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
+ const vr_values *rvals)
{
int idx, didx;
tree src, dst, srclen, len, lhs, type, fn, oldlen;
else if (idx < 0)
srclen = build_int_cst (size_type_node, ~idx);
+ maybe_warn_overflow (stmt, srclen, rvals, olddsi, true);
+
+ if (olddsi != NULL)
+ adjust_last_stmt (olddsi, stmt, false);
+
loc = gimple_location (stmt);
if (srclen == NULL_TREE)
switch (bcode)
if (TREE_CODE (len) != SSA_NAME)
return false;
- gimple *def_stmt = SSA_NAME_DEF_STMT (len);
- if (!def_stmt)
+ if (TREE_CODE (src) == SSA_NAME)
+ {
+ gimple *srcdef = SSA_NAME_DEF_STMT (src);
+ if (is_gimple_assign (srcdef))
+ {
+ /* Handle bitwise AND used in conversions from wider size_t
+ to narrower unsigned types. */
+ tree_code code = gimple_assign_rhs_code (srcdef);
+ if (code == BIT_AND_EXPR
+ || code == NOP_EXPR)
+ return is_strlen_related_p (gimple_assign_rhs1 (srcdef), len);
+
+ return false;
+ }
+
+ if (gimple_call_builtin_p (srcdef, BUILT_IN_NORMAL))
+ {
+ /* If SRC is the result of a call to an allocation function
+ or strlen, use the function's argument instead. */
+ tree func = gimple_call_fndecl (srcdef);
+ built_in_function code = DECL_FUNCTION_CODE (func);
+ if (code == BUILT_IN_ALLOCA
+ || code == BUILT_IN_ALLOCA_WITH_ALIGN
+ || code == BUILT_IN_MALLOC
+ || code == BUILT_IN_STRLEN)
+ return is_strlen_related_p (gimple_call_arg (srcdef, 0), len);
+
+ /* FIXME: Handle other functions with attribute alloc_size. */
+ return false;
+ }
+ }
+
+ gimple *lendef = SSA_NAME_DEF_STMT (len);
+ if (!lendef)
return false;
- if (is_gimple_call (def_stmt))
+ if (is_gimple_call (lendef))
{
- tree func = gimple_call_fndecl (def_stmt);
- if (!valid_builtin_call (def_stmt)
+ tree func = gimple_call_fndecl (lendef);
+ if (!valid_builtin_call (lendef)
|| DECL_FUNCTION_CODE (func) != BUILT_IN_STRLEN)
return false;
- tree arg = gimple_call_arg (def_stmt, 0);
+ tree arg = gimple_call_arg (lendef, 0);
return is_strlen_related_p (src, arg);
}
- if (!is_gimple_assign (def_stmt))
+ if (!is_gimple_assign (lendef))
return false;
- tree_code code = gimple_assign_rhs_code (def_stmt);
- tree rhs1 = gimple_assign_rhs1 (def_stmt);
+ tree_code code = gimple_assign_rhs_code (lendef);
+ tree rhs1 = gimple_assign_rhs1 (lendef);
tree rhstype = TREE_TYPE (rhs1);
if ((TREE_CODE (rhstype) == POINTER_TYPE && code == POINTER_PLUS_EXPR)
return is_strlen_related_p (src, rhs1);
}
- if (tree rhs2 = gimple_assign_rhs2 (def_stmt))
+ if (tree rhs2 = gimple_assign_rhs2 (lendef))
{
/* Integer subtraction is considered strlen-related when both
arguments are integers and second one is strlen-related. */
/* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
If strlen of the second argument is known and length of the third argument
is that plus one, strlen of the first argument is the same after this
- call. */
+ call. Uses RVALS to determine range information. */
static void
-handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
+ const vr_values *rvals)
{
- int idx, didx;
- tree src, dst, len, lhs, oldlen, newlen;
+ tree lhs, oldlen, newlen;
gimple *stmt = gsi_stmt (*gsi);
- strinfo *si, *dsi, *olddsi;
+ strinfo *si, *dsi;
- len = gimple_call_arg (stmt, 2);
- src = gimple_call_arg (stmt, 1);
- dst = gimple_call_arg (stmt, 0);
- idx = get_stridx (src);
- if (idx == 0)
- return;
+ tree len = gimple_call_arg (stmt, 2);
+ tree src = gimple_call_arg (stmt, 1);
+ tree dst = gimple_call_arg (stmt, 0);
- didx = get_stridx (dst);
- olddsi = NULL;
+ int didx = get_stridx (dst);
+ strinfo *olddsi = NULL;
if (didx > 0)
olddsi = get_strinfo (didx);
else if (didx < 0)
return;
if (olddsi != NULL
- && tree_fits_uhwi_p (len)
&& !integer_zerop (len))
- adjust_last_stmt (olddsi, stmt, false);
+ {
+ maybe_warn_overflow (stmt, len, rvals, olddsi, false, true);
+ adjust_last_stmt (olddsi, stmt, false);
+ }
+
+ int idx = get_stridx (src);
+ if (idx == 0)
+ return;
bool full_string_p;
if (idx > 0)
gimple_set_no_warning (stmt, true);
}
-/* Handle a call to malloc or calloc. */
+/* Handle a call to an allocation function like alloca, malloc or calloc,
+ or an ordinary allocation function declared with attribute alloc_size. */
static void
-handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_alloc_call (enum built_in_function bcode, gimple_stmt_iterator *gsi)
{
gimple *stmt = gsi_stmt (*gsi);
tree lhs = gimple_call_lhs (stmt);
length = build_int_cst (size_type_node, 0);
strinfo *si = new_strinfo (lhs, idx, length, length != NULL_TREE);
if (bcode == BUILT_IN_CALLOC)
- si->endptr = lhs;
+ {
+ /* Only set STMT for calloc and malloc. */
+ si->stmt = stmt;
+ /* Only set ENDPTR for calloc. */
+ si->endptr = lhs;
+ }
+ else if (bcode == BUILT_IN_MALLOC)
+ si->stmt = stmt;
+
+ /* Set ALLOC is set for all allocation functions. */
+ si->alloc = stmt;
set_strinfo (idx, si);
si->writable = true;
- si->stmt = stmt;
si->dont_invalidate = true;
}
/* Handle a call to memset.
After a call to calloc, memset(,0,) is unnecessary.
memset(malloc(n),0,n) is calloc(n,1).
- return true when the call is transformed, false otherwise. */
+ return true when the call is transformed, false otherwise.
+ When nonnull uses RVALS to determine range information. */
static bool
-handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write)
+handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
+ const vr_values *rvals)
{
- gimple *stmt2 = gsi_stmt (*gsi);
- if (!integer_zerop (gimple_call_arg (stmt2, 1)))
- return false;
-
- /* Let the caller know the memset call cleared the destination. */
- *zero_write = true;
-
- tree ptr = gimple_call_arg (stmt2, 0);
- int idx1 = get_stridx (ptr);
+ gimple *memset_stmt = gsi_stmt (*gsi);
+ tree ptr = gimple_call_arg (memset_stmt, 0);
+ /* Set to the non-constant offset added to PTR. */
+ wide_int offrng[2];
+ int idx1 = get_stridx (ptr, offrng, rvals);
if (idx1 <= 0)
return false;
strinfo *si1 = get_strinfo (idx1);
if (!si1)
return false;
- gimple *stmt1 = si1->stmt;
- if (!stmt1 || !is_gimple_call (stmt1))
+ gimple *alloc_stmt = si1->alloc;
+ if (!alloc_stmt || !is_gimple_call (alloc_stmt))
+ return false;
+ tree callee1 = gimple_call_fndecl (alloc_stmt);
+ if (!valid_builtin_call (alloc_stmt))
+ return false;
+ tree alloc_size = gimple_call_arg (alloc_stmt, 0);
+ tree memset_size = gimple_call_arg (memset_stmt, 2);
+
+ /* Check for overflow. */
+ maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, true);
+
+ /* Bail when there is no statement associated with the destination
+ (the statement may be null even when SI1->ALLOC is not). */
+ if (!si1->stmt)
return false;
- tree callee1 = gimple_call_fndecl (stmt1);
- if (!valid_builtin_call (stmt1))
+
+ /* Avoid optimizing if store is at a variable offset from the beginning
+ of the allocated object. */
+ if (offrng[0] != 0 || offrng[0] != offrng[1])
return false;
+
+ /* Bail when the call writes a non-zero value. */
+ if (!integer_zerop (gimple_call_arg (memset_stmt, 1)))
+ return false;
+
+ /* Let the caller know the memset call cleared the destination. */
+ *zero_write = true;
+
enum built_in_function code1 = DECL_FUNCTION_CODE (callee1);
- tree size = gimple_call_arg (stmt2, 2);
if (code1 == BUILT_IN_CALLOC)
- /* Not touching stmt1 */ ;
+ /* Not touching alloc_stmt */ ;
else if (code1 == BUILT_IN_MALLOC
- && operand_equal_p (gimple_call_arg (stmt1, 0), size, 0))
+ && operand_equal_p (memset_size, alloc_size, 0))
{
- gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1);
+ /* Replace the malloc + memset calls with calloc. */
+ gimple_stmt_iterator gsi1 = gsi_for_stmt (si1->stmt);
update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2,
- size, build_one_cst (size_type_node));
+ alloc_size, build_one_cst (size_type_node));
si1->nonzero_chars = build_int_cst (size_type_node, 0);
si1->full_string_p = true;
si1->stmt = gsi_stmt (gsi1);
}
else
return false;
- tree lhs = gimple_call_lhs (stmt2);
- unlink_stmt_vdef (stmt2);
+ tree lhs = gimple_call_lhs (memset_stmt);
+ unlink_stmt_vdef (memset_stmt);
if (lhs)
{
gimple *assign = gimple_build_assign (lhs, ptr);
else
{
gsi_remove (gsi, true);
- release_defs (stmt2);
+ release_defs (memset_stmt);
}
return true;
OFFSET and NBYTES are the offset into the representation and
the size of the access to it determined from a MEM_REF or zero
for other expressions.
- Avoid recursing deeper than the limits in SNLIM allow.
+ Uses RVALS to determine range information.
+ Avoids recursing deeper than the limits in SNLIM allow.
Returns true on success and false otherwise. */
static bool
if (maxlen + 1 < nbytes)
return false;
+ if (!nbytes
+ && TREE_CODE (si->ptr) == SSA_NAME
+ && !POINTER_TYPE_P (TREE_TYPE (si->ptr)))
+ {
+ /* SI->PTR is an SSA_NAME with a DEF_STMT like
+ _1 = MEM <unsigned int> [(char * {ref-all})s_4(D)]; */
+ gimple *stmt = SSA_NAME_DEF_STMT (exp);
+ if (gimple_assign_single_p (stmt)
+ && gimple_assign_rhs_code (stmt) == MEM_REF)
+ {
+ tree rhs = gimple_assign_rhs1 (stmt);
+ if (tree refsize = TYPE_SIZE_UNIT (TREE_TYPE (rhs)))
+ if (tree_fits_uhwi_p (refsize))
+ {
+ nbytes = tree_to_uhwi (refsize);
+ maxlen = nbytes;
+ }
+ }
+
+ if (!nbytes)
+ return false;
+ }
+
if (nbytes <= minlen)
*nulterm = false;
lenrange[1] = maxlen;
if (lenrange[2] < nbytes)
- (lenrange[2] = nbytes);
+ lenrange[2] = nbytes;
/* Since only the length of the string are known and not its contents,
clear ALLNUL and ALLNONNUL purely on the basis of the length. */
the next statement in the basic block and false otherwise. */
static bool
-handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rvals)
+handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
+ const vr_values *rvals)
{
int idx = -1;
strinfo *si = NULL;
}
/* Check the built-in call at GSI for validity and optimize it.
+ Uses RVALS to determine range information.
Return true to let the caller advance *GSI to the next statement
in the basic block and false otherwise. */
static bool
-strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
- bool *zero_write,
+strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
const vr_values *rvals)
{
gimple *stmt = gsi_stmt (*gsi);
+ if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+ {
+ tree fntype = gimple_call_fntype (stmt);
+ if (fntype && lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (fntype)))
+ handle_alloc_call (BUILT_IN_NONE, gsi);
+ }
+
/* When not optimizing we must be checking printf calls which
we do even for user-defined functions when they are declared
with attribute format. */
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY:
case BUILT_IN_STPCPY_CHK:
- handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
+ handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi, rvals);
break;
case BUILT_IN_STRNCAT:
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY:
case BUILT_IN_MEMPCPY_CHK:
- handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
+ handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi, rvals);
break;
case BUILT_IN_STRCAT:
case BUILT_IN_STRCAT_CHK:
handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
break;
+ case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
case BUILT_IN_MALLOC:
case BUILT_IN_CALLOC:
- handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
+ handle_alloc_call (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MEMSET:
- if (handle_builtin_memset (gsi, zero_write))
+ if (handle_builtin_memset (gsi, zero_write, rvals))
return false;
break;
case BUILT_IN_MEMCMP:
If GSI's basic block needs clean-up of EH, set *CLEANUP_EH to true. */
static void
-handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh)
+handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
+ const vr_values *rvals)
{
gimple *stmt = gsi_stmt (*gsi);
tree lhs = gimple_assign_lhs (stmt);
}
}
}
+ else if (code == MEM_REF && TREE_CODE (lhs) == SSA_NAME)
+ {
+ if (int idx = new_stridx (lhs))
+ {
+ /* Record multi-byte assignments from MEM_REFs. */
+ bool storing_all_nonzero_p;
+ bool storing_all_zeros_p;
+ bool full_string_p;
+ unsigned lenrange[] = { UINT_MAX, 0, 0 };
+ tree rhs = gimple_assign_rhs1 (stmt);
+ const bool ranges_valid
+ = count_nonzero_bytes (rhs, lenrange, &full_string_p,
+ &storing_all_zeros_p, &storing_all_nonzero_p,
+ rvals);
+ if (ranges_valid)
+ {
+ tree length = build_int_cst (sizetype, lenrange[0]);
+ strinfo *si = new_strinfo (lhs, idx, length, full_string_p);
+ set_strinfo (idx, si);
+ si->writable = true;
+ si->dont_invalidate = true;
+ maybe_warn_overflow (stmt, lenrange[2], rvals);
+ }
+ }
+ }
if (strlen_to_stridx)
{
}
else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (lhs_type))
/* Handle assignment to a character. */
- handle_integral_assign (gsi, cleanup_eh);
+ handle_integral_assign (gsi, cleanup_eh, rvals);
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
{
tree type = TREE_TYPE (lhs);
if (TREE_CODE (type) == ARRAY_TYPE)
type = TREE_TYPE (type);
- bool is_char_store = is_char_type (type);
- if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
- {
- /* To consider stores into char objects via integer types
- other than char but not those to non-character objects,
- determine the type of the destination rather than just
- the type of the access. */
- tree ref = TREE_OPERAND (lhs, 0);
- type = TREE_TYPE (ref);
- if (TREE_CODE (type) == POINTER_TYPE)
- type = TREE_TYPE (type);
- if (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
- if (is_char_type (type))
- is_char_store = true;
- }
+ bool is_char_store = is_char_type (type);
+ if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
+ {
+ /* To consider stores into char objects via integer types
+ other than char but not those to non-character objects,
+ determine the type of the destination rather than just
+ the type of the access. */
+ for (int i = 0; i != 2; ++i)
+ {
+ tree ref = TREE_OPERAND (lhs, i);
+ type = TREE_TYPE (ref);
+ if (TREE_CODE (type) == POINTER_TYPE)
+ type = TREE_TYPE (type);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+ if (is_char_type (type))
+ {
+ is_char_store = true;
+ break;
+ }
+ }
+ }
/* Handle a single or multibyte assignment. */
if (is_char_store && !handle_store (gsi, &zero_write, rvals))
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
-struct c_strlen_data;
class vr_values;
+extern tree get_range (tree, wide_int[2], const vr_values * = NULL);
+
+struct c_strlen_data;
extern void get_range_strlen_dynamic (tree , c_strlen_data *, const vr_values *);
/* APIs internal to strlen pass. Defined in in gimple-ssa-sprintf.c. */
determine the size of an initialized flexible array member.
If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
an interior zero-length array.
- Returns the size (which might be zero for an object with
- an uninitialized flexible array member) or null if the size
+ Returns the size as sizetype (which might be zero for an object
+ with an uninitialized flexible array member) or null if the size
cannot be determined. */
tree
memsz64 -= baseoff;
return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
}
- return integer_zero_node;
+ return size_zero_node;
}
/* Return "don't know" for an external non-array object since its
&& DECL_EXTERNAL (base)
&& (!typematch
|| TREE_CODE (basetype) != ARRAY_TYPE)
- ? NULL_TREE : integer_zero_node);
+ ? NULL_TREE : size_zero_node);
}
/* Return the machine mode of T. For vectors, returns the mode of the