On 08/09/2017 10:14 AM, Jeff Law wrote:
On 08/06/2017 05:08 PM, Martin Sebor wrote:
Well, simply because the way as implemented isn't a must-alias query
but maybe one that's good enough for warnings (reduces false positives
but surely doesn't eliminate them).
I'm very interested in reducing the rate of false positives in
these and all other warnings. As I mentioned in my comments,
I did my best to weed them out of the implementation by building
GDB, Glibc, Busybox, and the Linux kernel. That of course isn't
a guarantee that there aren't any. But the first implementation
of any non-trivial feature is never perfect, and hardly any
warning of sufficient complexity is free of false positives, no
matter here it's implemented (the middle-end, front-end, or
a standalone static analysis tool). If you spotted some cases
I had missed I'd certainly be grateful for examples. Otherwise,
they will undoubtedly be reported as more software is exposed
to the warning and, if possible, fixed, as happens with all
other warnings.
I think Richi is saying that the must alias query you've built isn't
proper/correct. It's less about false positives for Richi and more
about building a proper must alias query if I understand him correctly.
I suspect he's also saying that you can't reasonably build must-alias on
top of a may-alias query framework. They're pretty different queries.
If you need something that is close to, but not quite a must alias
query, then you're going to have to make a argument for that and you
can't call it a must alias query.
Attached is an updated and simplified patch that avoids making
changes to any of the may-alias functions. It turns out that
all the information the logic needs to determine the overlap
is already in the ao_ref structures populated by
ao_ref_init_from_ptr_and_size. The only changes to the pass
are the enhancement to ao_ref_init_from_ptr_and_size to make
use of range info and the addition of the two new functions
used by the -Wrestrict clients outside the pass.
Martin
PR middle-end/78918 - missing -Wrestrict on memcpy copying over self
gcc/ChangeLog:
PR middle-end/78918
* builtins.c (warn_for_overlap, maybe_warn_for_overlap): New.
(check_sizes): Add argument and call maybe_warn_for_overlap.
Rename function arguments for clarity.
(check_memop_sizes): Adjust.
(expand_builtin_memchr): Ditto.
(expand_builtin_strcat): Ditto.
(expand_builtin_strcpy): Ditto.
(expand_builtin_stpcpy): Ditto.
(expand_builtin_stpncpy): Ditto.
(expand_builtin_strncpy): Ditto.
(expand_builtin_memcmp): Ditto.
(expand_builtin_memory_chk): Ditto.
(check_strncat_sizes): Ditto. Rename locals for clarity.
(expand_builtin_strncat): Ditto.
(maybe_emit_chk_warning): Ditto.
(maybe_emit_sprintf_chk_warning): Adjust.
* cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING.
* gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict.
(gimple_fold_builtin_strcpy): Ditto.
(gimple_fold_builtin_memory_chk): Ditto.
(gimple_fold_builtin_stxcpy_chk): Ditto.
* gimple.c (gimple_build_call_from_tree): Set call location.
* tree-ssa-alias.h (refs_overlap, detect_overlap): New functions.
* tree-ssa-alias.c (refs_overlap, detect_overlap): Define.
* tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict.
(handle_builtin_strcat): Ditto.
gcc/c-family/ChangeLog:
PR middle-end/78918
* c-common.c (check_function_restrict): Suppress warning for
built-in functions.
* c.opt (-Wrestrict): Include in -Wall.
gcc/testsuite/ChangeLog:
PR middle-end/78918
* c-c++-common/Wrestrict.c: New test.
* gcc.dg/Walloca-1.c: Suppress macro expansion tracking.
* gcc.dg/pr69172.c: Prune -Wrestrict.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 2deef72..e0fd9a7 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3036,39 +3036,303 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
return dest_addr;
}
+/* Issue a warning for a restricted copy call expression EXP to a built-in
+ function FUNC, with a destination of size DSTSIZE, size of copy in RANGE,
+ and with OVERLAP bytes at offset OFFRANGE. MUST_OVERLAP is true when
+ the overlap is certain, false when it is likely. */
+
+static void
+warn_for_overlap (tree exp, tree func, bool must_overlap, tree dstsize,
+ const tree range[2], unsigned HOST_WIDE_INT overlap,
+ const unsigned HOST_WIDE_INT offrange[2])
+{
+ location_t loc = tree_nonartificial_location (exp);
+ loc = expansion_point_location_if_in_system_header (loc);
+
+ /* To avoid combinatorial explosion of diagnostics format the offset
+ or its range as a string and use it in the warning calls below. */
+ char offstr[64];
+ if (offrange[0] == offrange[1] || offrange[1] > HOST_WIDE_INT_MAX)
+ sprintf (offstr, "%llu", (long long) offrange[0]);
+ else
+ sprintf (offstr, "%llu - %llu", (long long) offrange[0],
+ (long long) offrange[1]);
+
+ /* The text uses the term "writing N bytes" even though most operations
+ involve "copying" and the latter might be clearer. This first term
+ is used because string functions like strncat and strncpy don't
+ necessarily copy all N bytes; they may copy fewer. */
+
+ if (tree_to_uhwi (dstsize) >= HOST_WIDE_INT_MAX)
+ {
+ if (must_overlap)
+ {
+ if (tree_int_cst_equal (range[0], range[1]))
+ warning_at (loc, OPT_Wrestrict,
+ (integer_onep (range[0])
+ ? G_("%K%qD writing %E byte overlaps %wu byte "
+ "at offset %s")
+ : (overlap == 1
+ ? G_("%K%qD writing %E bytes overlaps %wu byte "
+ "at offset %s")
+ : G_("%K%qD writing %E bytes overlaps %wu bytes "
+ "at offset %s"))),
+ exp, func, range[0], overlap, offstr);
+ else if (tree_int_cst_sign_bit (range[1]))
+ {
+ /* Avoid printing the upper bound if it's invalid.
+ The overlap can likewise be "or more" in this case. */
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing %E or more bytes overlaps %wu "
+ "or more bytes at offset %s",
+ exp, func, range[0], overlap, offstr);
+ }
+ else
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing between %E and %E bytes overlaps "
+ "%wu or more bytes at offset %s",
+ exp, func, range[0], range[1], overlap, offstr);
+ }
+ else
+ {
+ if (tree_int_cst_equal (range[0], range[1]))
+ warning_at (loc, OPT_Wrestrict,
+ (integer_onep (range[0])
+ ? G_("%K%qD writing %E byte may overlap %wu bytes "
+ "at offset %s")
+ : (overlap == 1
+ ? G_("%K%qD writing %E bytes may overlap %wu byte "
+ "at offset %s")
+ : G_("%K%qD writing %E bytes may overlap %wu bytes "
+ "at offset %s"))),
+ exp, func, range[0], overlap, offstr);
+ else if (tree_int_cst_sign_bit (range[1]))
+ {
+ /* Avoid printing the upper bound if it's invalid.
+ The overlap can likewise be "or more" in this case. */
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing %E or more bytes may overlap %wu "
+ "or more bytes at offset %s",
+ exp, func, range[0], overlap, offstr);
+ }
+ else
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing between %E and %E bytes may overlap "
+ "%wu or more bytes at offset %s",
+ exp, func, range[0], range[1], overlap,
+ offstr);
+ }
+
+ return;
+ }
+
+ if (must_overlap)
+ {
+ if (tree_int_cst_equal (range[0], range[1]))
+ warning_at (loc, OPT_Wrestrict,
+ (integer_onep (range[0])
+ ? G_("%K%qD writing %E byte into a region "
+ "of size %E overlaps %wu byte "
+ "at offset %s")
+ : (overlap == 1
+ ? G_("%K%qD writing %E bytes into a region "
+ "of size %E overlaps %wu byte "
+ "at offset %s")
+ : G_("%K%qD writing %E bytes into a region "
+ "of size %E overlaps %wu bytes "
+ "at offset %s"))),
+ exp, func, range[0], dstsize, overlap, offstr);
+ else if (tree_int_cst_sign_bit (range[1]))
+ {
+ /* Avoid printing the upper bound if it's invalid.
+ The overlap can likewise be "or more" in this case. */
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing %E or more bytes into "
+ "a region of size %E overlaps %wu or more bytes "
+ "at offset %s",
+ exp, func, range[0], dstsize, overlap, offstr);
+ }
+ else
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing between %E and %E bytes into "
+ "a region of size %E overlaps %wu or more bytes "
+ "at offset %s",
+ exp, func, range[0], range[1], dstsize, overlap,
+ offstr);
+ }
+ else
+ {
+ if (tree_int_cst_equal (range[0], range[1]))
+ {
+ if (overlap < HOST_WIDE_INT_MAX)
+ warning_at (loc, OPT_Wrestrict,
+ (integer_onep (range[0])
+ ? G_("%K%qD writing %E byte into a region "
+ "of size %E may overlap %wu bytes "
+ "at offset %s")
+ : (overlap == 1
+ ? G_("%K%qD writing %E bytes into a region "
+ "of size %E may overlap %wu byte at "
+ "offset %s")
+ : G_("%K%qD writing %E bytes into a region "
+ "of size %E may overlap %wu bytes at "
+ "offset %s"))),
+ exp, func, range[0], dstsize, overlap, offstr);
+ else
+ /* Non-constant overlap due to an unknown range. */
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing into a region of size %E may overlap "
+ "at offset %s",
+ exp, func, dstsize, offstr);
+ }
+ else if (tree_int_cst_sign_bit (range[1]))
+ {
+ /* Avoid printing the upper bound if it's invalid.
+ The overlap can likewise be "or more" in this case. */
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing %E or more bytes into "
+ "a region of size %E may overlap %wu or more "
+ "bytes at offset %s",
+ exp, func, range[0], dstsize, overlap, offstr);
+ }
+ else
+ warning_at (loc, OPT_Wrestrict,
+ "%K%qD writing between %E and %E bytes into "
+ "a region of size %E may overlap %wu or more bytes "
+ "at offset %s",
+ exp, func, range[0], range[1], dstsize, overlap,
+ offstr);
+ }
+}
+
+/* Determine whether an overlap exists in a restricted copy call
+ expression EXP to a built-in function FUNC, with a destination DST
+ of DSTSIZE in size and source SRC, writing DSTWRITE bytes into DST
+ and reading MAXREAD bytes from SRC of SLEN bytes in length copy in
+ RANGE. If so, diagnose it and return true. If RELAXED is true
+ diagnose even uncertain (but likely) overlaps. Otherwise do nothing
+ and return false. */
+
+static bool
+maybe_warn_for_overlap (tree exp, tree func, tree dst, tree src, tree dstwrite,
+ tree range[2], bool at_least_one, tree maxread,
+ tree slen, tree dstsize, bool relaxed)
+{
+ if (!dstwrite)
+ dstwrite = range[0];
+
+ /* If the range of byte counts is [0, N] and N is non-zero, use
+ 1 as the conservative minimum to detect at least the most basic
+ cases of overflow. */
+ if (dstwrite && TREE_CODE (dstwrite) != INTEGER_CST && range[0])
+ dstwrite = (integer_zerop (range[0]) && !integer_zerop (range[1])
+ ? size_one_node : range[0]);
+
+ bool must_overlap = dstwrite && tree_fits_uhwi_p (dstwrite);
+
+ if (maxread)
+ must_overlap &= tree_fits_uhwi_p (maxread);
+ else if (slen)
+ {
+ /* When DSTWRITE is known, set the maximum number of bytes
+ to access to DSTWRITE either when the length of the source
+ sequence is unknown, or when DSTWRITE is less than the length. */
+ maxread = (must_overlap
+ && (at_least_one || tree_int_cst_lt (dstwrite, slen))
+ ? dstwrite : slen);
+ }
+ else
+ maxread = dstwrite;
+
+ if (!dstwrite)
+ dstwrite = maxread;
+
+ /* The exact number of bytes copied is known only if the sizes
+ weren't adjusted earlier on. */
+ must_overlap &= !at_least_one || (dstwrite && dstwrite != slen);
+
+ /* Initialize and store the lower bound of a constant offset (in
+ bits), disregarding the offset for the destination. */
+ ao_ref dstref, srcref;
+ unsigned HOST_WIDE_INT offrange[2];
+
+ ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite, offrange);
+ ao_ref_init_from_ptr_and_size (&srcref, src, maxread, offrange);
+
+ unsigned HOST_WIDE_INT offset;
+ if (unsigned HOST_WIDE_INT overlap
+ = refs_overlap (&dstref, &srcref, &offset))
+ {
+
+ /* Convert the offset range from bits to bytes. */
+ offrange[0] /= BITS_PER_UNIT;
+ offrange[1] /= BITS_PER_UNIT;
+
+ if (overlap <= offrange[1] - offrange[0])
+ must_overlap = false;
+
+ if (offrange[0] != offrange[1])
+ ;
+ else if (offrange[0] != HOST_WIDE_INT_M1U)
+ offrange[0] = offrange[1] = offset;
+
+ if (must_overlap || !relaxed)
+ {
+ if (!range[0])
+ range[0] = range[1] = dstwrite;
+
+ warn_for_overlap (exp, func, must_overlap, dstsize, range, overlap,
+ offrange);
+ }
+
+ /* Return error when an overlap has been detected. */
+ return must_overlap;
+ }
+
+ return false;
+}
+
/* Try to verify that the sizes and lengths of the arguments to a string
manipulation function given by EXP are within valid bounds and that
- the operation does not lead to buffer overflow. Arguments other than
- EXP may be null. When non-null, the arguments have the following
- meaning:
- SIZE is the user-supplied size argument to the function (such as in
- memcpy(d, s, SIZE) or strncpy(d, s, SIZE). It specifies the exact
- number of bytes to write.
- MAXLEN is the user-supplied bound on the length of the source sequence
+ the operation does not lead to buffer overflow or read past the end.
+ For copy operations that must not overlap also verify that the source
+ and destination do not overlap one another.
+ Arguments other than EXP may be null. When non-null, the arguments
+ have the following meaning:
+ DST is the destination of a copy call or null otherwise.
+ SRC is the source of a copy call or null otherwise.
+ DSTWRITE is the number of bytes written into the destination obtained
+ from the user-supplied size argument to the function (such as in
+ memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+ MAXREAD is the user-supplied bound on the length of the source sequence
(such as in strncat(d, s, N). It specifies the upper limit on the number
- of bytes to write.
- SRC is the source string (such as in strcpy(d, s)) when the expression
- EXP is a string function call (as opposed to a memory call like memcpy).
- As an exception, SRC can also be an integer denoting the precomputed
- size of the source string or object (for functions like memcpy).
- OBJSIZE is the size of the destination object specified by the last
+ of bytes to write. If null it's taken to be the same as DSTWRITE.
+ SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+ expression EXP is a string function call (as opposed to a memory call
+ like memcpy). As an exception, SRCSTR can also be an integer denoting
+ the precomputed size of the source string or object (for functions like
+ memcpy).
+ DSTSIZE is the size of the destination object specified by the last
argument to the _chk builtins, typically resulting from the expansion
- of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
- OBJSIZE).
+ of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+ DSTSIZE).
- When SIZE is null LEN is checked to verify that it doesn't exceed
+ When DSTWRITE is null LEN is checked to verify that it doesn't exceed
SIZE_MAX.
If the call is successfully verified as safe from buffer overflow
- the function returns true, otherwise false.. */
+ the function returns true, otherwise false. */
static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite,
+ tree maxread, tree srcstr, tree dstsize)
{
/* The size of the largest object is half the address space, or
SSIZE_MAX. (This is way too permissive.) */
tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+ /* Either the length of the source string for string functions or
+ the size of the source object for raw memory functions. */
tree slen = NULL_TREE;
tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3077,28 +3341,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
function like strcpy is not known and the only thing that is
known is that it must be at least one (for the terminating nul). */
bool at_least_one = false;
- if (src)
+ if (srcstr)
{
- /* SRC is normally a pointer to string but as a special case
+ /* SRCSTR is normally a pointer to string but as a special case
it can be an integer denoting the length of a string. */
- if (POINTER_TYPE_P (TREE_TYPE (src)))
+ if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
{
/* Try to determine the range of lengths the source string
refers to. If it can be determined and is less than
- the upper bound given by MAXLEN add one to it for
+ the upper bound given by MAXREAD add one to it for
the terminating nul. Otherwise, set it to one for
- the same reason, or to MAXLEN as appropriate. */
- get_range_strlen (src, range);
- if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+ the same reason, or to MAXREAD as appropriate. */
+ get_range_strlen (srcstr, range);
+ if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
{
- if (maxlen && tree_int_cst_le (maxlen, range[0]))
- range[0] = range[1] = maxlen;
+ if (maxread && tree_int_cst_le (maxread, range[0]))
+ range[0] = range[1] = maxread;
else
range[0] = fold_build2 (PLUS_EXPR, size_type_node,
range[0], size_one_node);
- if (maxlen && tree_int_cst_le (maxlen, range[1]))
- range[1] = maxlen;
+ if (maxread && tree_int_cst_le (maxread, range[1]))
+ range[1] = maxread;
else if (!integer_all_onesp (range[1]))
range[1] = fold_build2 (PLUS_EXPR, size_type_node,
range[1], size_one_node);
@@ -3112,10 +3376,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
}
}
else
- slen = src;
+ slen = srcstr;
}
- if (!size && !maxlen)
+ if (!dstwrite && !maxread)
{
/* When the only available piece of data is the object size
there is nothing to do. */
@@ -3123,20 +3387,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
return true;
/* Otherwise, when the length of the source sequence is known
- (as with with strlen), set SIZE to it. */
+ (as with strlen), set DSTWRITE to it. */
if (!range[0])
- size = slen;
+ dstwrite = slen;
}
- if (!objsize)
- objsize = maxobjsize;
+ if (!dstsize)
+ dstsize = maxobjsize;
- /* The SIZE is exact if it's non-null, constant, and in range of
- unsigned HOST_WIDE_INT. */
- bool exactsize = size && tree_fits_uhwi_p (size);
+ if (dstwrite)
+ get_size_range (dstwrite, range);
- if (size)
- get_size_range (size, range);
+ tree func = get_callee_fndecl (exp);
/* First check the number of bytes to be written against the maximum
object size. */
@@ -3149,30 +3411,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
warning_at (loc, opt,
"%K%qD specified size %E "
"exceeds maximum object size %E",
- exp, get_callee_fndecl (exp), range[0], maxobjsize);
+ exp, func, range[0], maxobjsize);
else
warning_at (loc, opt,
"%K%qD specified size between %E and %E "
"exceeds maximum object size %E",
- exp, get_callee_fndecl (exp),
+ exp, func,
range[0], range[1], maxobjsize);
return false;
}
+ /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+ constant, and in range of unsigned HOST_WIDE_INT. */
+ bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
/* Next check the number of bytes to be written against the destination
object size. */
- if (range[0] || !exactsize || integer_all_onesp (size))
+ if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
{
if (range[0]
- && ((tree_fits_uhwi_p (objsize)
- && tree_int_cst_lt (objsize, range[0]))
- || (tree_fits_uhwi_p (size)
- && tree_int_cst_lt (size, range[0]))))
+ && ((tree_fits_uhwi_p (dstsize)
+ && tree_int_cst_lt (dstsize, range[0]))
+ || (tree_fits_uhwi_p (dstwrite)
+ && tree_int_cst_lt (dstwrite, range[0]))))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
- if (size == slen && at_least_one)
+ if (dstwrite == slen && at_least_one)
{
/* This is a call to strcpy with a destination of 0 size
and a source of unknown length. The call will write
@@ -3180,7 +3446,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
warning_at (loc, opt,
"%K%qD writing %E or more bytes into a region "
"of size %E overflows the destination",
- exp, get_callee_fndecl (exp), range[0], objsize);
+ exp, func, range[0], dstsize);
}
else if (tree_int_cst_equal (range[0], range[1]))
warning_at (loc, opt,
@@ -3189,21 +3455,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
"of size %E overflows the destination")
: G_("%K%qD writing %E bytes into a region "
"of size %E overflows the destination")),
- exp, get_callee_fndecl (exp), range[0], objsize);
+ exp, func, range[0], dstsize);
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
warning_at (loc, opt,
"%K%qD writing %E or more bytes into a region "
"of size %E overflows the destination",
- exp, get_callee_fndecl (exp), range[0], objsize);
+ exp, func, range[0], dstsize);
}
else
warning_at (loc, opt,
"%K%qD writing between %E and %E bytes into "
"a region of size %E overflows the destination",
- exp, get_callee_fndecl (exp), range[0], range[1],
- objsize);
+ exp, func, range[0], range[1],
+ dstsize);
/* Return error when an overflow has been detected. */
return false;
@@ -3213,11 +3479,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
/* Check the maximum length of the source sequence against the size
of the destination object if known, or against the maximum size
of an object. */
- if (maxlen)
+ if (maxread)
{
- get_size_range (maxlen, range);
+ get_size_range (maxread, range);
+
+ /* Use the lower end for MAXREAD from now on. */
+ if (range[0])
+ maxread = range[0];
- if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+ if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
@@ -3231,40 +3501,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
warning_at (loc, opt,
"%K%qD specified bound %E "
"exceeds maximum object size %E",
- exp, get_callee_fndecl (exp),
+ exp, func,
range[0], maxobjsize);
else
warning_at (loc, opt,
"%K%qD specified bound between %E and %E "
"exceeds maximum object size %E",
- exp, get_callee_fndecl (exp),
+ exp, func,
range[0], range[1], maxobjsize);
return false;
}
- if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+ if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
{
if (tree_int_cst_equal (range[0], range[1]))
warning_at (loc, opt,
"%K%qD specified bound %E "
"exceeds destination size %E",
- exp, get_callee_fndecl (exp),
- range[0], objsize);
+ exp, func,
+ range[0], dstsize);
else
warning_at (loc, opt,
"%K%qD specified bound between %E and %E "
"exceeds destination size %E",
- exp, get_callee_fndecl (exp),
- range[0], range[1], objsize);
+ exp, func,
+ range[0], range[1], dstsize);
return false;
}
}
}
+ /* Check for reading past the end of SRC. */
if (slen
- && slen == src
- && size && range[0]
+ && slen == srcstr
+ && dstwrite && range[0]
&& tree_int_cst_lt (slen, range[0]))
{
location_t loc = tree_nonartificial_location (exp);
@@ -3274,23 +3545,36 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
(tree_int_cst_equal (range[0], integer_one_node)
? G_("%K%qD reading %E byte from a region of size %E")
: G_("%K%qD reading %E bytes from a region of size %E")),
- exp, get_callee_fndecl (exp), range[0], slen);
+ exp, func, range[0], slen);
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
warning_at (loc, opt,
"%K%qD reading %E or more bytes from a region "
"of size %E",
- exp, get_callee_fndecl (exp), range[0], slen);
+ exp, func, range[0], slen);
}
else
warning_at (loc, opt,
"%K%qD reading between %E and %E bytes from a region "
"of size %E",
- exp, get_callee_fndecl (exp), range[0], range[1], slen);
+ exp, func, range[0], range[1], slen);
return false;
}
+ /* Check for overlapping copies. Avoid warning if one has already
+ been issued (unlike -Wstringop-overflow, -Wrestrict is checked
+ in many places and TREE_NO_WARNING applies to it but not to
+ the former (there should be a bit for every kind of warning). */
+ if (src && dst
+ && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+ && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+ && !TREE_NO_WARNING (exp)
+ && maybe_warn_for_overlap (exp, func, dst, src, dstwrite, range,
+ at_least_one, maxread, slen, dstsize,
+ srcstr == NULL))
+ return false;
+
return true;
}
@@ -3330,8 +3614,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
tree dstsize = compute_objsize (dest, 0);
- return check_sizes (OPT_Wstringop_overflow_, exp,
- size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+ return check_sizes (OPT_Wstringop_overflow_, exp, dest, src,
+ size, /*maxread=*/NULL_TREE, srcsize, dstsize);
}
/* Validate memchr arguments without performing any expansion.
@@ -3353,8 +3637,8 @@ expand_builtin_memchr (tree exp, rtx)
{
tree size = compute_objsize (arg1, 0);
check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE,
- size, /*objsize=*/NULL_TREE);
+ exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+ /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
}
return NULL_RTX;
@@ -3652,7 +3936,8 @@ expand_builtin_strcat (tree exp, rtx)
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+ exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+ destsize);
return NULL_RTX;
}
@@ -3671,11 +3956,15 @@ expand_builtin_strcpy (tree exp, rtx target)
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
- if (warn_stringop_overflow)
+ if (warn_stringop_overflow || warn_restrict)
{
- tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+ /* Use Object Size type-1 for -Wrestrict. */
+ int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+ tree destsize = compute_objsize (dest, ost);
check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+ exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+ src, destsize);
}
return expand_builtin_strcpy_args (dest, src, target);
@@ -3714,7 +4003,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
{
tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+ exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+ destsize);
}
/* If return value is ignored, transform stpcpy into strcpy. */
@@ -3799,7 +4089,7 @@ expand_builtin_stpncpy (tree exp, rtx)
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+ exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
return NULL_RTX;
}
@@ -3829,7 +4119,7 @@ check_strncat_sizes (tree exp, tree objsize)
{
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
- tree maxlen = CALL_EXPR_ARG (exp, 2);
+ tree maxread = CALL_EXPR_ARG (exp, 2);
/* Try to determine the range of lengths that the source expression
refers to. */
@@ -3853,32 +4143,33 @@ check_strncat_sizes (tree exp, tree objsize)
size_one_node)
: NULL_TREE);
- /* Strncat copies at most MAXLEN bytes and always appends the terminating
+ /* Strncat copies at most MAXREAD bytes and always appends the terminating
nul so the specified upper bound should never be equal to (or greater
than) the size of the destination. */
- if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
- && tree_int_cst_equal (objsize, maxlen))
+ if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+ && tree_int_cst_equal (objsize, maxread))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
warning_at (loc, OPT_Wstringop_overflow_,
"%K%qD specified bound %E equals destination size",
- exp, get_callee_fndecl (exp), maxlen);
+ exp, get_callee_fndecl (exp), maxread);
return false;
}
if (!srclen
- || (maxlen && tree_fits_uhwi_p (maxlen)
+ || (maxread && tree_fits_uhwi_p (maxread)
&& tree_fits_uhwi_p (srclen)
- && tree_int_cst_lt (maxlen, srclen)))
- srclen = maxlen;
+ && tree_int_cst_lt (maxread, srclen)))
+ srclen = maxread;
/* The number of bytes to write is LEN but check_sizes will also
check SRCLEN if LEN's value isn't known. */
return check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+ exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+ objsize);
}
/* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3896,7 +4187,7 @@ expand_builtin_strncat (tree exp, rtx)
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
/* The upper bound on the number of bytes to write. */
- tree maxlen = CALL_EXPR_ARG (exp, 2);
+ tree maxread = CALL_EXPR_ARG (exp, 2);
/* The length of the source sequence. */
tree slen = c_strlen (src, 1);
@@ -3919,32 +4210,31 @@ expand_builtin_strncat (tree exp, rtx)
size_one_node)
: NULL_TREE);
- /* Strncat copies at most MAXLEN bytes and always appends the terminating
+ /* Strncat copies at most MAXREAD bytes and always appends the terminating
nul so the specified upper bound should never be equal to (or greater
than) the size of the destination. */
- if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
- && tree_int_cst_equal (destsize, maxlen))
+ if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+ && tree_int_cst_equal (destsize, maxread))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
warning_at (loc, OPT_Wstringop_overflow_,
"%K%qD specified bound %E equals destination size",
- exp, get_callee_fndecl (exp), maxlen);
+ exp, get_callee_fndecl (exp), maxread);
return NULL_RTX;
}
if (!srclen
- || (maxlen && tree_fits_uhwi_p (maxlen)
+ || (maxread && tree_fits_uhwi_p (maxread)
&& tree_fits_uhwi_p (srclen)
- && tree_int_cst_lt (maxlen, srclen)))
- srclen = maxlen;
+ && tree_int_cst_lt (maxread, srclen)))
+ srclen = maxread;
- /* The number of bytes to write is LEN but check_sizes will also
- check SRCLEN if LEN's value isn't known. */
+ /* The number of bytes to write is SRCLEN. */
check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+ exp, dest, src, NULL_TREE, maxread, srclen, destsize);
return NULL_RTX;
}
@@ -3975,7 +4265,7 @@ expand_builtin_strncpy (tree exp, rtx target)
/* The number of bytes to write is LEN but check_sizes will also
check SLEN if LEN's value isn't known. */
check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+ exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
}
/* We must be passed a constant len and src parameter. */
@@ -4319,13 +4609,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
{
tree size = compute_objsize (arg1, 0);
if (check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE,
- size, /*objsize=*/NULL_TREE))
+ exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+ /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
{
size = compute_objsize (arg2, 0);
check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE,
- size, /*objsize=*/NULL_TREE);
+ exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+ /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
}
}
@@ -9708,8 +9998,6 @@ static rtx
expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
enum built_in_function fcode)
{
- tree dest, src, len, size;
-
if (!validate_arglist (exp,
POINTER_TYPE,
fcode == BUILT_IN_MEMSET_CHK
@@ -9717,13 +10005,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX;
- dest = CALL_EXPR_ARG (exp, 0);
- src = CALL_EXPR_ARG (exp, 1);
- len = CALL_EXPR_ARG (exp, 2);
- size = CALL_EXPR_ARG (exp, 3);
+ tree dest = CALL_EXPR_ARG (exp, 0);
+ tree src = CALL_EXPR_ARG (exp, 1);
+ tree len = CALL_EXPR_ARG (exp, 2);
+ tree size = CALL_EXPR_ARG (exp, 3);
bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE,
+ exp, dest, src, len, /*maxread=*/NULL_TREE,
/*str=*/NULL_TREE, size);
if (!tree_fits_uhwi_p (size))
@@ -9833,7 +10121,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
/* The maximum length of the source sequence in a bounded operation
(such as __strncat_chk) or null if the operation isn't bounded
(such as __strcat_chk). */
- tree maxlen = NULL_TREE;
+ tree maxread = NULL_TREE;
switch (fcode)
{
@@ -9854,27 +10142,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
case BUILT_IN_STRNCAT_CHK:
catstr = CALL_EXPR_ARG (exp, 0);
srcstr = CALL_EXPR_ARG (exp, 1);
- maxlen = CALL_EXPR_ARG (exp, 2);
+ maxread = CALL_EXPR_ARG (exp, 2);
objsize = CALL_EXPR_ARG (exp, 3);
break;
case BUILT_IN_STRNCPY_CHK:
case BUILT_IN_STPNCPY_CHK:
srcstr = CALL_EXPR_ARG (exp, 1);
- maxlen = CALL_EXPR_ARG (exp, 2);
+ maxread = CALL_EXPR_ARG (exp, 2);
objsize = CALL_EXPR_ARG (exp, 3);
break;
case BUILT_IN_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK:
- maxlen = CALL_EXPR_ARG (exp, 1);
+ maxread = CALL_EXPR_ARG (exp, 1);
objsize = CALL_EXPR_ARG (exp, 3);
break;
default:
gcc_unreachable ();
}
- if (catstr && maxlen)
+ if (catstr && maxread)
{
/* Check __strncat_chk. There is no way to determine the length
of the string to which the source string is being appended so
@@ -9883,8 +10171,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
return;
}
- check_sizes (OPT_Wstringop_overflow_, exp,
- /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+ /* The destination argument is the first one for all built-ins above. */
+ tree dst = CALL_EXPR_ARG (exp, 0);
+
+ check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr,
+ /*size=*/NULL_TREE, maxread, srcstr, objsize);
}
/* Emit warning if a buffer overflow is detected at compile time
@@ -9940,8 +10231,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
/* Add one for the terminating nul. */
len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+ exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+ /*maxread=*/NULL_TREE, len, size);
}
/* Emit warning if a free is called with address of a variable. */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..c86a393 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5261,14 +5261,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
int nargs, tree *argarray)
{
int i;
- tree parms;
+ tree parms = TYPE_ARG_TYPES (fntype);
if (fndecl
- && TREE_CODE (fndecl) == FUNCTION_DECL
- && DECL_ARGUMENTS (fndecl))
- parms = DECL_ARGUMENTS (fndecl);
- else
- parms = TYPE_ARG_TYPES (fntype);
+ && TREE_CODE (fndecl) == FUNCTION_DECL)
+ {
+ /* Skip checking built-ins here. They are checked in more
+ detail elsewhere. */
+ if (DECL_BUILT_IN (fndecl)
+ && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+ return;
+
+ if (DECL_ARGUMENTS (fndecl))
+ parms = DECL_ARGUMENTS (fndecl);
+ }
for (i = 0; i < nargs; i++)
TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e0ad3ab..01506fd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1162,7 +1162,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
Warn when an argument passed to a restrict-qualified parameter aliases with
another argument.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 3e1d24d..76b9b15 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2627,6 +2627,9 @@ expand_call_stmt (gcall *stmt)
if (gimple_call_nothrow_p (stmt))
TREE_NOTHROW (exp) = 1;
+ if (gimple_no_warning_p (stmt))
+ TREE_NO_WARNING (exp) = 1;
+
CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b9d071b..5c3b14d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
-Wparentheses @gol
-Wpointer-sign @gol
-Wreorder @gol
+-Wrestrict @gol
-Wreturn-type @gol
-Wsequence-point @gol
-Wsign-compare @r{(only in C++)} @gol
@@ -6535,11 +6536,25 @@ reduce the padding and so make the structure smaller.
Warn if anything is declared more than once in the same scope, even in
cases where multiple declaration is valid and changes nothing.
-@item -Wrestrict
+@item -Wno-restrict
@opindex Wrestrict
@opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap. For example,
+the call to the @code{strcpy} function below attempts to truncate the string
+by replacing its initial characters with the last four. However, because
+the call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+ char a[] = "abcd1234";
+ strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
@item -Wnested-externs @r{(C and Objective-C only)}
@opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d94dc9c..eb61383 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,6 +57,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-cfg.h"
#include "fold-const-call.h"
#include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
/* Return true when DECL can be referenced from current unit.
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -644,7 +646,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
tree destvar, srcvar;
location_t loc = gimple_location (stmt);
- /* If the LEN parameter is zero, return DEST. */
+ tree func = gimple_call_fndecl (stmt);
+ bool nowarn = gimple_no_warning_p (stmt);
+ bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+ && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+ && !nowarn);
+
+ /* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
{
gimple *repl;
@@ -666,6 +674,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
DEST{,+LEN,+LEN-1}. */
if (operand_equal_p (src, dest, 0))
{
+ if (check_overlap)
+ warning_at (loc, OPT_Wrestrict,
+ "%qD source argument is the same as destination",
+ func);
+
unlink_stmt_vdef (stmt);
if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
release_ssa_name (gimple_vdef (stmt));
@@ -715,6 +728,31 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
unsigned ilen = tree_to_uhwi (len);
if (pow2p_hwi (ilen))
{
+ if (check_overlap)
+ {
+ ao_ref dstref, srcref;
+ ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+ ao_ref_init_from_ptr_and_size (&srcref, src, len);
+
+ if (refs_may_alias_p_1 (&dstref, &srcref, true))
+ {
+ /* Defer folding until both arguments are in an SSA
+ form so that aliasing can be determined with greater
+ accuracy. */
+ if (TREE_CODE (dest) != SSA_NAME
+ || TREE_CODE (src) != SSA_NAME)
+ return false;
+
+ /* Check for overlap and diagnose it. If it's found
+ avoid folding. */
+ if (detect_overlap (loc, stmt, dest, src, len, endp == 2))
+ {
+ gimple_set_no_warning (stmt, true);
+ return false;
+ }
+ }
+ }
+
tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
if (type
&& TYPE_MODE (type) != BLKmode
@@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
}
}
+ /* Avoid folding the call if overlap is detected. */
+ if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
+ return false;
+
gimple *new_stmt;
if (is_gimple_reg_type (TREE_TYPE (srcvar)))
{
@@ -1392,7 +1434,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
tree op3 = gimple_assign_rhs3 (def_stmt);
return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
&& get_range_strlen (op3, length, visited, type, fuzzy, flexp);
- }
+ }
return false;
case GIMPLE_PHI:
@@ -1484,12 +1526,19 @@ static bool
gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
tree dest, tree src)
{
- location_t loc = gimple_location (gsi_stmt (*gsi));
+ gimple *stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
tree fn;
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (operand_equal_p (src, dest, 0))
{
+ tree func = gimple_call_fndecl (stmt);
+
+ warning_at (loc, OPT_Wrestrict,
+ "%qD source argument is the same as destination",
+ func);
+
replace_call_with_value (gsi, dest);
return true;
}
@@ -2272,6 +2321,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
(resp. DEST+LEN for __mempcpy_chk). */
if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
{
+ if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+ {
+ tree func = gimple_call_fndecl (stmt);
+
+ warning_at (loc, OPT_Wrestrict,
+ "%qD source argument is the same as destination",
+ func);
+ }
+
if (fcode != BUILT_IN_MEMPCPY_CHK)
{
replace_call_with_value (gsi, dest);
@@ -2373,6 +2431,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
{
+ tree func = gimple_call_fndecl (stmt);
+
+ warning_at (loc, OPT_Wrestrict,
+ "%qD source argument is the same as destination",
+ func);
+
replace_call_with_value (gsi, dest);
return true;
}
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 488f8c8..f2dcece 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
gimple_set_block (call, TREE_BLOCK (t));
+ gimple_set_location (call, EXPR_LOCATION (t));
/* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */
gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..8e36bea
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,589 @@
+/* PR 35503 - Warn about restricted pointers
+ { dg-do compile }
+ { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+# define restrict __restrict
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+size_t value (void);
+
+size_t range (size_t min, size_t max)
+{
+ size_t val = value ();
+ return val < min || max < val ? min : val;
+}
+
+#define R(min, max) range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments. */
+
+void test_memcpy_cst (void)
+{
+#undef T
+#define T(dst, src, n) do { \
+ char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; \
+ const void *s = 0; \
+ memcpy ((dst), (src), (n)); \
+ sink (a, (dst), s); \
+ } while (0)
+
+ T (a, a, 0);
+ T (a, s = a, 1); /* { dg-warning "\\\[-Wrestrict" } */
+ T (a, a + 1, 1);
+ T (a, a + 1, 2); /* { dg-warning "\\\[-Wrestrict" } */
+
+ {
+ char a[3][7];
+ sink (a);
+
+ void *d = a[0];
+ const void *s = a[1];
+ memcpy (d, s, sizeof a[0]);
+ sink (&a);
+
+ d = a[0];
+ s = a[1];
+ memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (&a);
+
+ d = a[0] + 1;
+ s = a[1] + 1;
+ memcpy (d, s, sizeof a[0]);
+ sink (&a);
+
+ d = a[0] + 1;
+ s = a[1] + 1;
+ memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (&a);
+ }
+
+ {
+ struct {
+ char a[7];
+ char b[7];
+ char c[7];
+ } x;
+ sink (&x);
+
+ void *d = x.a;
+ const void *s = x.b;
+ memcpy (d, s, sizeof x.a);
+ sink (&x);
+
+ d = x.a;
+ s = x.a;
+ memcpy (d, s, sizeof x.a); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (&x);
+
+ d = x.a + 4;
+ s = x.b;
+ memcpy (d, s, sizeof x.a); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (&x);
+
+ d = x.a + 6;
+ s = x.b;
+ memcpy (d, s, 1);
+ sink (&x);
+
+ d = x.a + 7;
+ s = x.b;
+ memcpy (d, s, 1); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (&x);
+
+ d = x.a + 7;
+ s = x.b + 1;
+ memcpy (d, s, 1);
+ sink (&x);
+
+ d = x.b;
+ s = x.a;
+ memcpy (d, s, 1);
+ sink (&x);
+
+ d = x.b;
+ s = x.a;
+ memcpy (d, s, sizeof x.b);
+ sink (&x);
+
+ d = x.b + 2;
+ s = x.a + 1;
+ memcpy (d, s, sizeof x.b);
+ sink (&x);
+
+ d = x.b + 2;
+ s = x.a + 2;
+ memcpy (d, s, sizeof x.b);
+ sink (&x);
+
+ d = x.b + 2;
+ s = x.a + 3;
+ memcpy (d, s, sizeof x.b); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (&x);
+ }
+
+ {
+#undef T
+#define T(dst, src, n) do { \
+ char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; \
+ memcpy ((dst), (src), (n)); \
+ sink (a); \
+ } while (0)
+
+ /* Verify the offset of the overlap is the same regardless of whether
+ the destination is at lower or higher offset than the source. */
+ T (a, a + 1, 5); /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 4 bytes at offset 1" } */
+ T (a, a + 2, 5); /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 3 bytes at offset 2" } */
+ T (a, a + 3, 5); /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 2 bytes at offset 3" } */
+
+ T (a + 1, a, 5); /* { dg-warning "writing 5 bytes into a region of size 8 overlaps 4 bytes at offset 1" } */
+ T (a + 2, a, 5); /* { dg-warning "writing 5 bytes into a region of size 7 overlaps 3 bytes at offset 2" } */
+ T (a + 3, a, 5); /* { dg-warning "writing 5 bytes into a region of size 6 overlaps 2 bytes at offset 3" } */
+ }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+ a determinate range. */
+
+void test_memcpy_range (void *d)
+{
+#undef T
+#define T(dst, src, n) do { \
+ char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; \
+ memcpy ((dst), (src), (n)); \
+ sink (a); \
+ } while (0)
+
+ int r = R (2, 3);
+ T (a + r, a, 0);
+ T (a + r, a, 1);
+ T (a + r, a, 2);
+ T (a + r, a, 3); /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+ T (d + r, d, 0);
+ T (d + r, d, 1);
+ T (d + r, d, 2);
+ T (d + r, d, 3); /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+ /* Because the size is constant and a power of 2 the following is
+ folded too early to detect the overlap. */
+ T (d + r, d, 4); /* { dg-warning "writing 4 bytes overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+ T (d + r, d, 5); /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+ T (a, a + 1, R (0, 1));
+ T (a, a + 1, R (0, 2));
+ T (a, a + 1, R (1, 2));
+ T (a, a + 1, R (2, 3)); /* { dg-warning "writing between 2 and 3 bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+ T (a, a + 1, R (2, -2)); /* { dg-warning "writing 2 or more bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+ T (a, a + 2, R (2, 3));
+ T (a, a + 2, R (3, 4)); /* { dg-warning "writing between 3 and 4 bytes into a region of size 9 overlaps 1 or more bytes at offset 2" } */
+ T (a, a + 3, R (3, 4));
+ T (a, a + 3, R (4, 5)); /* { dg-warning "writing between 4 and 5 bytes into a region of size 9 overlaps 1 or more bytes at offset 3" } */
+ T (a, a + 3, R (5, 6)); /* { dg-warning "writing between 5 and 6 bytes into a region of size 9 overlaps 2 or more bytes at offset 3" } */
+
+ T (a + 1, a, R (0, 1));
+ T (a + 1, a, R (0, 2));
+ T (a + 1, a, R (1, 2));
+ T (a + 1, a, R (2, 3)); /* { dg-warning "writing between 2 and 3 bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+ T (a + 1, a, R (2, -2)); /* { dg-warning "writing 2 or more bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+ T (a + 2, a, R (2, 3));
+ T (a + 2, a, R (3, 4)); /* { dg-warning "writing between 3 and 4 bytes into a region of size 7 overlaps 1 or more bytes at offset 2" } */
+ T (a + 3, a, R (3, 4));
+ T (a + 3, a, R (4, 5)); /* { dg-warning "writing between 4 and 5 bytes into a region of size 6 overlaps 1 or more bytes at offset 3" } */
+ T (a + 3, a, R (5, 6)); /* { dg-warning "writing between 5 and 6 bytes into a region of size 6 overlaps 2 or more bytes at offset 3" } */
+
+ /* Verify offset and size both in some range. The memcpy checking
+ is less strict than that of string functions like strncpy and
+ doesn't trigger unless the overlap is certain. The following
+ overlaps for (r == 3 && n > 3) but not, for example, for
+ (r == 4 && n == 4), and so it's not diagnosed. */
+ r = R (3, 5);
+ T (a, a + r, R (4, 6));
+ /* Ditto for objects of unknown sizes. */
+ T (d, d + r, R (4, 6));
+}
+
+/* Exercise memcpy with destination and source of unknown size. */
+
+void test_memcpy_var (char *d, const char *s)
+{
+ int n = value ();
+
+ memcpy (d, d, 0);
+ sink (d);
+
+ memcpy (d, d, n); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (d);
+
+ memcpy (d, &d[0], n); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (d);
+
+ memcpy (&d[0], d, n); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (d);
+
+ s = d;
+ memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (d);
+
+ /* The following overlaps if n is greater than 1. */
+ s = d + 1;
+ memcpy (d, s, n);
+ sink (d);
+
+ /* The following only overlaps if strlen (s + i) >= i so it's not
+ diagnosed. */
+ s = d + n;
+ memcpy (d, s, n);
+ sink (d);
+
+ s = d + 3;
+ n = 1;
+ memcpy (d, s, n);
+ sink (d);
+
+ s = d + 3;
+ n = 2;
+ memcpy (d, s, n);
+ sink (d);
+
+ s = d + 3;
+ n = 3;
+ memcpy (d, s, n);
+ sink (d);
+
+ s = d + 3;
+ n = 4;
+ memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */
+ sink (d);
+
+ s = d + 5;
+ n = 7;
+ memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */
+
+ n = range (0, 1);
+ s = d;
+ memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise the absence of warnings with memmove. */
+
+void test_memmove (void)
+{
+ {
+ char d[7];
+ sink (d);
+
+ const void *s = d;
+ memmove (d, s, 7);
+ sink (d);
+
+ s = d + 1;
+ memmove (d, s, 6);
+ sink (d);
+
+ s = d + 2;
+ memmove (d + 1, s, 5);
+ sink (d);
+ }
+}
+
+/* Exercise strcat with constant or known arguments. */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do { \
+ char a[9] = init; \
+ strcat ((dst), (src)); \
+ sink (a); \
+ } while (0)
+
+ T ("123", a, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 1); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 2); /* { dg-warning "\\\[-Wrestrict" } */
+ /* The nul copied from a[3] to a[3] overwrites itself so this is
+ diagnosed. */
+ T ("123", a, a + 3); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 4);
+ T ("123", a, a + 5);
+ T ("123", a, a + 6);
+ T ("123", a, a + 7);
+ T ("123", a, a + 8);
+
+ T ("123", a + 1, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a + 2, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a + 3, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a + 4, a); /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+ T ("123", a + 5, a); /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+
+ /* Verify that the obviously benign cases below aren't diagnosed. */
+ T ("123", a, "123");
+ T ("123", a, s);
+ T ("12345678", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length. */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do { \
+ strcat ((dst), (src)); \
+ sink ((dst)); \
+ } while (0)
+
+ T (d, d); /* { dg-warning "\\\[-Wrestrict" } */
+ T (d, d + 1); /* { dg-warning "\\\[-Wrestrict" } */
+ T (d, d + 2); /* { dg-warning "\\\[-Wrestrict" } */
+ T (d, d + 3);
+
+ int n = value ();
+
+ /* Verify that the obviously benign cases below aren't diagnosed. */
+ T (d, "123");
+ T (d + 1, "1234");
+ T (d + n, "12345");
+ T (d, s);
+ T (d + 1, s);
+ T (d + n, s);
+
+ /* This one isn't so obvious and might be worth diagnosing with "may
+ overlap," even more so than the analogous strcpy case. */
+ T (d, d + n);
+}
+
+/* Exercise strcpy with constant or known arguments. */
+
+void test_strcpy_cst (void)
+{
+#undef T
+#define T(init, dst, src) do { \
+ char a[8] = init; \
+ strcpy ((dst), (src)); \
+ sink (a); \
+ } while (0)
+
+ T ("123", a, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 1); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 2);
+ T ("123", a, a + 3);
+
+ /* The terminating nul written to d[2] overwrites s[0]. */
+ T ("1234", a, a + 2); /* { dg-warning "\\\[-Wrestrict" } */
+
+ /* The '5' copied from s[2] to d[2] overwrites s[0]. */
+ T ("12345", a, a + 2); /* { dg-warning "\\\[-Wrestrict" } */
+
+ /* It's not 100% clear if this should trigger a warning. The non-const
+ string case doesn't. */
+ T ("123", a, a + value ());
+
+ /* This happens to be safe in GCC but it's still wrong. */
+ T ("123", a, a); /* { dg-warning "\\\[-Wrestrict" } */
+
+ T ("123", a + 1, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a + 2, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a + 3, a); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a + 4, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+ The tests verify the use of the lower bound of the range which is
+ more restrictive than using the upper bound for positive values. */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(init, dst, src) do { \
+ char a[8] = init; \
+ strcpy ((dst), (src)); \
+ sink (a); \
+ } while (0)
+
+ /* The range needs to be an int variable and not a size_t here,
+ otherwise the range info in the strlen pass is not good enough
+ to detect these cases. */
+ int r = R (3, 4);
+ T ("12", a + r, a);
+ T ("123", a + r, a); /* { dg-warning "writing 4 bytes overlaps 1 byte at offset 3" } */
+
+ T ("1234", a + r, a); /* { dg-warning "writing 5 bytes overlaps 2 bytes at offset 3" } */
+
+ T ("12", a, a + r);
+ T ("123", a, a + r);
+ T ("1234", a, a + r);
+ T ("12345", a, a + r);
+
+ /* The final NUL overlaps the '4' at a[3]. */
+ T ("123456", a, a + r); /* { dg-warning "writing between 3 and 4 bytes may overlap 1 byte at offset 3" } */
+ T ("1234567", a, a + r); /* { dg-warning "writing between 4 and 5 bytes overlaps 2 bytes at offset 3" } */
+
+ r = R (2, 5);
+ T ("1234", a + r, a); /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+
+}
+
+
+/* Exercise strcpy with destination and source of unknown length. */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do { \
+ strcpy ((dst), (src)); \
+ sink (dst); \
+ } while (0)
+
+ T (d, s);
+
+ T (d, &d[0]); /* { dg-warning "\\\[-Wrestrict" } */
+ T (&d[0], d); /* { dg-warning "\\\[-Wrestrict" } */
+
+ s = d;
+ T (d, s); /* { dg-warning "\\\[-Wrestrict" } */
+
+ /* The following overlaps if *s is not nul. It arguably should be
+ diagnosed. */
+ T (d, d + 1);
+
+ /* The following overlaps if strlen (d) is greater than 1. Like
+ the above, it possibly should be diagnosed too. */
+ int r = R (2, 3);
+ T (d, d + r);
+
+ /* The following overlaps only if strlen (s + n) >= n so it's not
+ diagnosed. */
+ s = d + value ();
+ T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments. */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do { \
+ char a[7] = init; \
+ strncpy (dst, src, size); \
+ sink (a); \
+ } while (0)
+
+ T ("123", a, a, 0);
+ T ("123", a, a, 1); /* { dg-warning "\\\[-Wrestrict" } */
+ /* The following overlaps except in the unlikely case that value ()
+ is zero, so it's diagnosed. */
+ T ("123", a, a, value ()); /* { dg-warning "writing into a region of size 7 may overlap at offset 0" } */
+ T ("123", a, a + 1, 1);
+ T ("123", a, a + 1, 2); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 1, 3); /* { dg-warning "\\\[-Wrestrict" } */
+ T ("123", a, a + 2, 1);
+ T ("123", a, a + 2, 2);
+ /* The third written byte (nul) overwrites a[2]. */
+ T ("123", a, a + 2, 3); /* { dg-warning "\\\[-Wrestrict" } */
+
+ T ("1234", a, a + 2, 1);
+ T ("1234", a, a + 2, 2);
+ /* The terminating nul written to a[2] overwrites s[0]. */
+ T ("1234", a, a + 2, 3); /* { dg-warning "\\\[-Wrestrict" } */
+
+ T ("12345", a, a + 2, 1);
+ T ("12345", a, a + 2, 2);
+ /* The '5' copied from s[2] to d[2] overwrites s[0]. */
+ T ("12345", a, a + 2, 4); /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range. */
+
+void test_strncpy_range (char *d)
+{
+#undef T
+#define T(init, dst, src, size) do { \
+ char a[9] = init; \
+ strncpy ((dst), (src), (size)); \
+ sink (a); \
+ } while (0)
+
+ /* Verify offset and size both in some range. The strncpy checking
+ is more strict than that of memcpy and triggers even when the
+ overlap is possible but not inevitable. The following overlaps
+ for (r == 3 && n > 3) but not, for example, for (r == 4 && n == 4),
+ but it's still diagnosed. */
+ int r = R (3, 5);
+ T ("12345678", a, a + r, R (4, 6)); /* { dg-warning "writing between 4 and 6 bytes into a region of size 9 may overlap 1 or more bytes at offset 3 - 5" } */
+
+ /* Ditto for objects of unknown sizes. Verify also that since it's
+ unknown the size isn't printed. */
+ T ("12345678", d, d + r, R (4, 6)); /* { dg-warning "writing between 4 and 6 bytes may overlap 1 or more bytes at offset 3 - 5" } */
+
+ T ("12345678", a, a + r, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 9 overlaps 3 or more bytes at offset 3 - 5" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length. */
+
+void test_strncpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src, size) do { \
+ strncpy (dst, src, size); \
+ sink ((dst)); \
+ } while (0)
+
+ int n = value ();
+
+ T (d, s, 1);
+ T (d, s, n);
+
+ T (d, d, 1); /* { dg-warning "\\\[-Wrestrict" } */
+ T (d, d, n); /* { dg-warning "\\\[-Wrestrict" } */
+
+ T (d, d + 1, 1);
+ T (d, d + 1, 2); /* { dg-warning "\\\[-Wrestrict" } */
+ T (d + 1, d, 1);
+ T (d + 1, d, 2); /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+struct MemberArrays
+{
+ char a[8];
+ char b[8];
+ char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+ /* The following is safe and should not trigger a warning. */
+ strncpy (ar->b, s, sizeof ar->b - 1);
+ ar->b[sizeof ar->b - 1] = '\0';
+ strcpy (ar->a, ar->b);
+ sink (ar);
+
+ /* The following is not as safe (it might overflow ar->a) but there
+ is no overlap so it also shouldn't trigger -Wrestrict. */
+ strncpy (ar->c, s, sizeof ar->c - 1);
+ ar->c[sizeof ar->c - 1] = '\0';
+ strcpy (ar->a, ar->c);
+ sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
/* { dg-do compile } */
/* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
#define alloca __builtin_alloca
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+ at tree-ssanames.c:266 */
/* { dg-do compile } */
/* { dg-options "-O2" } */
@@ -43,3 +44,7 @@ f6 (int x)
{
return __builtin___mempcpy_chk (&a, &a, x, 0);
}
+
+/* The calls above violate strict aliasing. Eliminate the -Wrestrict
+ warnings they trigger.
+ { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..1b46581 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -38,6 +38,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-dfa.h"
#include "ipa-reference.h"
#include "varasm.h"
+#include "diagnostic-core.h"
+#include "intl.h"
/* Broad overview of how alias analysis on gimple works:
@@ -677,10 +679,15 @@ ao_ref_alias_set (ao_ref *ref)
to or after of the pointer target, not before it. */
void
-ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
+ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size,
+ unsigned HOST_WIDE_INT offrng[2] /* = NULL */)
{
HOST_WIDE_INT t, size_hwi, extra_offset = 0;
- ref->ref = NULL_TREE;
+
+ if (offrng)
+ offrng[0] = offrng[1] = 0;
+
+ ref->ref = NULL_TREE;
if (TREE_CODE (ptr) == SSA_NAME)
{
gimple *stmt = SSA_NAME_DEF_STMT (ptr);
@@ -688,12 +695,30 @@ ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
&& gimple_assign_rhs_code (stmt) == ADDR_EXPR)
ptr = gimple_assign_rhs1 (stmt);
else if (is_gimple_assign (stmt)
- && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
- && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
+ && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
{
- ptr = gimple_assign_rhs1 (stmt);
- extra_offset = BITS_PER_UNIT
- * int_cst_value (gimple_assign_rhs2 (stmt));
+ tree offset = gimple_assign_rhs2 (stmt);
+ if (TREE_CODE (offset) == INTEGER_CST)
+ {
+ ptr = gimple_assign_rhs1 (stmt);
+ extra_offset = BITS_PER_UNIT * int_cst_value (offset);
+ }
+ else if (offrng && TREE_CODE (offset) == SSA_NAME)
+ {
+ wide_int min, max;
+ value_range_type rng = get_range_info (offset, &min, &max);
+ if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+ {
+ ptr = gimple_assign_rhs1 (stmt);
+ offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
+ offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+
+ extra_offset = offrng[0];
+ }
+ else
+ /* Offset range is indeterminate. */
+ offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
+ }
}
}
@@ -2945,3 +2970,145 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef,
return ret;
}
+/* Return the size of the overlap in bytes if REF1 and REF2 alias one
+ another, and set *ALOFF (alias offset) to the offset of the overlap.
+ in REF1. Otherwise, if REF1 and REF2 do not definitely overlap,
+ return zero. Used for -Wrestrict warnings. */
+
+unsigned HOST_WIDE_INT
+refs_overlap (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
+{
+ tree base1 = ao_ref_base (ref1);
+ tree base2 = ao_ref_base (ref2);
+
+ if (!base1 || !base2)
+ return 0;
+
+ offset_int offset1 = ref1->offset;
+ offset_int offset2 = ref2->offset;
+
+ if (TREE_CODE (base1) == MEM_REF)
+ {
+ if (TREE_CODE (base2) != MEM_REF)
+ return 0;
+
+ // Compare pointers.
+ offset1 += mem_ref_offset (base1) << LOG2_BITS_PER_UNIT;
+ offset2 += mem_ref_offset (base2) << LOG2_BITS_PER_UNIT;
+ base1 = TREE_OPERAND (base1, 0);
+ base2 = TREE_OPERAND (base2, 0);
+ }
+
+ if (base1 != base2)
+ return 0;
+
+ if (offset1 <= offset2)
+ {
+ *aloff = (offset2 - offset1).to_uhwi () >> LOG2_BITS_PER_UNIT;
+ if (ref1->size == -1)
+ return -1;
+
+ offset_int end1 = offset1 + ref1->size;
+ if (end1 > offset2)
+ return (end1 - offset2).to_uhwi () >> LOG2_BITS_PER_UNIT;
+ }
+ if (offset2 <= offset1)
+ {
+ *aloff = (offset1 - offset2).to_uhwi () >> LOG2_BITS_PER_UNIT;
+ if (ref2->size == -1)
+ return -1;
+
+ offset_int end2 = offset2 + ref2->size;
+ if (end2 > offset1)
+ return (end2 - offset1).to_uhwi () >> LOG2_BITS_PER_UNIT;
+ }
+
+ return 0;
+}
+
+/* Attempt to detect and diagnose overlapping copy in a statement STMT
+ from SRC to DST of SIZE bytes. When ADJUST is set, adjust SIZE to
+ reflect the lower bound of the non-constant SRC offset. Return true
+ when overlap has been detected, false otherwise.
+ In the case of a non-constant offset, using a positive lower bound
+ is more strict than using the upper bound. */
+
+bool
+detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
+ bool adjust /* = false */)
+{
+ ao_ref dstref, srcref;
+ unsigned HOST_WIDE_INT range[2];
+
+ /* Initialize and store the lower bound of a constant offset (in
+ bits), disregarding the offset for the destination. */
+ ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
+ ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
+
+ if (adjust)
+ {
+ /* Adjust the source and destination sizes (in bits) by
+ the non-constant offset (also in bits). */
+ dstref.size -= range[0];
+ srcref.size -= range[0];
+ }
+
+ /* Determine the size of the OVERLAP, if any, and its OFFSET, both
+ in bytes. */
+ unsigned HOST_WIDE_INT offset;
+ unsigned HOST_WIDE_INT overlap = refs_overlap (&dstref, &srcref, &offset);
+
+ if (!overlap)
+ return false;
+
+ /* Convert the size from bits to bytes (OFFSET is in bytes). */
+
+ range[1] = (dstref.size - (range[1] - range[0])) / BITS_PER_UNIT;
+ range[0] = dstref.size / BITS_PER_UNIT;
+
+ if (range[0] > range[1])
+ std::swap (range[0], range[1]);
+
+ bool warned;
+
+ tree func = gimple_call_fndecl (stmt);
+
+ /* Below distinguish three cases:
+ 1) certain constant overlap
+ 2) certain overlap in some range
+ 3) possible overlap. */
+ if (range[0] == range[1])
+ warned = warning_at (loc, OPT_Wrestrict,
+ range[0] == 1
+ ? (overlap == 1
+ ? G_("%qD writing %wu byte overlaps %wu byte "
+ "at offset %wu")
+ : G_("%qD writing %wu byte overlaps %wu bytes "
+ "at offset %wu"))
+ : (overlap == 1
+ ? G_("%qD writing %wu bytes overlaps %wu byte "
+ "at offset %wu")
+ : G_("%qD writing %wu bytes overlaps %wu bytes "
+ "at offset %wu")),
+ func, range[0], overlap, offset);
+ else if (range[1] - range[0] < overlap)
+ warned = warning_at (loc, OPT_Wrestrict,
+ overlap == 1
+ ? G_("%qD writing between %wu and %wu bytes "
+ "overlaps %wu byte at offset %wu")
+ : G_("%qD writing between %wu and %wu bytes "
+ "overlaps %wu bytes at offset %wu"),
+ func, range[0], range[1], overlap, offset);
+ else
+ warned = warning_at (loc, OPT_Wrestrict,
+ overlap == 1
+ ? G_("%qD writing between %wu and %wu bytes "
+ "may overlap %wu byte at offset %wu")
+ : G_("%qD writing %wu byte may overlap %wu bytes "
+ "at offset %wu"),
+ func, range[0], range[1], overlap, offset);
+ if (warned)
+ gimple_set_no_warning (stmt, true);
+
+ return true;
+}
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 8c89c69..f15dd17 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -99,7 +99,8 @@ struct ao_ref
/* In tree-ssa-alias.c */
extern void ao_ref_init (ao_ref *, tree);
-extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
+extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree,
+ unsigned HOST_WIDE_INT[2] = NULL);
extern tree ao_ref_base (ao_ref *);
extern alias_set_type ao_ref_alias_set (ao_ref *);
extern alias_set_type ao_ref_base_alias_set (ao_ref *);
@@ -145,6 +146,10 @@ extern void dump_points_to_info_for (FILE *, tree);
extern void debug_points_to_info_for (tree);
extern void dump_alias_stats (FILE *);
+extern unsigned HOST_WIDE_INT refs_overlap (ao_ref *, ao_ref *,
+ unsigned HOST_WIDE_INT *);
+extern bool detect_overlap (location_t, gimple *, tree, tree, tree,
+ bool = false);
/* In tree-ssa-structalias.c */
extern unsigned int compute_may_aliases (void);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b0563fe..75c01b0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "expr.h"
#include "tree-dfa.h"
#include "domwalk.h"
+#include "tree-ssa-alias.h"
#include "tree-ssa-propagate.h"
#include "params.h"
#include "ipa-chkp.h"
@@ -1477,6 +1478,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
}
}
dsi->stmt = stmt;
+
+ /* Try to detect overlap before returning. This catches cases
+ like strcpy (d, d + n) where n is non-constant whose range
+ is such that (n <= strlen (d) holds).
+
+ OLDDSI->NONZERO_chars may have been reset by this point with
+ oldlen holding it original value. */
+ if (olddsi && oldlen)
+ {
+ /* Add 1 for the terminating NUL. */
+ tree type = TREE_TYPE (oldlen);
+ oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+ build_int_cst (type, 1));
+ detect_overlap (loc, stmt, olddsi->ptr, src, oldlen, true);
+ }
+
return;
}
@@ -1559,6 +1576,12 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
GSI_SAME_STMT);
+
+ if (const strinfo *chksi = olddsi ? olddsi : dsi)
+ if (si && detect_overlap (loc, stmt, chksi->ptr, si->ptr, len, true))
+ /* Avoid transforming strcpy to memcpy when overlap is detected. */
+ return;
+
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
{
fprintf (dump_file, "Optimizing: ");
@@ -1786,7 +1809,7 @@ static void
handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
{
int idx, didx;
- tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+ tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr;
bool success;
gimple *stmt = gsi_stmt (*gsi);
strinfo *si, *dsi;
@@ -1804,10 +1827,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
dsi = NULL;
if (didx > 0)
dsi = get_strinfo (didx);
+
+ srclen = NULL_TREE;
+ si = NULL;
+ idx = get_stridx (src);
+ if (idx < 0)
+ srclen = build_int_cst (size_type_node, ~idx);
+ else if (idx > 0)
+ {
+ si = get_strinfo (idx);
+ if (si != NULL)
+ srclen = get_string_length (si);
+ }
+
+ loc = gimple_location (stmt);
+
if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
{
+ if (warn_restrict)
+ {
+ /* The concatenation always involves copying at least one byte
+ (the terminating nul), even if the source string is empty.
+ If the source is unknown assume it's one character long and
+ compute the size of the result. */
+ tree ressize = srclen ? srclen : size_one_node;
+ tree type = TREE_TYPE (ressize);
+ ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize);
+ ressize = fold_build2 (PLUS_EXPR, type, ressize,
+ build_int_cst (type, 1));
+
+ tree sptr = si && si->ptr ? si->ptr : src;
+ if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+ /* Avoid transforming overlapping strcat. */
+ return;
+ }
+
/* strcat (p, q) can be transformed into
- tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+ tmp = p + strlen (p); endptr = stpcpy (tmp, q);
with length endptr - p if we need to compute the length
later on. Don't do this transformation if we don't need
it. */
@@ -1840,19 +1896,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
return;
}
- srclen = NULL_TREE;
- si = NULL;
- idx = get_stridx (src);
- if (idx < 0)
- srclen = build_int_cst (size_type_node, ~idx);
- else if (idx > 0)
- {
- si = get_strinfo (idx);
- if (si != NULL)
- srclen = get_string_length (si);
- }
-
- loc = gimple_location (stmt);
dstlen = dsi->nonzero_chars;
endptr = dsi->endptr;
@@ -1914,7 +1957,22 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (fn == NULL_TREE)
return;
- len = NULL_TREE;
+ if (warn_restrict && dsi && dstlen)
+ {
+ tree slen = srclen ? srclen : size_zero_node;
+ /* Compute the size of the concatenated string, including
+ the terminating nul. */
+ tree type = TREE_TYPE (dstlen);
+ tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen);
+ ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1));
+
+ tree sptr = si && si->ptr ? si->ptr : src;
+ if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+ /* Avoid transforming overlapping strcat. */
+ return;
+ }
+
+ tree len = NULL_TREE;
if (srclen != NULL_TREE)
{
args = TYPE_ARG_TYPES (TREE_TYPE (fn));