+2019-01-17 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/88800
+ * gimple-fold.c (gimple_fold_builtin_memory_op): Avoid checking
+ NO_WARNING bit here. Avoid folding out-of-bounds calls.
+ * gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Remove
+ redundant argument. Add new argument and issue diagnostics under
+ its control. Detect out-of-bounds access even with warnings
+ disabled.
+ (check_bounds_or_overlap): Change return type. Add argument.
+ (wrestrict_dom_walker::check_call): Adjust.
+ * gimple-ssa-warn-restrict.h (check_bounds_or_overlap): Add argument.
+ * tree-ssa-strlen.c (handle_builtin_strcpy): Adjust to change in
+ check_bounds_or_overlap's return value.
+ (handle_builtin_stxncpy): Same.
+ (handle_builtin_strcat): Same.
+
2019-01-17 Andrew Stubbs <ams@codesourcery.com>
Kwok Cheung Yeung <kcy@codesourcery.com>
Julian Brown <julian@codesourcery.com>
tree destvar, srcvar;
location_t loc = gimple_location (stmt);
- bool nowarn = gimple_no_warning_p (stmt);
-
/* If the LEN parameter is a constant zero or in range where
the only valid value is zero, return DEST. */
if (size_must_be_zero_p (len))
unsigned ilen = tree_to_uhwi (len);
if (pow2p_hwi (ilen))
{
- /* Detect invalid bounds and overlapping copies and issue
- either -Warray-bounds or -Wrestrict. */
- if (!nowarn
- && check_bounds_or_overlap (as_a <gcall *>(stmt),
- dest, src, len, len))
- gimple_set_no_warning (stmt, true);
+ /* Detect out-of-bounds accesses without issuing warnings.
+ Avoid folding out-of-bounds copies but to avoid false
+ positives for unreachable code defer warning until after
+ DCE has worked its magic.
+ -Wrestrict is still diagnosed. */
+ if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+ dest, src, len, len,
+ false, false))
+ if (warning != OPT_Wrestrict)
+ return false;
scalar_int_mode mode;
tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
}
}
- /* Detect invalid bounds and overlapping copies and issue either
- -Warray-bounds or -Wrestrict. */
- if (!nowarn)
- check_bounds_or_overlap (as_a <gcall *>(stmt), dest, src, len, len);
+ /* Same as above, detect out-of-bounds accesses without issuing
+ warnings. Avoid folding out-of-bounds copies but to avoid
+ false positives for unreachable code defer warning until
+ after DCE has worked its magic.
+ -Wrestrict is still diagnosed. */
+ if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+ dest, src, len, len,
+ false, false))
+ if (warning != OPT_Wrestrict)
+ return false;
gimple *new_stmt;
if (is_gimple_reg_type (TREE_TYPE (srcvar)))
if (!acs.overlap ())
return false;
+ if (gimple_no_warning_p (call))
+ return true;
+
/* For convenience. */
const builtin_memref &dstref = *acs.dstref;
const builtin_memref &srcref = *acs.srcref;
return true;
}
-/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+/* Validate REF offsets in an expression passed as an argument to a CALL
to a built-in function FUNC to make sure they are within the bounds
of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
Both initial values of the offsets and their final value computed by
static bool
maybe_diag_offset_bounds (location_t loc, gimple *call, tree func, int strict,
- tree expr, const builtin_memref &ref)
+ const builtin_memref &ref, bool do_warn)
{
- if (!warn_array_bounds)
+ /* Check for out-bounds pointers regardless of warning options since
+ the result is used to make codegen decisions. */
+ offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+ tree oobref = ref.offset_out_of_bounds (strict, ooboff);
+ if (!oobref)
return false;
- if (ref.ref && TREE_NO_WARNING (ref.ref))
+ /* Return true without issuing a warning. */
+ if (!do_warn)
+ return true;
+
+ if (!warn_array_bounds)
return false;
- offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
- tree oobref = ref.offset_out_of_bounds (strict, ooboff);
- if (!oobref)
+ if (ref.ref && TREE_NO_WARNING (ref.ref))
return false;
- if (EXPR_HAS_LOCATION (expr))
- loc = EXPR_LOCATION (expr);
+ if (EXPR_HAS_LOCATION (ref.ptr))
+ loc = EXPR_LOCATION (ref.ptr);
loc = expansion_point_location_if_in_system_header (loc);
|| (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
return;
- if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+ if (!check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
return;
/* Avoid diagnosing the call again. */
/* Attempt to detect and diagnose invalid offset bounds and (except for
memmove) overlapping copy in a call expression EXPR from SRC to DST
and DSTSIZE and SRCSIZE bytes, respectively. Both DSTSIZE and
- SRCSIZE may be NULL. Return false when one or the other has been
- detected and diagnosed, true otherwise. */
+ SRCSIZE may be NULL. DO_WARN is false to detect either problem
+ without issue a warning. Return the OPT_Wxxx constant corresponding
+ to the warning if one has been detected and zero otherwise. */
-bool
+int
check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
- tree srcsize, bool bounds_only /* = false */)
+ tree srcsize, bool bounds_only /* = false */,
+ bool do_warn /* = true */)
{
location_t loc = gimple_nonartificial_location (call);
loc = expansion_point_location_if_in_system_header (loc);
/* Validate offsets first to make sure they are within the bounds
of the destination object if its size is known, or PTRDIFF_MAX
otherwise. */
- if (maybe_diag_offset_bounds (loc, call, func, strict, dst, dstref)
- || maybe_diag_offset_bounds (loc, call, func, strict, src, srcref))
+ if (maybe_diag_offset_bounds (loc, call, func, strict, dstref, do_warn)
+ || maybe_diag_offset_bounds (loc, call, func, strict, srcref, do_warn))
{
- gimple_set_no_warning (call, true);
- return false;
+ if (do_warn)
+ gimple_set_no_warning (call, true);
+ return OPT_Warray_bounds;
}
bool check_overlap
&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
if (!check_overlap)
- return true;
+ return 0;
if (operand_equal_p (dst, src, 0))
{
"%G%qD source argument is the same as destination",
call, func);
gimple_set_no_warning (call, true);
- return false;
+ return OPT_Wrestrict;
}
- return true;
+ return 0;
}
/* Return false when overlap has been detected. */
if (maybe_diag_overlap (loc, call, acs))
{
gimple_set_no_warning (call, true);
- return false;
+ return OPT_Wrestrict;
}
- return true;
+ return 0;
}
gimple_opt_pass *
#ifndef GIMPLE_SSA_WARN_RESTRICT_H
-extern bool check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
- bool = false);
+extern int check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
+ bool = false, bool = true);
#endif /* GIMPLE_SSA_WARN_RESTRICT_H */
+2019-01-17 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/88800
+ * c-c++-common/Wrestrict.c: Adjust.
+ * gcc.dg/Warray-bounds-37.c: New test.
+ * gcc.dg/builtin-memcpy-2.c: New test.
+ * gcc.dg/builtin-memcpy.c: New test.
+
2019-01-17 Tamar Christina <tamar.christina@arm.com>
PR target/88850
T ("012", a, a + 3);
/* The following doesn't overlap but it should trigger -Wstringop-overflow
for reading past the end. */
- T ("012", a, a + sizeof a);
+ T ("012", a, a + sizeof a); /* { dg-warning "\\\[-Wstringop-overflow" "pr81437" { xfail *-*-* } } */
/* The terminating nul written to d[2] overwrites s[0]. */
T ("0123", a, a + 2); /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
T ("012", a + 2, a); /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
T ("012", a + 3, a); /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
T ("012", a + 4, a);
- /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+ /* The following doesn't overlap but it triggers -Wstringop-overflow
for writing past the end. */
- T ("012", a + sizeof a, a);
+ T ("012", a + sizeof a, a); /* { dg-warning "\\\[-Wstringop-overflow" } */
}
/* Exercise strcpy with constant or known arguments offset by a range.
--- /dev/null
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+ branch
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+extern void* memmove (void*, const void*, __SIZE_TYPE__);
+
+struct A
+{
+ const char *s;
+ int n;
+};
+
+void f (void*);
+
+struct B
+{
+ char d[5];
+ int n;
+};
+
+__attribute__ ((always_inline)) inline void
+g (struct B *p, struct A a)
+{
+ int i = a.n;
+ if (i <= 5)
+ p->n = i;
+ else {
+ p->n = -1;
+ f (p);
+ }
+
+ if (p->n >= 0)
+ memmove (p->d, a.s, a.n); /* { dg-bogus "\\\[-Warray-bounds" } */
+}
+
+void h (void)
+{
+ char c[8] = "";
+
+ struct A a;
+ a.s = c;
+ a.n = 8;
+
+ struct B b;
+ g (&b, a);
+}
--- /dev/null
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+ branch
+ Verify that out-of-bounds memcpy calls are not folded even when
+ warnings are disabled.
+ { dg-do compile }
+ { dg-options "-O2 -w -fdump-tree-optimized" } */
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+char a1[1], a2[2], a4[4], a8[8], a16[16], a32[32];
+
+void f1 (const void *p)
+{
+ memcpy (a1, p, sizeof a1 * 2);
+}
+
+void f2 (const void *p)
+{
+ memcpy (a2, p, sizeof a2 * 2);
+}
+
+void f4 (const void *p)
+{
+ memcpy (a4, p, sizeof a4 * 2);
+}
+
+void f8 (const void *p)
+{
+ memcpy (a8, p, sizeof a8 * 2);
+}
+
+void f16 (const void *p)
+{
+ memcpy (a16, p, sizeof a16 * 2);
+}
+
+void f32 (const void *p)
+{
+ memcpy (a32, p, sizeof a32 * 2);
+}
+
+/* { dg-final { scan-tree-dump-times "memcpy" 6 "optimized" } } */
--- /dev/null
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+ branch
+ Verify that out-of-bounds memcpy calls are not folded when warnings are
+ enabled (builtin-memcpy-2.c verifies they're not folded with warnings
+ disabled).
+ { dg-do compile }
+ { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+char a1[1], a2[2], a4[4], a8[8], a16[16], a32[32];
+
+void f1 (const void *p)
+{
+ memcpy (a1, p, sizeof a1 * 2); /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f2 (const void *p)
+{
+ memcpy (a2, p, sizeof a2 * 2); /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f4 (const void *p)
+{
+ memcpy (a4, p, sizeof a4 * 2); /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f8 (const void *p)
+{
+ memcpy (a8, p, sizeof a8 * 2); /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f16 (const void *p)
+{
+ memcpy (a16, p, sizeof a16 * 2); /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f32 (const void *p)
+{
+ memcpy (a32, p, sizeof a32 * 2); /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+/* { dg-final { scan-tree-dump-times "memcpy" 6 "optimized" } } */
if (const strinfo *chksi = olddsi ? olddsi : dsi)
if (si
- && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+ && check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
{
gimple_set_no_warning (stmt, true);
set_no_warning = true;
else
srcsize = NULL_TREE;
- if (!check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
+ if (check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
{
gimple_set_no_warning (stmt, true);
return;
tree sptr = si && si->ptr ? si->ptr : src;
- if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+ if (check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
{
gimple_set_no_warning (stmt, true);
set_no_warning = true;
tree sptr = si && si->ptr ? si->ptr : src;
- if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
+ if (check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
{
gimple_set_no_warning (stmt, true);
set_no_warning = true;