+2017-12-16 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/78918
+ * Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
+ * builtins.c (check_sizes): Rename...
+ (check_access): ...to this. Rename function arguments for clarity.
+ (check_memop_sizes): Adjust names.
+ (expand_builtin_memchr, expand_builtin_memcpy): Same.
+ (expand_builtin_memmove, expand_builtin_mempcpy): Same.
+ (expand_builtin_strcat, expand_builtin_stpncpy): Same.
+ (check_strncat_sizes, expand_builtin_strncat): Same.
+ (expand_builtin_strncpy, expand_builtin_memset): Same.
+ (expand_builtin_bzero, expand_builtin_memcmp): Same.
+ (expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
+ (maybe_emit_sprintf_chk_warning): Same.
+ (expand_builtin_strcpy): Adjust.
+ (expand_builtin_stpcpy): Same.
+ (expand_builtin_with_bounds): Detect out-of-bounds accesses
+ in pointer-checking forms of memcpy, memmove, and mempcpy.
+ (gcall_to_tree_minimal, max_object_size): Define new functions.
+ * builtins.h (max_object_size): Declare.
+ * calls.c (alloc_max_size): Call max_object_size instead of
+ hardcoding ssizetype limit.
+ (get_size_range): Handle new argument.
+ * calls.h (get_size_range): Add a new argument.
+ * cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
+ * doc/invoke.texi (-Wrestrict): Adjust, add example.
+ * gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
+ operations.
+ (gimple_fold_builtin_memory_chk): Same.
+ (gimple_fold_builtin_stxcpy_chk): New function.
+ * gimple-ssa-warn-restrict.c: New source.
+ * gimple-ssa-warn-restrict.h: New header.
+ * gimple.c (gimple_build_call_from_tree): Propagate location.
+ * passes.def (pass_warn_restrict): Add new pass.
+ * tree-pass.h (make_pass_warn_restrict): Declare.
+ * tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
+ operations.
+ (handle_builtin_strcat): Same.
+ (strlen_optimize_stmt): Rename...
+ (strlen_check_and_optimize_stmt): ...to this. Handle strncat,
+ stpncpy, strncpy, and their checking forms.
+
2017-12-16 Jan Hubicka <hubicka@ucw.cz>
PR rtl-optimization/82849
gimple-ssa-strength-reduction.o \
gimple-ssa-sprintf.o \
gimple-ssa-warn-alloca.o \
+ gimple-ssa-warn-restrict.o \
gimple-streamer-in.o \
gimple-streamer-out.o \
gimple-walk.o \
#include "alias.h"
#include "fold-const.h"
#include "fold-const-call.h"
+#include "gimple-ssa-warn-restrict.h"
#include "stor-layout.h"
#include "calls.h"
#include "varasm.h"
/* 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.
+ 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.. */
+ If the call is successfully verified as safe return true, otherwise
+ return false. */
static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_access (tree exp, tree, tree, tree dstwrite,
+ tree maxread, tree srcstr, tree dstsize)
{
+ int opt = OPT_Wstringop_overflow_;
+
/* 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);
+ PTRDIFF_MAX. (This is way too permissive.) */
+ tree maxobjsize = max_object_size ();
+ /* 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 };
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);
}
}
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. */
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. */
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
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,
"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;
/* 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);
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);
(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;
}
(no overflow or invalid sizes), false otherwise. */
static bool
-check_memop_sizes (tree exp, tree dest, tree src, tree size)
+check_memop_access (tree exp, tree dest, tree src, tree size)
{
- if (!warn_stringop_overflow)
- return true;
-
/* For functions like memset and memcpy that operate on raw memory
try to determine the size of the largest source and destination
object using type-0 Object Size regardless of the object 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_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
+ srcsize, dstsize);
}
/* Validate memchr arguments without performing any expansion.
if (warn_stringop_overflow)
{
tree size = compute_objsize (arg1, 0);
- check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE,
- size, /*objsize=*/NULL_TREE);
+ check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+ /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
}
return NULL_RTX;
tree src = CALL_EXPR_ARG (exp, 1);
tree len = CALL_EXPR_ARG (exp, 2);
- check_memop_sizes (exp, dest, src, len);
+ check_memop_access (exp, dest, src, len);
return expand_builtin_memory_copy_args (dest, src, len, target, exp,
/*endp=*/ 0);
tree src = CALL_EXPR_ARG (exp, 1);
tree len = CALL_EXPR_ARG (exp, 2);
- check_memop_sizes (exp, dest, src, len);
+ check_memop_access (exp, dest, src, len);
return NULL_RTX;
}
/* Avoid expanding mempcpy into memcpy when the call is determined
to overflow the buffer. This also prevents the same overflow
from being diagnosed again when expanding memcpy. */
- if (!check_memop_sizes (exp, dest, src, len))
+ if (!check_memop_access (exp, dest, src, len))
return NULL_RTX;
return expand_builtin_mempcpy_args (dest, src, len,
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
- check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+ check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+ destsize);
return NULL_RTX;
}
if (warn_stringop_overflow)
{
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
- check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+ check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+ src, destsize);
}
return expand_builtin_strcpy_args (dest, src, target);
if (warn_stringop_overflow)
{
tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
- check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+ check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+ src, destsize);
}
/* If return value is ignored, transform stpcpy into strcpy. */
/* The size of the destination object. */
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
- check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+ check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
return NULL_RTX;
}
{
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. */
size_one_node)
: NULL_TREE);
- /* Strncat copies at most MAXLEN 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))
+ /* The strncat function 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 (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
+ /* The number of bytes to write is LEN but check_access will also
check SRCLEN if LEN's value isn't known. */
- return check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+ return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+ objsize);
}
/* Similar to expand_builtin_strcat, do some very basic size validation
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);
size_one_node)
: NULL_TREE);
- /* Strncat copies at most MAXLEN 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))
+ /* The strncat function 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 (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. */
- check_sizes (OPT_Wstringop_overflow_,
- exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+ /* The number of bytes to write is SRCLEN. */
+ check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
return NULL_RTX;
}
-/* Helper to check the sizes of sequences and the destination of calls
- to __builtin_strncpy (DST, SRC, CNT) and __builtin___strncpy_chk.
- Returns true on success (no overflow warning), false otherwise. */
-
-static bool
-check_strncpy_sizes (tree exp, tree dst, tree src, tree cnt)
-{
- tree dstsize = compute_objsize (dst, warn_stringop_overflow - 1);
-
- if (!check_sizes (OPT_Wstringop_overflow_,
- exp, cnt, /*maxlen=*/NULL_TREE, src, dstsize))
- return false;
-
- return true;
-}
-
/* Expand expression EXP, which is a call to the strncpy builtin. Return
NULL_RTX if we failed the caller should emit a normal call. */
/* The length of the source sequence. */
tree slen = c_strlen (src, 1);
- check_strncpy_sizes (exp, dest, src, len);
+ if (warn_stringop_overflow)
+ {
+ tree destsize = compute_objsize (dest,
+ warn_stringop_overflow - 1);
+
+ /* The number of bytes to write is LEN but check_access will also
+ check SLEN if LEN's value isn't known. */
+ check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
+ destsize);
+ }
/* We must be passed a constant len and src parameter. */
if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
tree val = CALL_EXPR_ARG (exp, 1);
tree len = CALL_EXPR_ARG (exp, 2);
- check_memop_sizes (exp, dest, NULL_TREE, len);
+ check_memop_access (exp, dest, NULL_TREE, len);
return expand_builtin_memset_args (dest, val, len, target, mode, exp);
}
tree dest = CALL_EXPR_ARG (exp, 0);
tree size = CALL_EXPR_ARG (exp, 1);
- check_memop_sizes (exp, dest, NULL_TREE, size);
+ check_memop_access (exp, dest, NULL_TREE, size);
/* New argument list transforming bzero(ptr x, int y) to
memset(ptr x, int 0, size_t y). This is done this way
if (warn_stringop_overflow)
{
tree size = compute_objsize (arg1, 0);
- if (check_sizes (OPT_Wstringop_overflow_,
- exp, len, /*maxlen=*/NULL_TREE,
- size, /*objsize=*/NULL_TREE))
+ if (check_access (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);
+ check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+ /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
}
}
return target;
break;
+ case BUILT_IN_MEMCPY_CHKP:
+ case BUILT_IN_MEMMOVE_CHKP:
+ case BUILT_IN_MEMPCPY_CHKP:
+ if (call_expr_nargs (exp) > 3)
+ {
+ /* memcpy_chkp (void *dst, size_t dstbnd,
+ const void *src, size_t srcbnd, size_t n)
+ and others take a pointer bound argument just after each
+ pointer argument. */
+ tree dest = CALL_EXPR_ARG (exp, 0);
+ tree src = CALL_EXPR_ARG (exp, 2);
+ tree len = CALL_EXPR_ARG (exp, 4);
+
+ check_memop_access (exp, dest, src, len);
+ break;
+ }
+
default:
break;
}
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
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,
- /*str=*/NULL_TREE, size);
+ bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
+ /*str=*/NULL_TREE, size);
if (!tree_fits_uhwi_p (size))
return NULL_RTX;
/* 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;
/* The exact size of the access (such as in __strncpy_chk). */
tree size = NULL_TREE;
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_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
return;
}
- check_sizes (OPT_Wstringop_overflow_, exp,
- size, maxlen, srcstr, objsize);
+ /* The destination argument is the first one for all built-ins above. */
+ tree dst = CALL_EXPR_ARG (exp, 0);
+
+ check_access (exp, dst, srcstr, size, maxread, srcstr, objsize);
}
/* Emit warning if a buffer overflow is detected at compile time
/* 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);
+
+ check_access (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. */
*p = (char)tree_to_uhwi (t);
return true;
}
+
+/* Return the maximum object size. */
+
+tree
+max_object_size (void)
+{
+ /* To do: Make this a configurable parameter. */
+ return TYPE_MAX_VALUE (ptrdiff_type_node);
+}
extern internal_fn associated_internal_fn (tree);
extern internal_fn replacement_internal_fn (gcall *);
-#endif
+extern tree max_object_size ();
+
+#endif /* GCC_BUILTINS_H */
+2017-12-16 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/78918
+ * c-common.c (check_function_restrict): Avoid checking built-ins.
+ * c.opt (-Wrestrict): Include in -Wall.
+
2017-12-15 Jakub Jelinek <jakub@redhat.com>
* c-attribs.c (c_common_attribute_table,
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;
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.
#include "intl.h"
#include "stringpool.h"
#include "attribs.h"
+#include "builtins.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
{
if (!alloc_object_size_limit)
{
- alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+ alloc_object_size_limit = max_object_size ();
if (warn_alloc_size_limit)
{
{
widest_int w = wi::mul (limit, unit);
if (w < wi::to_widest (alloc_object_size_limit))
- alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
+ alloc_object_size_limit
+ = wide_int_to_tree (ptrdiff_type_node, w);
}
}
}
}
/* Return true when EXP's range can be determined and set RANGE[] to it
- after adjusting it if necessary to make EXP a valid size argument to
- an allocation function declared with attribute alloc_size (whose
- argument may be signed), or to a string manipulation function like
- memset. */
+ after adjusting it if necessary to make EXP a represents a valid size
+ of object, or a valid size argument to an allocation function declared
+ with attribute alloc_size (whose argument may be signed), or to a string
+ manipulation function like memset. When ALLOW_ZERO is true, allow
+ returning a range of [0, 0] for a size in an anti-range [1, N] where
+ N > PTRDIFF_MAX. A zero range is a (nearly) invalid argument to
+ allocation functions like malloc but it is a valid argument to
+ functions like memset. */
bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
{
if (tree_fits_uhwi_p (exp))
{
return true;
}
+ tree exptype = TREE_TYPE (exp);
+ bool integral = INTEGRAL_TYPE_P (exptype);
+
wide_int min, max;
- enum value_range_type range_type
- = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
- ? get_range_info (exp, &min, &max) : VR_VARYING);
+ enum value_range_type range_type;
+
+ if (TREE_CODE (exp) == SSA_NAME && integral)
+ range_type = get_range_info (exp, &min, &max);
+ else
+ range_type = VR_VARYING;
if (range_type == VR_VARYING)
{
- /* No range information available. */
+ if (integral)
+ {
+ /* Use the full range of the type of the expression when
+ no value range information is available. */
+ range[0] = TYPE_MIN_VALUE (exptype);
+ range[1] = TYPE_MAX_VALUE (exptype);
+ return true;
+ }
+
range[0] = NULL_TREE;
range[1] = NULL_TREE;
return false;
}
- tree exptype = TREE_TYPE (exp);
unsigned expprec = TYPE_PRECISION (exptype);
bool signed_p = !TYPE_UNSIGNED (exptype);
{
/* EXP is unsigned and not in the range [1, MAX]. That means
it's either zero or greater than MAX. Even though 0 would
- normally be detected by -Walloc-zero set the range to
- [MAX, TYPE_MAX] so that when MAX is greater than the limit
- the whole range is diagnosed. */
- min = max + 1;
- max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+ normally be detected by -Walloc-zero, unless ALLOW_ZERO
+ is true, set the range to [MAX, TYPE_MAX] so that when MAX
+ is greater than the limit the whole range is diagnosed. */
+ if (allow_zero)
+ min = max = wi::zero (expprec);
+ else
+ {
+ min = max + 1;
+ max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+ }
}
else
{
extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
tree, bool);
extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern bool get_size_range (tree, tree[2]);
extern tree get_attr_nonstring_decl (tree, tree * = NULL);
extern void maybe_warn_nonstring_arg (tree, tree);
+extern bool get_size_range (tree, tree[2], bool = false);
#endif // GCC_CALLS_H
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);
-Wparentheses @gol
-Wpointer-sign @gol
-Wreorder @gol
+-Wrestrict @gol
-Wreturn-type @gol
-Wsequence-point @gol
-Wsign-compare @r{(only in C++)} @gol
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++, a @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
#include "ssa.h"
#include "cgraph.h"
#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
#include "fold-const.h"
#include "stmt.h"
#include "expr.h"
return wi::eq_p (min, wone) && wi::geu_p (max, ssize_max);
}
-/* Fold function call to builtin mem{{,p}cpy,move}. Return
- false if no simplification can be made.
- If ENDP is 0, return DEST (like memcpy).
- If ENDP is 1, return DEST+LEN (like mempcpy).
- If ENDP is 2, return DEST+LEN-1 (like stpcpy).
- If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
- (memmove). */
+/* Fold function call to builtin mem{{,p}cpy,move}. Try to detect and
+ diagnose (otherwise undefined) overlapping copies without preventing
+ folding. When folded, GCC guarantees that overlapping memcpy has
+ the same semantics as memmove. Call to the library memcpy need not
+ provide the same guarantee. Return false if no simplification can
+ be made. */
static bool
gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
tree destvar, srcvar;
location_t loc = gimple_location (stmt);
+ 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 a constant zero or in range where
the only valid value is zero, return DEST. */
if (size_must_be_zero_p (len))
DEST{,+LEN,+LEN-1}. */
if (operand_equal_p (src, dest, 0))
{
+ /* Avoid diagnosing exact overlap in calls to __builtin_memcpy.
+ It's safe and may even be emitted by GCC itself (see bug
+ 32667). However, diagnose it in explicit calls to the memcpy
+ function. */
+ if (check_overlap && *IDENTIFIER_POINTER (DECL_NAME (func)) != '_')
+ 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));
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);
+
scalar_int_mode mode;
tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
if (type
}
}
+ /* 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 *new_stmt;
if (is_gimple_reg_type (TREE_TYPE (srcvar)))
{
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:
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;
}
(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);
/* 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;
}
--- /dev/null
+/* Pass to detect and issue warnings for violations of the restrict
+ qualifier.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Contributed by Martin Sebor <msebor@redhat.com>.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "domwalk.h"
+#include "tree-pass.h"
+#include "builtins.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "gimple-iterator.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
+#include "params.h"
+#include "tree-cfg.h"
+#include "tree-object-size.h"
+#include "calls.h"
+#include "cfgloop.h"
+#include "intl.h"
+
+namespace {
+
+const pass_data pass_data_wrestrict = {
+ GIMPLE_PASS,
+ "wrestrict",
+ OPTGROUP_NONE,
+ TV_NONE,
+ PROP_cfg, /* Properties_required. */
+ 0, /* properties_provided. */
+ 0, /* properties_destroyed. */
+ 0, /* properties_start */
+ 0, /* properties_finish */
+};
+
+/* Pass to detect violations of strict aliasing requirements in calls
+ to built-in string and raw memory functions. */
+class pass_wrestrict : public gimple_opt_pass
+{
+ public:
+ pass_wrestrict (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_wrestrict, ctxt)
+ { }
+
+ opt_pass *clone () { return new pass_wrestrict (m_ctxt); }
+
+ virtual bool gate (function *);
+ virtual unsigned int execute (function *);
+};
+
+bool
+pass_wrestrict::gate (function *fun ATTRIBUTE_UNUSED)
+{
+ return warn_array_bounds != 0 || warn_restrict != 0;
+}
+
+/* Class to walk the basic blocks of a function in dominator order. */
+class wrestrict_dom_walker : public dom_walker
+{
+ public:
+ wrestrict_dom_walker () : dom_walker (CDI_DOMINATORS) {}
+
+ edge before_dom_children (basic_block) FINAL OVERRIDE;
+ bool handle_gimple_call (gimple_stmt_iterator *);
+
+ private:
+ void check_call (gcall *);
+};
+
+edge
+wrestrict_dom_walker::before_dom_children (basic_block bb)
+{
+ /* Iterate over statements, looking for function calls. */
+ for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
+ gsi_next (&si))
+ {
+ gimple *stmt = gsi_stmt (si);
+ if (!is_gimple_call (stmt))
+ continue;
+
+ if (gcall *call = as_a <gcall *> (stmt))
+ check_call (call);
+ }
+
+ return NULL;
+}
+
+/* Execute the pass for function FUN, walking in dominator order. */
+
+unsigned
+pass_wrestrict::execute (function *fun)
+{
+ calculate_dominance_info (CDI_DOMINATORS);
+
+ wrestrict_dom_walker walker;
+ walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
+
+ return 0;
+}
+
+/* Description of a memory reference by a built-in function. This
+ is similar to ao_ref but made especially suitable for -Wrestrict
+ and not for optimization. */
+struct builtin_memref
+{
+ /* The original pointer argument to the built-in function. */
+ tree ptr;
+ /* The referenced subobject or NULL if not available, and the base
+ object of the memory reference or NULL. */
+ tree ref;
+ tree base;
+
+ /* The size of the BASE object, PTRDIFF_MAX if indeterminate,
+ and negative until (possibly lazily) initialized. */
+ offset_int basesize;
+
+ /* The non-negative offset of the referenced subobject. Used to avoid
+ warnings for (apparently) possibly but not definitively overlapping
+ accesses to member arrays. Negative when unknown/invalid. */
+ offset_int refoff;
+
+ /* The offset range relative to the base. */
+ offset_int offrange[2];
+ /* The size range of the access to this reference. */
+ offset_int sizrange[2];
+
+ /* True for "bounded" string functions like strncat, and strncpy
+ and their variants that specify either an exact or upper bound
+ on the size of the accesses they perform. For strncat both
+ the source and destination references are bounded. For strncpy
+ only the destination reference is. */
+ bool strbounded_p;
+
+ builtin_memref (tree, tree);
+
+ tree offset_out_of_bounds (int, offset_int[2]) const;
+};
+
+/* Description of a memory access by a raw memory or string built-in
+ function involving a pair of builtin_memref's. */
+class builtin_access
+{
+ public:
+ /* Destination and source memory reference. */
+ builtin_memref* const dstref;
+ builtin_memref* const srcref;
+ /* The size range of the access. It's the greater of the accesses
+ to the two references. */
+ HOST_WIDE_INT sizrange[2];
+
+ /* The minimum and maximum offset of an overlap of the access
+ (if it does, in fact, overlap), and the size of the overlap. */
+ HOST_WIDE_INT ovloff[2];
+ HOST_WIDE_INT ovlsiz[2];
+
+ /* True to consider valid only accesses to the smallest subobject
+ and false for raw memory functions. */
+ bool strict () const
+ {
+ return detect_overlap != &builtin_access::generic_overlap;
+ }
+
+ builtin_access (gcall *, builtin_memref &, builtin_memref &);
+
+ /* Entry point to determine overlap. */
+ bool overlap ();
+
+ private:
+ /* Implementation functions used to determine overlap. */
+ bool generic_overlap ();
+ bool strcat_overlap ();
+ bool strcpy_overlap ();
+
+ bool no_overlap ()
+ {
+ return false;
+ }
+
+ offset_int overlap_size (const offset_int [2], const offset_int[2],
+ offset_int [2]);
+
+ private:
+ /* Temporaries used to compute the final result. */
+ offset_int dstoff[2];
+ offset_int srcoff[2];
+ offset_int dstsiz[2];
+ offset_int srcsiz[2];
+
+ /* Pointer to a member function to call to determine overlap. */
+ bool (builtin_access::*detect_overlap) ();
+};
+
+/* Initialize a memory reference representation from a pointer EXPR and
+ a size SIZE in bytes. If SIZE is NULL_TREE then the size is assumed
+ to be unknown. */
+
+builtin_memref::builtin_memref (tree expr, tree size)
+: ptr (expr),
+ ref (),
+ base (),
+ basesize (-1),
+ refoff (HOST_WIDE_INT_MIN),
+ offrange (),
+ sizrange (),
+ strbounded_p ()
+{
+ /* Unfortunately, wide_int default ctor is a no-op so array members
+ of the type must be set individually. */
+ offrange[0] = offrange[1] = 0;
+ sizrange[0] = sizrange[1] = 0;
+
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ if (TREE_CODE (expr) == SSA_NAME)
+ {
+ /* Try to tease the offset out of the pointer. */
+ gimple *stmt = SSA_NAME_DEF_STMT (expr);
+ if (gimple_assign_single_p (stmt)
+ && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+ expr = gimple_assign_rhs1 (stmt);
+ else if (is_gimple_assign (stmt))
+ {
+ tree_code code = gimple_assign_rhs_code (stmt);
+ if (code == NOP_EXPR)
+ {
+ tree rhs = gimple_assign_rhs1 (stmt);
+ if (POINTER_TYPE_P (TREE_TYPE (rhs)))
+ expr = gimple_assign_rhs1 (stmt);
+ }
+ else if (code == POINTER_PLUS_EXPR)
+ {
+ expr = gimple_assign_rhs1 (stmt);
+
+ tree offset = gimple_assign_rhs2 (stmt);
+ if (TREE_CODE (offset) == INTEGER_CST)
+ {
+ offset_int off = int_cst_value (offset);
+ offrange[0] = off;
+ offrange[1] = off;
+
+ if (TREE_CODE (expr) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (expr);
+ if (gimple_assign_single_p (stmt)
+ && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+ expr = gimple_assign_rhs1 (stmt);
+ }
+ }
+ else if (TREE_CODE (offset) == SSA_NAME)
+ {
+ wide_int min, max;
+ value_range_type rng = get_range_info (offset, &min, &max);
+ if (rng == VR_RANGE)
+ {
+ offrange[0] = min.to_shwi ();
+ offrange[1] = max.to_shwi ();
+ }
+ else if (rng == VR_ANTI_RANGE)
+ {
+ offrange[0] = (max + 1).to_shwi ();
+ offrange[1] = (min - 1).to_shwi ();
+ }
+ else
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (offset);
+ if (is_gimple_assign (stmt)
+ && gimple_assign_rhs_code (stmt) == NOP_EXPR)
+ {
+ /* Use the bounds of the type of the NOP_EXPR operand
+ even if it's signed. The result doesn't trigger
+ warnings but makes their output more readable. */
+ tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+ offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
+ offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+ }
+ else
+ offrange[1] = maxobjsize;
+ }
+ }
+ else
+ offrange[1] = maxobjsize;
+ }
+ }
+ }
+
+ if (TREE_CODE (expr) == ADDR_EXPR)
+ {
+ HOST_WIDE_INT off;
+ tree oper = TREE_OPERAND (expr, 0);
+
+ /* Determine the base object or pointer of the reference
+ and its constant offset from the beginning of the base. */
+ base = get_addr_base_and_unit_offset (oper, &off);
+
+ if (base)
+ {
+ offrange[0] += off;
+ offrange[1] += off;
+
+ /* Stash the reference for offset validation. */
+ ref = oper;
+
+ /* Also stash the constant offset for offset validation. */
+ tree_code code = TREE_CODE (oper);
+ if (code == COMPONENT_REF)
+ {
+ tree field = TREE_OPERAND (ref, 1);
+ tree fldoff = DECL_FIELD_OFFSET (field);
+ if (TREE_CODE (fldoff) == INTEGER_CST)
+ refoff = off + wi::to_offset (fldoff);
+ }
+ }
+ else
+ {
+ size = NULL_TREE;
+ base = get_base_address (TREE_OPERAND (expr, 0));
+ }
+ }
+
+ if (!base)
+ base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+
+ if (TREE_CODE (base) == MEM_REF)
+ {
+ offset_int off = mem_ref_offset (base);
+ refoff += off;
+ offrange[0] += off;
+ offrange[1] += off;
+ base = TREE_OPERAND (base, 0);
+ }
+
+ if (TREE_CODE (base) == SSA_NAME)
+ if (gimple *stmt = SSA_NAME_DEF_STMT (base))
+ {
+ enum gimple_code code = gimple_code (stmt);
+ if (code == GIMPLE_ASSIGN)
+ if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+ {
+ base = gimple_assign_rhs1 (stmt);
+
+ tree offset = gimple_assign_rhs2 (stmt);
+ if (TREE_CODE (offset) == INTEGER_CST)
+ {
+ offset_int off = int_cst_value (offset);
+ refoff += off;
+ offrange[0] += off;
+ offrange[1] += off;
+ }
+ }
+
+ if (TREE_CODE (base) == SSA_NAME && SSA_NAME_VAR (base))
+ base = SSA_NAME_VAR (base);
+ }
+
+ if (size)
+ {
+ tree range[2];
+ /* Determine the size range, allowing for the result to be [0, 0]
+ for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX. */
+ get_size_range (size, range, true);
+ sizrange[0] = wi::to_offset (range[0]);
+ sizrange[1] = wi::to_offset (range[1]);
+ /* get_size_range returns SIZE_MAX for the maximum size.
+ Constrain it to the real maximum of PTRDIFF_MAX. */
+ if (sizrange[1] > maxobjsize)
+ sizrange[1] = maxobjsize;
+ }
+ else
+ sizrange[1] = maxobjsize;
+}
+
+/* Return error_mark_node if the signed offset exceeds the bounds
+ of the address space (PTRDIFF_MAX). Otherwise, return either
+ BASE or REF when the offset exceeds the bounds of the BASE or
+ REF object, and set OOBOFF to the past-the-end offset formed
+ by the reference, including its size. When STRICT is non-zero
+ use REF size, when available, otherwise use BASE size. When
+ STRICT is greater than 1, use the size of the last array member
+ as the bound, otherwise treat such a member as a flexible array
+ member. Return NULL when the offset is in bounds. */
+
+tree
+builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const
+{
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ /* A temporary, possibly adjusted, copy of the offset range. */
+ offset_int offrng[2] = { offrange[0], offrange[1] };
+
+ if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+ {
+ if (offrng[1] < offrng[0])
+ offrng[1] = maxobjsize;
+ }
+
+ /* Conservative offset of the last byte of the referenced object. */
+ offset_int endoff;
+
+ /* The bounds need not be ordered. Set HIB to use as the index
+ of the larger of the bounds and LOB as the opposite. */
+ bool hib = wi::les_p (offrng[0], offrng[1]);
+ bool lob = !hib;
+
+ if (basesize < 0)
+ {
+ endoff = offrng[lob] + sizrange[0];
+
+ /* For a reference through a pointer to an object of unknown size
+ all initial offsets are considered valid, positive as well as
+ negative, since the pointer itself can point past the beginning
+ of the object. However, the sum of the lower bound of the offset
+ and that of the size must be less than or equal than PTRDIFF_MAX. */
+ if (endoff > maxobjsize)
+ return error_mark_node;
+
+ return NULL_TREE;
+ }
+
+ /* A reference to an object of known size must be within the bounds
+ of the base object. */
+ if (offrng[hib] < 0 || offrng[lob] > basesize)
+ return base;
+
+ /* The extent of the reference must also be within the bounds of
+ the base object (if known) or the maximum object size otherwise. */
+ endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+ if (endoff > maxobjsize)
+ return error_mark_node;
+
+ offset_int size = basesize;
+ tree obj = base;
+
+ if (strict
+ && DECL_P (obj)
+ && ref
+ && refoff >= 0
+ && TREE_CODE (ref) == COMPONENT_REF
+ && (strict > 1
+ || !array_at_struct_end_p (ref)))
+ {
+ /* If the reference is to a member subobject, the offset must
+ be within the bounds of the subobject. */
+ tree field = TREE_OPERAND (ref, 1);
+ tree type = TREE_TYPE (field);
+ if (tree sz = TYPE_SIZE_UNIT (type))
+ if (TREE_CODE (sz) == INTEGER_CST)
+ {
+ size = refoff + wi::to_offset (sz);
+ obj = ref;
+ }
+ }
+
+ if (endoff <= size)
+ return NULL_TREE;
+
+ /* Set the out-of-bounds offset range to be one greater than
+ that delimited by the reference including its size. */
+ ooboff[lob] = size + 1;
+
+ if (endoff > ooboff[lob])
+ ooboff[hib] = endoff;
+ else
+ ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1];
+
+ return obj;
+}
+
+/* Create an association between the memory references DST and SRC
+ for access by a call EXPR to a memory or string built-in funtion. */
+
+builtin_access::builtin_access (gcall *call, builtin_memref &dst,
+ builtin_memref &src)
+: dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
+ dstoff (), srcoff (), dstsiz (), srcsiz ()
+{
+ /* Zero out since the offset_int ctors invoked above are no-op. */
+ dstoff[0] = dstoff[1] = 0;
+ srcoff[0] = srcoff[1] = 0;
+ dstsiz[0] = dstsiz[1] = 0;
+ srcsiz[0] = srcsiz[1] = 0;
+
+ /* Object Size Type to use to determine the size of the destination
+ and source objects. Overridden below for raw memory functions. */
+ int ostype = 1;
+
+ /* True when the size of one reference depends on the offset of
+ itself or the other. */
+ bool depends_p = true;
+
+ /* True when the size of the destination reference DSTREF has been
+ determined from SRCREF and so needs to be adjusted by the latter's
+ offset. Only meaningful for bounded string functions like strncpy. */
+ bool dstadjust_p = false;
+
+ /* The size argument number (depends on the built-in). */
+ unsigned sizeargno = 2;
+ if (gimple_call_with_bounds_p (call))
+ sizeargno += 2;
+
+ tree func = gimple_call_fndecl (call);
+ switch (DECL_FUNCTION_CODE (func))
+ {
+ case BUILT_IN_MEMCPY:
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMCPY_CHKP:
+ case BUILT_IN_MEMCPY_CHK_CHKP:
+ case BUILT_IN_MEMPCPY:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMPCPY_CHKP:
+ case BUILT_IN_MEMPCPY_CHK_CHKP:
+ ostype = 0;
+ depends_p = false;
+ detect_overlap = &builtin_access::generic_overlap;
+ break;
+
+ case BUILT_IN_MEMMOVE:
+ case BUILT_IN_MEMMOVE_CHK:
+ case BUILT_IN_MEMMOVE_CHKP:
+ case BUILT_IN_MEMMOVE_CHK_CHKP:
+ /* For memmove there is never any overlap to check for. */
+ ostype = 0;
+ depends_p = false;
+ detect_overlap = &builtin_access::no_overlap;
+ break;
+
+ case BUILT_IN_STPNCPY:
+ case BUILT_IN_STPNCPY_CHK:
+ case BUILT_IN_STRNCPY:
+ case BUILT_IN_STRNCPY_CHK:
+ dstref->strbounded_p = true;
+ detect_overlap = &builtin_access::strcpy_overlap;
+ break;
+
+ case BUILT_IN_STPCPY:
+ case BUILT_IN_STPCPY_CHK:
+ case BUILT_IN_STPCPY_CHKP:
+ case BUILT_IN_STPCPY_CHK_CHKP:
+ case BUILT_IN_STRCPY:
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STRCPY_CHKP:
+ case BUILT_IN_STRCPY_CHK_CHKP:
+ detect_overlap = &builtin_access::strcpy_overlap;
+ break;
+
+ case BUILT_IN_STRCAT:
+ case BUILT_IN_STRCAT_CHK:
+ case BUILT_IN_STRCAT_CHKP:
+ case BUILT_IN_STRCAT_CHK_CHKP:
+ detect_overlap = &builtin_access::strcat_overlap;
+ break;
+
+ case BUILT_IN_STRNCAT:
+ case BUILT_IN_STRNCAT_CHK:
+ dstref->strbounded_p = true;
+ srcref->strbounded_p = true;
+ detect_overlap = &builtin_access::strcat_overlap;
+ break;
+
+ default:
+ /* Handle other string functions here whose access may need
+ to be validated for in-bounds offsets and non-overlapping
+ copies. (Not all _chkp functions have BUILT_IN_XXX_CHKP
+ macros so they need to be handled here.) */
+ return;
+ }
+
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ /* Try to determine the size of the base object. compute_objsize
+ expects a pointer so create one if BASE is a non-pointer object. */
+ tree addr;
+ if (dst.basesize < 0)
+ {
+ addr = dst.base;
+ if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+ addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+ if (tree dstsize = compute_objsize (addr, ostype))
+ dst.basesize = wi::to_offset (dstsize);
+ else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+ dst.basesize = HOST_WIDE_INT_MIN;
+ else
+ dst.basesize = maxobjsize;
+ }
+
+ if (src.basesize < 0)
+ {
+ addr = src.base;
+ if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+ addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+ if (tree srcsize = compute_objsize (addr, ostype))
+ src.basesize = wi::to_offset (srcsize);
+ else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+ src.basesize = HOST_WIDE_INT_MIN;
+ else
+ src.basesize = maxobjsize;
+ }
+
+ /* If there is no dependency between the references or the base
+ objects of the two references aren't the same there's nothing
+ else to do. */
+ if (depends_p && dstref->base != srcref->base)
+ return;
+
+ /* ...otherwise, make adjustments for references to the same object
+ by string built-in functions to reflect the constraints imposed
+ by the function. */
+
+ /* For bounded string functions determine the range of the bound
+ on the access. For others, the range stays unbounded. */
+ offset_int bounds[2] = { maxobjsize, maxobjsize };
+ if (dstref->strbounded_p)
+ {
+ tree size = gimple_call_arg (call, sizeargno);
+ tree range[2];
+ if (get_size_range (size, range, true))
+ {
+ bounds[0] = wi::to_offset (range[0]);
+ bounds[1] = wi::to_offset (range[1]);
+ }
+
+ /* If both references' size ranges are indeterminate use the last
+ (size) argument from the function call as a substitute. This
+ may only be necessary for strncpy (but not for memcpy where
+ the size range would have been already determined this way). */
+ if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize
+ && srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+ {
+ dstref->sizrange[0] = bounds[0];
+ dstref->sizrange[1] = bounds[1];
+ }
+ }
+
+ /* The size range of one reference involving the same base object
+ can be determined from the size range of the other reference.
+ This makes it possible to compute accurate offsets for warnings
+ involving functions like strcpy where the length of just one of
+ the two arguments is known (determined by tree-ssa-strlen). */
+ if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize)
+ {
+ /* When the destination size is unknown set it to the size of
+ the source. */
+ dstref->sizrange[0] = srcref->sizrange[0];
+ dstref->sizrange[1] = srcref->sizrange[1];
+ }
+ else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+ {
+ /* When the source size is unknown set it to the size of
+ the destination. */
+ srcref->sizrange[0] = dstref->sizrange[0];
+ srcref->sizrange[1] = dstref->sizrange[1];
+
+ if (depends_p)
+ {
+ if (dstref->strbounded_p)
+ {
+ /* Read access by strncpy is bounded. */
+ if (bounds[0] < srcref->sizrange[0])
+ srcref->sizrange[0] = bounds[0];
+ if (bounds[1] < srcref->sizrange[1])
+ srcref->sizrange[1] = bounds[1];
+ }
+
+ /* For string functions, adjust the size range of the source
+ reference by the inverse boundaries of the offset (because
+ the higher the offset into the string the shorter its
+ length). */
+ if (srcref->offrange[1] < srcref->sizrange[0])
+ srcref->sizrange[0] -= srcref->offrange[1];
+ else
+ srcref->sizrange[0] = 0;
+
+ if (srcref->offrange[0] > 0)
+ {
+ if (srcref->offrange[0] < srcref->sizrange[1])
+ srcref->sizrange[1] -= srcref->offrange[0];
+ else
+ srcref->sizrange[1] = 0;
+ }
+
+ dstadjust_p = true;
+ }
+ }
+
+ if (detect_overlap == &builtin_access::generic_overlap)
+ {
+ if (dstref->strbounded_p)
+ {
+ dstref->sizrange[0] = bounds[0];
+ dstref->sizrange[1] = bounds[1];
+
+ if (dstref->sizrange[0] < srcref->sizrange[0])
+ srcref->sizrange[0] = dstref->sizrange[0];
+
+ if (dstref->sizrange[1] < srcref->sizrange[1])
+ srcref->sizrange[1] = dstref->sizrange[1];
+ }
+ }
+ else if (detect_overlap == &builtin_access::strcpy_overlap)
+ {
+ if (!dstref->strbounded_p)
+ {
+ /* For strcpy, adjust the destination size range to match that
+ of the source computed above. */
+ if (depends_p && dstadjust_p)
+ {
+ dstref->sizrange[0] = srcref->sizrange[0];
+ dstref->sizrange[1] = srcref->sizrange[1];
+ }
+ }
+ }
+
+ if (dstref->strbounded_p)
+ {
+ /* For strncpy, adjust the destination size range to match that
+ of the source computed above. */
+ dstref->sizrange[0] = bounds[0];
+ dstref->sizrange[1] = bounds[1];
+
+ if (bounds[0] < srcref->sizrange[0])
+ srcref->sizrange[0] = bounds[0];
+
+ if (bounds[1] < srcref->sizrange[1])
+ srcref->sizrange[1] = bounds[1];
+ }
+}
+
+offset_int
+builtin_access::overlap_size (const offset_int a[2], const offset_int b[2],
+ offset_int *off)
+{
+ const offset_int *p = a;
+ const offset_int *q = b;
+
+ /* Point P at the bigger of the two ranges and Q at the smaller. */
+ if (wi::lts_p (a[1] - a[0], b[1] - b[0]))
+ {
+ p = b;
+ q = a;
+ }
+
+ if (p[0] < q[0])
+ {
+ if (p[1] < q[0])
+ return 0;
+
+ *off = q[0];
+ return wi::smin (p[1], q[1]) - q[0];
+ }
+
+ if (q[1] < p[0])
+ return 0;
+
+ off[0] = p[0];
+ return q[1] - p[0];
+}
+
+/* Return true if the bounded mempry (memcpy amd similar) or string function
+ access (strncpy and similar) ACS overlaps. */
+
+bool
+builtin_access::generic_overlap ()
+{
+ builtin_access &acs = *this;
+ const builtin_memref *dstref = acs.dstref;
+ const builtin_memref *srcref = acs.srcref;
+
+ gcc_assert (dstref->base == srcref->base);
+
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
+ gcc_assert (maxsize <= maxobjsize);
+
+ /* Adjust the larger bounds of the offsets (which may be the first
+ element if the lower bound is larger than the upper bound) to
+ make them valid for the smallest access (if possible) but no smaller
+ than the smaller bounds. */
+ gcc_assert (wi::les_p (acs.dstoff[0], acs.dstoff[1]));
+
+ if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+ acs.dstoff[1] = maxsize - acs.dstsiz[0];
+ if (acs.dstoff[1] < acs.dstoff[0])
+ acs.dstoff[1] = acs.dstoff[0];
+
+ gcc_assert (wi::les_p (acs.srcoff[0], acs.srcoff[1]));
+
+ if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+ acs.srcoff[1] = maxsize - acs.srcsiz[0];
+ if (acs.srcoff[1] < acs.srcoff[0])
+ acs.srcoff[1] = acs.srcoff[0];
+
+ /* Determine the minimum and maximum space for the access given
+ the offsets. */
+ offset_int space[2];
+ space[0] = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+ space[1] = space[0];
+
+ offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+ if (acs.srcsiz[0] > 0)
+ {
+ if (d < space[0])
+ space[0] = d;
+
+ if (space[1] < d)
+ space[1] = d;
+ }
+ else
+ space[1] = acs.dstsiz[1];
+
+ d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+ if (d < space[0])
+ space[0] = d;
+
+ if (space[1] < d)
+ space[1] = d;
+
+ /* Treat raw memory functions both of whose references are bounded
+ as special and permit uncertain overlaps to go undetected. For
+ all kinds of constant offset and constant size accesses, if
+ overlap isn't certain it is not possible. */
+ bool overlap_possible = space[0] < acs.dstsiz[1];
+ if (!overlap_possible)
+ return false;
+
+ bool overlap_certain = space[1] < acs.dstsiz[0];
+
+ /* True when the size of one reference depends on the offset of
+ the other. */
+ bool depends_p = detect_overlap != &builtin_access::generic_overlap;
+
+ if (!overlap_certain
+ && !dstref->strbounded_p
+ && !depends_p)
+ return false;
+
+ /* True for stpcpy and strcpy. */
+ bool stxcpy_p = (!dstref->strbounded_p
+ && detect_overlap == &builtin_access::strcpy_overlap);
+
+ if (dstref->refoff >= 0
+ && srcref->refoff >= 0
+ && dstref->refoff != srcref->refoff
+ && (stxcpy_p || dstref->strbounded_p || srcref->strbounded_p))
+ return false;
+
+ offset_int siz[2] = { maxobjsize + 1, 0 };
+
+ ovloff[0] = HOST_WIDE_INT_MAX;
+ ovloff[1] = HOST_WIDE_INT_MIN;
+
+ /* Adjustment to the lower bound of the offset of the overlap to
+ account for a subset of unbounded string calls where the size
+ of the destination string depends on the length of the source
+ which in turn depends on the offset into it. */
+ bool sub1;
+
+ if (stxcpy_p)
+ {
+ sub1 = acs.dstoff[0] <= acs.srcoff[0];
+
+ /* Iterate over the extreme locations (on the horizontal axis formed
+ by their offsets) and sizes of two regions and find their smallest
+ and largest overlap and the corresponding offsets. */
+ for (unsigned i = 0; i != 2; ++i)
+ {
+ const offset_int a[2] = {
+ acs.dstoff[i], acs.dstoff[i] + acs.dstsiz[!i]
+ };
+
+ const offset_int b[2] = {
+ acs.srcoff[i], acs.srcoff[i] + acs.srcsiz[!i]
+ };
+
+ offset_int off;
+ offset_int sz = overlap_size (a, b, &off);
+ if (sz < siz[0])
+ siz[0] = sz;
+
+ if (siz[1] <= sz)
+ siz[1] = sz;
+
+ if (sz != 0)
+ {
+ if (wi::lts_p (off, ovloff[0]))
+ ovloff[0] = off.to_shwi ();
+ if (wi::lts_p (ovloff[1], off))
+ ovloff[1] = off.to_shwi ();
+ }
+ }
+ }
+ else
+ {
+ sub1 = !depends_p;
+
+ /* Iterate over the extreme locations (on the horizontal axis
+ formed by their offsets) and sizes of two regions and find
+ their smallest and largest overlap and the corresponding
+ offsets. */
+
+ for (unsigned io = 0; io != 2; ++io)
+ for (unsigned is = 0; is != 2; ++is)
+ {
+ const offset_int a[2] = {
+ acs.dstoff[io], acs.dstoff[io] + acs.dstsiz[is]
+ };
+
+ for (unsigned jo = 0; jo != 2; ++jo)
+ for (unsigned js = 0; js != 2; ++js)
+ {
+ if (depends_p)
+ {
+ /* For st{p,r}ncpy the size of the source sequence
+ depends on the offset into it. */
+ if (js)
+ break;
+ js = !jo;
+ }
+
+ const offset_int b[2] = {
+ acs.srcoff[jo], acs.srcoff[jo] + acs.srcsiz[js]
+ };
+
+ offset_int off;
+ offset_int sz = overlap_size (a, b, &off);
+ if (sz < siz[0])
+ siz[0] = sz;
+
+ if (siz[1] <= sz)
+ siz[1] = sz;
+
+ if (sz != 0)
+ {
+ if (wi::lts_p (off, ovloff[0]))
+ ovloff[0] = off.to_shwi ();
+ if (wi::lts_p (ovloff[1], off))
+ ovloff[1] = off.to_shwi ();
+ }
+ }
+ }
+ }
+
+ ovlsiz[0] = siz[0].to_shwi ();
+ ovlsiz[1] = siz[1].to_shwi ();
+
+ if (ovlsiz[0] == 0 && ovlsiz[1] > 1)
+ ovloff[0] = ovloff[1] + ovlsiz[1] - 1 - sub1;
+
+ return true;
+}
+
+/* Return true if the strcat-like access overlaps. */
+
+bool
+builtin_access::strcat_overlap ()
+{
+ builtin_access &acs = *this;
+ const builtin_memref *dstref = acs.dstref;
+ const builtin_memref *srcref = acs.srcref;
+
+ gcc_assert (dstref->base == srcref->base);
+
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ gcc_assert (dstref->base && dstref->base == srcref->base);
+
+ /* Adjust for strcat-like accesses. */
+
+ /* As a special case for strcat, set the DSTREF offsets to the length
+ of the source string since the function starts writing at the first
+ nul, and set the size to 1 for the length of the nul. */
+ acs.dstoff[0] += acs.dstsiz[0];
+ acs.dstoff[1] += acs.dstsiz[1];
+
+ bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
+
+ /* The lower bound is zero when the size is unknown because then
+ overlap is not certain. */
+ acs.dstsiz[0] = strfunc_unknown_args ? 0 : 1;
+ acs.dstsiz[1] = 1;
+
+ offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
+ gcc_assert (maxsize <= maxobjsize);
+
+ /* For references to the same base object, determine if there's a pair
+ of valid offsets into the two references such that access between
+ them doesn't overlap. Adjust both upper bounds to be valid for
+ the smaller size (i.e., at most MAXSIZE - SIZE). */
+
+ if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+ acs.dstoff[1] = maxsize - acs.dstsiz[0];
+
+ if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+ acs.srcoff[1] = maxsize - acs.srcsiz[0];
+
+ /* Check to see if there's enough space for both accesses without
+ overlap. Determine the optimistic (maximum) amount of available
+ space. */
+ offset_int space;
+ if (acs.dstoff[0] <= acs.srcoff[0])
+ {
+ if (acs.dstoff[1] < acs.srcoff[1])
+ space = acs.srcoff[1] + acs.srcsiz[0] - acs.dstoff[0];
+ else
+ space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+ }
+ else
+ space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+
+ /* Overlap is certain if the distance between the farthest offsets
+ of the opposite accesses is less than the sum of the lower bounds
+ of the sizes of the two accesses. */
+ bool overlap_certain = space < acs.dstsiz[0] + acs.srcsiz[0];
+
+ /* For a constant-offset, constant size access, consider the largest
+ distance between the offset bounds and the lower bound of the access
+ size. If the overlap isn't certain return success. */
+ if (!overlap_certain
+ && acs.dstoff[0] == acs.dstoff[1]
+ && acs.srcoff[0] == acs.srcoff[1]
+ && acs.dstsiz[0] == acs.dstsiz[1]
+ && acs.srcsiz[0] == acs.srcsiz[1])
+ return false;
+
+ /* Overlap is not certain but may be possible. */
+
+ offset_int access_min = acs.dstsiz[0] + acs.srcsiz[0];
+
+ /* Determine the conservative (minimum) amount of space. */
+ space = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+ offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+ if (d < space)
+ space = d;
+ d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+ if (d < space)
+ space = d;
+
+ /* For a strict test (used for strcpy and similar with unknown or
+ variable bounds or sizes), consider the smallest distance between
+ the offset bounds and either the upper bound of the access size
+ if known, or the lower bound otherwise. */
+ if (access_min <= space && (access_min != 0 || !strfunc_unknown_args))
+ return false;
+
+ /* When strcat overlap is certain it is always a single byte:
+ the terminatinn NUL, regardless of offsets and sizes. When
+ overlap is only possible its range is [0, 1]. */
+ acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
+ acs.ovlsiz[1] = 1;
+ acs.ovloff[0] = (dstref->sizrange[0] + dstref->offrange[0]).to_shwi ();
+ acs.ovloff[1] = (dstref->sizrange[1] + dstref->offrange[1]).to_shwi ();
+
+ acs.sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+ acs.sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+ return true;
+}
+
+/* Return true if the strcpy-like access overlaps. */
+
+bool
+builtin_access::strcpy_overlap ()
+{
+ return generic_overlap ();
+}
+
+
+/* Return true if DSTREF and SRCREF describe accesses that either overlap
+ one another or that, in order not to overlap, would imply that the size
+ of the referenced object(s) exceeds the maximum size of an object. Set
+ Otherwise, if DSTREF and SRCREF do not definitely overlap (even though
+ they may overlap in a way that's not apparent from the available data),
+ return false. */
+
+bool
+builtin_access::overlap ()
+{
+ builtin_access &acs = *this;
+
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ acs.sizrange[0] = wi::smax (dstref->sizrange[0],
+ srcref->sizrange[0]).to_shwi ();
+ acs.sizrange[1] = wi::smax (dstref->sizrange[1],
+ srcref->sizrange[1]).to_shwi ();
+
+ /* Check to see if the two references refer to regions that are
+ too large not to overlap in the address space (whose maximum
+ size is PTRDIFF_MAX). */
+ offset_int size = dstref->sizrange[0] + srcref->sizrange[0];
+ if (maxobjsize < size)
+ {
+ acs.ovloff[0] = (maxobjsize - dstref->sizrange[0]).to_shwi ();
+ acs.ovlsiz[0] = (size - maxobjsize).to_shwi ();
+ return true;
+ }
+
+ /* If both base objects aren't known return the maximum possible
+ offset that would make them not overlap. */
+ if (!dstref->base || !srcref->base)
+ return false;
+
+ /* If the base object is an array adjust the lower bound of the offset
+ to be non-negative. */
+ if (dstref->base
+ && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
+ acs.dstoff[0] = wi::smax (dstref->offrange[0], 0);
+ else
+ acs.dstoff[0] = dstref->offrange[0];
+
+ acs.dstoff[1] = dstref->offrange[1];
+
+ if (srcref->base
+ && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
+ acs.srcoff[0] = wi::smax (srcref->offrange[0], 0);
+ else
+ acs.srcoff[0] = srcref->offrange[0];
+
+ acs.srcoff[1] = srcref->offrange[1];
+
+ /* When the lower bound of the offset is less that the upper bound
+ disregard it and use the inverse of the maximum object size
+ instead. The upper bound is the result of a negative offset
+ being represented as a large positive value. */
+ if (acs.dstoff[1] < acs.dstoff[0])
+ acs.dstoff[0] = -maxobjsize;
+
+ /* Validate the offset and size of each reference on its own first.
+ This is independent of whether or not the base objects are the
+ same. Normally, this would have already been detected and
+ diagnosed by -Warray-bounds, unless it has been disabled. */
+ offset_int maxoff = acs.dstoff[0] + dstref->sizrange[0];
+ if (maxobjsize < maxoff)
+ {
+ acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+ acs.ovloff[0] = acs.dstoff[0].to_shwi () - acs.ovlsiz[0];
+ return true;
+ }
+
+ /* Repeat the same as above but for the source offsets. */
+ if (acs.srcoff[1] < acs.srcoff[0])
+ acs.srcoff[0] = -maxobjsize;
+
+ maxoff = acs.srcoff[0] + srcref->sizrange[0];
+ if (maxobjsize < maxoff)
+ {
+ acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+ acs.ovlsiz[1] = (acs.srcoff[0] + srcref->sizrange[1]
+ - maxobjsize).to_shwi ();
+ acs.ovloff[0] = acs.srcoff[0].to_shwi () - acs.ovlsiz[0];
+ return true;
+ }
+
+ if (dstref->base != srcref->base)
+ return false;
+
+ acs.dstsiz[0] = dstref->sizrange[0];
+ acs.dstsiz[1] = dstref->sizrange[1];
+
+ acs.srcsiz[0] = srcref->sizrange[0];
+ acs.srcsiz[1] = srcref->sizrange[1];
+
+ /* Call the appropriate function to determine the overlap. */
+ if ((this->*detect_overlap) ())
+ {
+ sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+ sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+ return true;
+ }
+
+ return false;
+}
+
+/* Attempt to detect and diagnose an overlapping copy in a call expression
+ EXPR involving an an access ACS to a built-in memory or string function.
+ Return true when one has been detected, false otherwise. */
+
+static bool
+maybe_diag_overlap (location_t loc, gcall *call, builtin_access &acs)
+{
+ if (!acs.overlap ())
+ return false;
+
+ /* For convenience. */
+ const builtin_memref &dstref = *acs.dstref;
+ const builtin_memref &srcref = *acs.srcref;
+
+ /* Determine the range of offsets and sizes of the overlap if it
+ exists and issue diagnostics. */
+ HOST_WIDE_INT *ovloff = acs.ovloff;
+ HOST_WIDE_INT *ovlsiz = acs.ovlsiz;
+ HOST_WIDE_INT *sizrange = acs.sizrange;
+
+ tree func = gimple_call_fndecl (call);
+
+ /* To avoid a combinatorial explosion of diagnostics format the offsets
+ or their ranges as strings and use them in the warning calls below. */
+ char offstr[3][64];
+
+ if (dstref.offrange[0] == dstref.offrange[1]
+ || dstref.offrange[1] > HOST_WIDE_INT_MAX)
+ sprintf (offstr[0], "%lli", (long long) dstref.offrange[0].to_shwi ());
+ else
+ sprintf (offstr[0], "[%lli, %lli]",
+ (long long) dstref.offrange[0].to_shwi (),
+ (long long) dstref.offrange[1].to_shwi ());
+
+ if (srcref.offrange[0] == srcref.offrange[1]
+ || srcref.offrange[1] > HOST_WIDE_INT_MAX)
+ sprintf (offstr[1], "%lli", (long long) srcref.offrange[0].to_shwi ());
+ else
+ sprintf (offstr[1], "[%lli, %lli]",
+ (long long) srcref.offrange[0].to_shwi (),
+ (long long) srcref.offrange[1].to_shwi ());
+
+ if (ovloff[0] == ovloff[1] || !ovloff[1])
+ sprintf (offstr[2], "%lli", (long long) ovloff[0]);
+ else
+ sprintf (offstr[2], "[%lli, %lli]",
+ (long long) ovloff[0], (long long) ovloff[1]);
+
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+ bool must_overlap = ovlsiz[0] > 0;
+
+ if (ovlsiz[1] == 0)
+ ovlsiz[1] = ovlsiz[0];
+
+ if (must_overlap)
+ {
+ /* Issue definitive "overlaps" diagnostic in this block. */
+
+ if (sizrange[0] == sizrange[1])
+ {
+ if (ovlsiz[0] == ovlsiz[1])
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[0] == 1
+ ? (ovlsiz[0] == 1
+ ? G_("%G%qD accessing %wu byte at offsets %s "
+ "and %s overlaps %wu byte at offset %s")
+ : G_("%G%qD accessing %wu byte at offsets %s "
+ "and %s overlaps %wu bytes at offset "
+ "%s"))
+ : (ovlsiz[0] == 1
+ ? G_("%G%qD accessing %wu bytes at offsets %s "
+ "and %s overlaps %wu byte at offset %s")
+ : G_("%G%qD accessing %wu bytes at offsets %s "
+ "and %s overlaps %wu bytes at offset "
+ "%s")),
+ call, func, sizrange[0],
+ offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+ else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[0] == 1
+ ? G_("%G%qD accessing %wu byte at offsets %s "
+ "and %s overlaps between %wu and %wu bytes "
+ "at offset %s")
+ : G_("%G%qD accessing %wu bytes at offsets %s "
+ "and %s overlaps between %wu and %wu bytes "
+ "at offset %s"),
+ call, func, sizrange[0],
+ offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+ offstr[2]);
+ else
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[0] == 1
+ ? G_("%G%qD accessing %wu byte at offsets %s and "
+ "%s overlaps %wu or more bytes at offset %s")
+ : G_("%G%qD accessing %wu bytes at offsets %s and "
+ "%s overlaps %wu or more bytes at offset %s"),
+ call, func, sizrange[0],
+ offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+ return true;
+ }
+
+ if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+ {
+ if (ovlsiz[0] == ovlsiz[1])
+ warning_at (loc, OPT_Wrestrict,
+ ovlsiz[0] == 1
+ ? G_("%G%qD accessing between %wu and %wu bytes "
+ "at offsets %s and %s overlaps %wu byte at "
+ "offset %s")
+ : G_("%G%qD accessing between %wu and %wu bytes "
+ "at offsets %s and %s overlaps %wu bytes "
+ "at offset %s"),
+ call, func, sizrange[0], sizrange[1],
+ offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+ else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+ warning_at (loc, OPT_Wrestrict,
+ "%G%qD accessing between %wu and %wu bytes at "
+ "offsets %s and %s overlaps between %wu and %wu "
+ "bytes at offset %s",
+ call, func, sizrange[0], sizrange[1],
+ offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+ offstr[2]);
+ else
+ warning_at (loc, OPT_Wrestrict,
+ "%G%qD accessing between %wu and %wu bytes at "
+ "offsets %s and %s overlaps %wu or more bytes "
+ "at offset %s",
+ call, func, sizrange[0], sizrange[1],
+ offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+ return true;
+ }
+
+ if (ovlsiz[0] != ovlsiz[1])
+ ovlsiz[1] = maxobjsize.to_shwi ();
+
+ if (ovlsiz[0] == ovlsiz[1])
+ warning_at (loc, OPT_Wrestrict,
+ ovlsiz[0] == 1
+ ? G_("%G%qD accessing %wu or more bytes at offsets "
+ "%s and %s overlaps %wu byte at offset %s")
+ : G_("%G%qD accessing %wu or more bytes at offsets "
+ "%s and %s overlaps %wu bytes at offset %s"),
+ call, func, sizrange[0], offstr[0], offstr[1],
+ ovlsiz[0], offstr[2]);
+ else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+ warning_at (loc, OPT_Wrestrict,
+ "%G%qD accessing %wu or more bytes at offsets %s "
+ "and %s overlaps between %wu and %wu bytes "
+ "at offset %s",
+ call, func, sizrange[0], offstr[0], offstr[1],
+ ovlsiz[0], ovlsiz[1], offstr[2]);
+ else
+ warning_at (loc, OPT_Wrestrict,
+ "%G%qD accessing %wu or more bytes at offsets %s "
+ "and %s overlaps %wu or more bytes at offset %s",
+ call, func, sizrange[0], offstr[0], offstr[1],
+ ovlsiz[0], offstr[2]);
+ return true;
+ }
+
+ /* Issue "may overlap" diagnostics below. */
+ gcc_assert (ovlsiz[0] == 0
+ && ovlsiz[1] > 0
+ && ovlsiz[1] <= maxobjsize.to_shwi ());
+
+ /* Use more concise wording when one of the offsets is unbounded
+ to avoid confusing the user with large and mostly meaningless
+ numbers. */
+ bool open_range = ((dstref.offrange[0] == -maxobjsize - 1
+ && dstref.offrange[1] == maxobjsize)
+ || (srcref.offrange[0] == -maxobjsize - 1
+ && srcref.offrange[1] == maxobjsize));
+
+ if (sizrange[0] == sizrange[1] || sizrange[1] == 1)
+ {
+ if (ovlsiz[1] == 1)
+ {
+ if (open_range)
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[1] == 1
+ ? G_("%G%qD accessing %wu byte may overlap "
+ "%wu byte")
+ : G_("%G%qD accessing %wu bytes may overlap "
+ "%wu byte"),
+ call, func, sizrange[1], ovlsiz[1]);
+ else
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[1] == 1
+ ? G_("%G%qD accessing %wu byte at offsets %s "
+ "and %s may overlap %wu byte at offset %s")
+ : G_("%G%qD accessing %wu bytes at offsets %s "
+ "and %s may overlap %wu byte at offset %s"),
+ call, func, sizrange[1], offstr[0], offstr[1],
+ ovlsiz[1], offstr[2]);
+ return true;
+ }
+
+ if (open_range)
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[1] == 1
+ ? G_("%G%qD accessing %wu byte may overlap "
+ "up to %wu bytes")
+ : G_("%G%qD accessing %wu bytes may overlap "
+ "up to %wu bytes"),
+ call, func, sizrange[1], ovlsiz[1]);
+ else
+ warning_at (loc, OPT_Wrestrict,
+ sizrange[1] == 1
+ ? G_("%G%qD accessing %wu byte at offsets %s and "
+ "%s may overlap up to %wu bytes at offset %s")
+ : G_("%G%qD accessing %wu bytes at offsets %s and "
+ "%s may overlap up to %wu bytes at offset %s"),
+ call, func, sizrange[1], offstr[0], offstr[1],
+ ovlsiz[1], offstr[2]);
+ return true;
+ }
+
+ if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+ {
+ if (open_range)
+ warning_at (loc, OPT_Wrestrict,
+ ovlsiz[1] == 1
+ ? G_("%G%qD accessing between %wu and %wu bytes "
+ "may overlap %wu byte")
+ : G_("%G%qD accessing between %wu and %wu bytes "
+ "may overlap up to %wu bytes"),
+ call, func, sizrange[0], sizrange[1], ovlsiz[1]);
+ else
+ warning_at (loc, OPT_Wrestrict,
+ ovlsiz[1] == 1
+ ? G_("%G%qD accessing between %wu and %wu bytes "
+ "at offsets %s and %s may overlap %wu byte "
+ "at offset %s")
+ : G_("%G%qD accessing between %wu and %wu bytes "
+ "at offsets %s and %s may overlap up to %wu "
+ "bytes at offset %s"),
+ call, func, sizrange[0], sizrange[1],
+ offstr[0], offstr[1], ovlsiz[1], offstr[2]);
+ return true;
+ }
+
+ warning_at (loc, OPT_Wrestrict,
+ ovlsiz[1] == 1
+ ? G_("%G%qD accessing %wu or more bytes at offsets %s "
+ "and %s may overlap %wu byte at offset %s")
+ : G_("%G%qD accessing %wu or more bytes at offsets %s "
+ "and %s may overlap up to %wu bytes at offset %s"),
+ call, func, sizrange[0], offstr[0], offstr[1],
+ ovlsiz[1], offstr[2]);
+
+ return true;
+}
+
+/* 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
+ the function by incrementing the initial value by the size are
+ validated. Return true if the offsets are not valid and a diagnostic
+ has been issued. */
+
+static bool
+maybe_diag_offset_bounds (location_t loc, gcall *call, tree func, int strict,
+ tree expr, const builtin_memref &ref)
+{
+ 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)
+ return false;
+
+ if (EXPR_HAS_LOCATION (expr))
+ loc = EXPR_LOCATION (expr);
+
+ loc = expansion_point_location_if_in_system_header (loc);
+
+ tree type;
+
+ char rangestr[2][64];
+ if (ooboff[0] == ooboff[1]
+ || (ooboff[0] != ref.offrange[0]
+ && ooboff[0].to_shwi () >= ooboff[1].to_shwi ()))
+ sprintf (rangestr[0], "%lli", (long long) ooboff[0].to_shwi ());
+ else
+ sprintf (rangestr[0], "[%lli, %lli]",
+ (long long) ooboff[0].to_shwi (),
+ (long long) ooboff[1].to_shwi ());
+
+ if (oobref == error_mark_node)
+ {
+ if (ref.sizrange[0] == ref.sizrange[1])
+ sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+ else
+ sprintf (rangestr[1], "[%lli, %lli]",
+ (long long) ref.sizrange[0].to_shwi (),
+ (long long) ref.sizrange[1].to_shwi ());
+
+ if (DECL_P (ref.base)
+ && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+ {
+ if (warning_at (loc, OPT_Warray_bounds,
+ "%G%qD pointer overflow between offset %s "
+ "and size %s accessing array %qD with type %qT",
+ call, func, rangestr[0], rangestr[1], ref.base, type))
+ inform (DECL_SOURCE_LOCATION (ref.base),
+ "array %qD declared here", ref.base);
+ else
+ warning_at (loc, OPT_Warray_bounds,
+ "%G%qD pointer overflow between offset %s "
+ "and size %s",
+ call, func, rangestr[0], rangestr[1]);
+ }
+ else
+ warning_at (loc, OPT_Warray_bounds,
+ "%G%qD pointer overflow between offset %s "
+ "and size %s",
+ call, func, rangestr[0], rangestr[1]);
+ }
+ else if (oobref == ref.base)
+ {
+ const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+ /* True when the offset formed by an access to the reference
+ is out of bounds, rather than the initial offset wich is
+ in bounds. This implies access past the end. */
+ bool form = ooboff[0] != ref.offrange[0];
+
+ if (DECL_P (ref.base))
+ {
+ if ((ref.basesize < maxobjsize
+ && warning_at (loc, OPT_Warray_bounds,
+ form
+ ? G_("%G%qD forming offset %s is out of "
+ "the bounds [0, %wu] of object %qD with "
+ "type %qT")
+ : G_("%G%qD offset %s is out of the bounds "
+ "[0, %wu] of object %qD with type %qT"),
+ call, func, rangestr[0], ref.basesize.to_uhwi (),
+ ref.base, TREE_TYPE (ref.base)))
+ || warning_at (loc, OPT_Warray_bounds,
+ form
+ ? G_("%G%qD forming offset %s is out of "
+ "the bounds of object %qD with type %qT")
+ : G_("%G%qD offset %s is out of the bounds "
+ "of object %qD with type %qT"),
+ call, func, rangestr[0],
+ ref.base, TREE_TYPE (ref.base)))
+ inform (DECL_SOURCE_LOCATION (ref.base),
+ "%qD declared here", ref.base);
+ }
+ else if (ref.basesize < maxobjsize)
+ warning_at (loc, OPT_Warray_bounds,
+ form
+ ? G_("%G%qD forming offset %s is out of the bounds "
+ "[0, %wu]")
+ : G_("%G%qD offset %s is out of the bounds [0, %wu]"),
+ call, func, rangestr[0], ref.basesize.to_uhwi ());
+ else
+ warning_at (loc, OPT_Warray_bounds,
+ form
+ ? G_("%G%qD forming offset %s is out of bounds")
+ : G_("%G%qD offset %s is out of bounds"),
+ call, func, rangestr[0]);
+ }
+ else if (TREE_CODE (ref.ref) == MEM_REF)
+ {
+ tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+ if (POINTER_TYPE_P (type))
+ type = TREE_TYPE (type);
+ type = TYPE_MAIN_VARIANT (type);
+
+ warning_at (loc, OPT_Warray_bounds,
+ "%G%qD offset %s from the object at %qE is out "
+ "of the bounds of %qT",
+ call, func, rangestr[0], ref.base, type);
+ }
+ else
+ {
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+
+ warning_at (loc, OPT_Warray_bounds,
+ "%G%qD offset %s from the object at %qE is out "
+ "of the bounds of referenced subobject %qD with type %qT "
+ "at offset %wu",
+ call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
+ type, ref.refoff.to_uhwi ());
+ }
+
+ return true;
+}
+
+/* Check a CALL statement for restrict-violations and issue warnings
+ if/when appropriate. */
+
+void
+wrestrict_dom_walker::check_call (gcall *call)
+{
+ /* Avoid checking the call if it has already been diagnosed for
+ some reason. */
+ if (gimple_no_warning_p (call))
+ return;
+
+ tree func = gimple_call_fndecl (call);
+ if (!func || DECL_BUILT_IN_CLASS (func) != BUILT_IN_NORMAL)
+ return;
+
+ bool with_bounds = gimple_call_with_bounds_p (call);
+
+ /* Argument number to extract from the call (depends on the built-in
+ and its kind). */
+ unsigned dst_idx = -1;
+ unsigned src_idx = -1;
+ unsigned bnd_idx = -1;
+
+ /* Is this CALL to a string function (as opposed to one to a raw
+ memory function). */
+ bool strfun = true;
+
+ switch (DECL_FUNCTION_CODE (func))
+ {
+ case BUILT_IN_MEMCPY:
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMCPY_CHKP:
+ case BUILT_IN_MEMCPY_CHK_CHKP:
+ case BUILT_IN_MEMPCPY:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMPCPY_CHKP:
+ case BUILT_IN_MEMPCPY_CHK_CHKP:
+ case BUILT_IN_MEMMOVE:
+ case BUILT_IN_MEMMOVE_CHK:
+ case BUILT_IN_MEMMOVE_CHKP:
+ case BUILT_IN_MEMMOVE_CHK_CHKP:
+ strfun = false;
+ /* Fall through. */
+
+ case BUILT_IN_STPNCPY:
+ case BUILT_IN_STPNCPY_CHK:
+ case BUILT_IN_STRNCAT:
+ case BUILT_IN_STRNCAT_CHK:
+ case BUILT_IN_STRNCPY:
+ case BUILT_IN_STRNCPY_CHK:
+ dst_idx = 0;
+ src_idx = 1 + with_bounds;
+ bnd_idx = 2 + 2 * with_bounds;
+ break;
+
+ case BUILT_IN_STPCPY:
+ case BUILT_IN_STPCPY_CHK:
+ case BUILT_IN_STPCPY_CHKP:
+ case BUILT_IN_STPCPY_CHK_CHKP:
+ case BUILT_IN_STRCPY:
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STRCPY_CHKP:
+ case BUILT_IN_STRCPY_CHK_CHKP:
+ case BUILT_IN_STRCAT:
+ case BUILT_IN_STRCAT_CHK:
+ case BUILT_IN_STRCAT_CHKP:
+ case BUILT_IN_STRCAT_CHK_CHKP:
+ dst_idx = 0;
+ src_idx = 1 + with_bounds;
+ break;
+
+ default:
+ /* Handle other string functions here whose access may need
+ to be validated for in-bounds offsets and non-overlapping
+ copies. (Not all _chkp functions have BUILT_IN_XXX_CHKP
+ macros so they need to be handled here.) */
+ return;
+ }
+
+ unsigned nargs = gimple_call_num_args (call);
+
+ tree dst = dst_idx < nargs ? gimple_call_arg (call, dst_idx) : NULL_TREE;
+ tree src = src_idx < nargs ? gimple_call_arg (call, src_idx) : NULL_TREE;
+ tree dstwr = bnd_idx < nargs ? gimple_call_arg (call, bnd_idx) : NULL_TREE;
+
+ /* For string functions with an unspecified or unknown bound,
+ assume the size of the access is one. */
+ if (!dstwr && strfun)
+ dstwr = size_one_node;
+
+ if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+ return;
+
+ /* Avoid diagnosing the call again. */
+ gimple_set_no_warning (call, true);
+}
+
+} /* anonymous namespace */
+
+/* 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. */
+
+bool
+check_bounds_or_overlap (gcall *call, tree dst, tree src, tree dstsize,
+ tree srcsize, bool bounds_only /* = false */)
+{
+ location_t loc = gimple_location (call);
+
+ if (tree block = gimple_block (call))
+ if (location_t *pbloc = block_nonartificial_location (block))
+ loc = *pbloc;
+
+ loc = expansion_point_location_if_in_system_header (loc);
+
+ tree func = gimple_call_fndecl (call);
+
+ builtin_memref dstref (dst, dstsize);
+ builtin_memref srcref (src, srcsize);
+
+ builtin_access acs (call, dstref, srcref);
+
+ /* Set STRICT to the value of the -Warray-bounds=N argument for
+ string functions or when N > 1. */
+ int strict = (acs.strict () || warn_array_bounds > 1 ? warn_array_bounds : 0);
+
+ /* 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))
+ {
+ gimple_set_no_warning (call, true);
+ return false;
+ }
+
+ bool check_overlap
+ = (warn_restrict
+ && (bounds_only
+ || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+ && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
+
+ if (!check_overlap)
+ return true;
+
+ if (operand_equal_p (dst, src, 0))
+ {
+ warning_at (loc, OPT_Wrestrict,
+ "%G%qD source argument is the same as destination",
+ call, func);
+ gimple_set_no_warning (call, true);
+ return false;
+ }
+
+ /* Return false when overlap has been detected. */
+ if (maybe_diag_overlap (loc, call, acs))
+ {
+ gimple_set_no_warning (call, true);
+ return false;
+ }
+
+ return true;
+}
+
+gimple_opt_pass *
+make_pass_warn_restrict (gcc::context *ctxt)
+{
+ return new pass_wrestrict (ctxt);
+}
--- /dev/null
+/* Warn on violations of the restrict qualifier.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Contributed by Martin Sebor <msebor@redhat.com>.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef GIMPLE_SSA_WARN_RESTRICT_H
+
+extern bool check_bounds_or_overlap (gcall *, tree, tree, tree, tree,
+ bool = false);
+
+#endif /* GIMPLE_SSA_WARN_RESTRICT_H */
run the full propagators, run a specialized pass which
only examines PHIs to discover const/copy propagation
opportunities. */
+ NEXT_PASS (pass_warn_restrict);
NEXT_PASS (pass_phi_only_cprop);
NEXT_PASS (pass_dse);
NEXT_PASS (pass_cd_dce);
+2017-12-16 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/78918
+ * c-c++-common/Warray-bounds.c: New test.
+ * c-c++-common/Warray-bounds-2.c: New test.
+ * c-c++-common/Warray-bounds-3.c: New test.
+ * c-c++-common/Warray-bounds-4.c: New test.
+ * c-c++-common/Warray-bounds-5.c: New test.
+ * c-c++-common/Wrestrict-2.c: New test.
+ * c-c++-common/Wrestrict.c: New test.
+ * c-c++-common/Wrestrict.s: New test.
+ * c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
+ * c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
+ * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
+ * g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
+ * gcc.dg/range.h: New header.
+ * gcc.dg/memcpy-6.c: New test.
+ * gcc.dg/pr69172.c: Adjust.
+ * gcc.dg/pr79223.c: Same.
+ * gcc.dg/pr81345.c: Adjust.
+ * gcc.dg/Wobjsize-1.c: Same.
+ * gcc.dg/Wrestrict-2.c: New test.
+ * gcc.dg/Wrestrict.c: New test.
+ * gcc.dg/Wsizeof-pointer-memaccess1.c: Adjust.
+ * gcc.dg/builtin-stpncpy.c: Same.
+ * gcc.dg/builtin-stringop-chk-1.c: Same.
+ * gcc.target/i386/chkp-stropt-17.c: New test.
+ * gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
+
2017-12-16 Martin Sebor <msebor@redhat.com>
PR tree-optimization/83239
--- /dev/null
+/* Test to exercise that -Warray-bounds warnings for memory and sring
+ functions are issued even when they are declared in system headers
+ (i.e., not just when they are explicitly declared in the source
+ file.)
+ Also verify that the warnings are issued even for calls where the
+ source of the excessive array bound is in a different function than
+ the call.
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
+
+#include <stddef.h>
+#include <string.h>
+
+#define MAX (__SIZE_MAX__ / 2)
+
+void sink (void*);
+
+struct __attribute__ ((packed)) Array
+{
+ char a13[13];
+ char a15[15];
+ char a17[17];
+};
+
+/* Exercise memcpy out-of-bounds offsets with an array of known size. */
+
+void wrap_memcpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ memcpy (d, s + i, n); /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_src_xsize (char *d, size_t n)
+{
+ struct Array ar;
+ sink (&ar);
+ wrap_memcpy_src_xsize (d, ar.a13, 46, n);
+ sink (&ar);
+}
+
+/* Exercise memcpy out-of-bounds offsets with an array of unknown size. */
+
+void wrap_memcpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ memcpy (d, s + i, n); /* { dg-warning "pointer overflow between offset \[0-9\]+ and size 3" "memcpy" } */
+}
+
+void call_memcpy_src_diff_max (char *d, const char *s, size_t n)
+{
+ wrap_memcpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_memcpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ memcpy (d + i, s, n); /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar1. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_xsize (const char *s, size_t n)
+{
+ struct Array ar1; /* { dg-message ".ar1. declared here" } */
+ sink (&ar1);
+ wrap_memcpy_dst_xsize (ar1.a15, s, 34, n);
+ sink (&ar1);
+}
+
+void wrap_memcpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ memcpy (d + i, s, n); /* { dg-warning "offset -?\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar2. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_diff_max (const char *s, size_t n)
+{
+ struct Array ar2; /* { dg-message ".ar2. declared here" } */
+ sink (&ar2);
+ wrap_memcpy_dst_diff_max (ar2.a15, s, MAX, n);
+ sink (&ar2);
+}
+
+
+void wrap_strcat_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+ strcat (d, s + i); /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar3. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_src_xsize (char *d)
+{
+ struct Array ar3; /* { dg-message ".ar3. declared here" } */
+ sink (&ar3);
+ wrap_strcat_src_xsize (d, ar3.a15, 15 + 17 + 1);
+ sink (&ar3);
+}
+
+void wrap_strcat_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+ strcat (d + i, s); /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar4. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_dst_xsize (const char *s)
+{
+ struct Array ar4; /* { dg-message ".ar4. declared here" } */
+ sink (&ar4);
+ wrap_strcat_dst_xsize (ar4.a15, s, 15 + 17 + 2);
+ sink (&ar4);
+}
+
+
+void wrap_strcpy_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+ strcpy (d, s + i); /* { dg-warning "offset 48 is out of the bounds \\\[0, 45] of object .ar5. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_src_xsize (char *d)
+{
+ struct Array ar5; /* { dg-message ".ar5. declared here" } */
+ sink (&ar5);
+ wrap_strcpy_src_xsize (d, ar5.a15, 15 + 17 + 3);
+ sink (&ar5);
+}
+
+void wrap_strcpy_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+ strcpy (d + i, s); /* { dg-warning "offset 49 is out of the bounds \\\[0, 45] of object .ar6. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_dst_xsize (const char *s)
+{
+ struct Array ar6; /* { dg-message ".ar6. declared here" } */
+ sink (&ar6);
+ wrap_strcpy_dst_xsize (ar6.a15, s, 15 + 17 + 4);
+ sink (&ar6);
+}
+
+
+/* Exercise strncpy out-of-bounds offsets with an array of known size. */
+
+void wrap_strncpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ strncpy (d, s + i, n); /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar7. with type '(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_src_xsize (char *d, size_t n)
+{
+ struct Array ar7; /* { dg-message ".ar7. declared here" } */
+ sink (&ar7);
+ wrap_strncpy_src_xsize (d, ar7.a17, 17 + 1, n);
+ sink (&ar7);
+}
+
+/* Exercise strncpy out-of-bounds offsets with an array of unknown size. */
+
+void wrap_strncpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ /* Unlike in the similar call to memcpy(), there is no pointer
+ overflow here because the size N is not added to the source
+ offset. */
+ strncpy (d, s + i, n);
+}
+
+void call_strncpy_src_diff_max (char *d, const char *s, size_t n)
+{
+ wrap_strncpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_strncpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ strncpy (d + i, s, n); /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar8. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_xsize (const char *s, size_t n)
+{
+ struct Array ar8; /* { dg-message ".ar8. declared here" } */
+ sink (&ar8);
+ wrap_strncpy_dst_xsize (ar8.a17, s, 17 + 2, n);
+ sink (&ar8);
+}
+
+void wrap_strncpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+ strncpy (d + i, s, n); /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar9. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_diff_max (const char *s, size_t n)
+{
+ struct Array ar9; /* { dg-message ".ar9. declared here" } */
+ sink (&ar9);
+ wrap_strncpy_dst_diff_max (ar9.a17, s, MAX, n);
+ sink (&ar9);
+}
+
+void wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i,
+ size_t n)
+{
+ strncpy (d + i, s, n); /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
+}
+
+void call_strncpy_dstarray_diff_neg (const char *s, size_t n)
+{
+ struct Array ar10[2]; /* { dg-message ".ar10. declared here" } */
+ sink (&ar10);
+
+ int off = (char*)ar10[1].a17 - (char*)ar10 + 1;
+ wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n);
+
+ sink (&ar10);
+}
--- /dev/null
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+ in calls to built-in functions.
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+#if __cplusplus
+# define restrict __restrict
+extern "C" {
+#endif
+
+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
+
+void sink (void*, ...);
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define UNIQUE_NAME(x) CONCAT(x, __LINE__)
+
+#define T(type, N, dst, src, n) do { \
+ extern type UNIQUE_NAME (a)[N]; \
+ type *a = UNIQUE_NAME (a); \
+ type *pd = (dst); \
+ const type *ps = (src); \
+ FUNC (pd, ps, n); \
+ sink (a, pd, ps); \
+ } while (0)
+
+
+void test_memcpy_bounds (char *d, const char *s, size_t n)
+{
+#define FUNC memcpy
+
+ /* Verify that invalid offsets into an array of known size are
+ detected. */
+
+ T (char, 1, a + SR (DIFF_MIN, -1), s, n); /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]* with type .char ?\\\[1]" } */
+ T (char, 1, a + SR (-2, -1), s, n); /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+ T (char, 1, a + SR (-2, 0), s, n);
+
+ T (char, 1, a + UR (0, 1), s, n);
+ T (char, 1, a + UR (0, 2), s, n);
+ T (char, 1, a + UR (1, 2), s, n);
+ T (char, 1, a + UR (2, 3), s, n); /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object " } */
+ T (char, 1, a + UR (2, DIFF_MAX), s, n); /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object " "memcpy" } */
+
+ /* Offsets in excess of DIFF_MAX are treated as negative even if
+ they appear as large positive in the source. It would be nice
+ if they retained their type but unfortunately that's not how
+ it works so be prepared for both in case it even gets fixed. */
+ T (char, 1, a + UR (3, SIZE_MAX - 1), s, n); /* { dg-warning "offset \\\[3, -2] is out of the bounds \\\[0, 1] of object" "memcpy" } */
+
+ /* Verify that invalid offsets into an array of unknown size are
+ detected. */
+ extern char arr[];
+ T (char, 1, arr + SR (DIFF_MIN, 0), s, n);
+ T (char, 1, arr + SR (DIFF_MIN + 1, -1), s, n); /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object " "memcpy" } */
+ T (char, 1, arr + SR (DIFF_MIN, 1), s, n);
+ T (char, 1, arr + SR (DIFF_MIN, DIFF_MAX), s, n);
+ T (char, 1, arr + SR ( -2, -1), s, n); /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object " "memcpy" } */
+ T (char, 1, arr + SR ( -1, 0), s, n);
+ T (char, 1, arr + SR ( -1, 1), s, n);
+ T (char, 1, arr + SR ( -1, DIFF_MAX - 1), s, n);
+ T (char, 1, arr + SR ( 0, 1), s, n);
+ T (char, 1, arr + SR ( 0, DIFF_MAX - 1), s, n);
+ T (char, 1, arr + SR ( 1, 2), s, n);
+ T (char, 1, arr + SR ( 1, DIFF_MAX - 1), s, n);
+
+ /* Verify that all offsets via a pointer to an uknown object are
+ accepted. */
+
+ /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+ the pointer to which the offset is applied can be at a positive
+ offset from the beginning of an object. */
+ T (char, 1, d + SR (DIFF_MIN, 0), s, n);
+ T (char, 1, d + SR (DIFF_MIN, -1), s, n);
+ T (char, 1, d + SR (DIFF_MIN, 1), s, n);
+ T (char, 1, d + SR (DIFF_MIN, DIFF_MAX - 1), s, n);
+ T (char, 1, d + SR ( -2, -1), s, n);
+ T (char, 1, d + SR ( -1, 0), s, n);
+ T (char, 1, d + SR ( -1, 1), s, n);
+ T (char, 1, d + SR ( -1, DIFF_MAX - 1), s, n);
+ T (char, 1, d + SR ( 0, 1), s, n);
+ T (char, 1, d + SR ( 0, DIFF_MAX - 1), s, n);
+ T (char, 1, d + SR ( 1, 2), s, n);
+ T (char, 1, d + SR ( 1, DIFF_MAX - 1), s, n);
+}
+
+/* Verify offsets in an anti-range. */
+
+void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n)
+{
+ T (char, 9, a, a + SAR (-2, -1), 3);
+ T (char, 9, a, a + SAR (-1, 1), 3);
+ T (char, 9, a, a + SAR ( 0, 1), 3);
+ T (char, 9, a, a + SAR ( 0, 2), 3);
+ T (char, 9, a, a + SAR ( 0, 3), 3);
+ T (char, 9, a, a + SAR ( 0, 4), 3);
+ T (char, 9, a, a + SAR ( 0, 5), 3);
+ /* The initial source range is valid but the final range after the access
+ has complete cannot be. The value mentioned in the warning is the final
+ offset, i.e., 7 + 3. Including the whole final range because would be
+ confusing (the upper bound would either be negative or a very large
+ positive number) so only the lower bound is included. */
+ T (char, 9, a, a + SAR ( 0, 6), 3); /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+ /* This fails because the offset isn't represented as an SSA_NAME
+ but rather as a GIMPLE_PHI (offset, 0). With some effort it is
+ possible to extract the range from the PHI but it's not implemented
+ (yet). */
+ T (char, 9, a, a + SAR ( 1, 6), 3); /* { dg-warning "forming offset \\\[9, 0] is out of the bounds \\\[0, 9] of object " "memcpy" { xfail *-*-* } } */
+
+ T (char, 9, a, a + SAR ( 2, 6), 3); /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+ T (char, 9, a, a + SAR ( 3, 6), 3); /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+ T (char, 9, a, a + SAR (-1, 7), 3); /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+ T (char, 9, a, a + SAR (-2, 8), 3); /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+ T (char, 9, a, a + SAR (-3, 7), 5); /* { dg-warning "forming offset \\\[10, 13] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+ T (char, 9, a + SAR (-2, -1), a, 3);
+ T (char, 9, a + SAR (-1, 1), a, 3);
+ T (char, 9, a + SAR ( 0, 1), a, 3);
+ T (char, 9, a + SAR ( 0, 2), a, 3);
+ T (char, 9, a + SAR ( 0, 3), a, 3);
+ T (char, 9, a + SAR ( 0, 6), a, 3); /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+ T (char, 9, a + SAR (-1, 7), a, 3); /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+ T (char, 9, a + SAR (-2, 8), a, 3); /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+ ptrdiff_t i = SAR (DIFF_MIN + 1, DIFF_MAX - 4);
+ T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 1), 3);
+ T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 3), 5);
+}
+
+/* Verify that pointer overflow in the computation done by memcpy
+ (i.e., offset + size) is detected and diagnosed. */
+
+void test_memcpy_overflow (char *d, const char *s, size_t n)
+{
+ extern char arr[];
+
+ /* Verify that offset overflow involving an array of unknown size
+ but known access size is detected. This works except with small
+ sizes that are powers of 2 due to bug . */
+ T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 1);
+ T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 2); /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 2 accessing array " "bug " { xfail *-*-* } } */
+ T (char, 1, arr + SR (DIFF_MAX - 2, DIFF_MAX), s, 3); /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 3 accessing array " "memcpy" } */
+ T (char, 1, arr + SR (DIFF_MAX - 4, DIFF_MAX), s, 5); /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 5 accessing array " "memcpy" } */
+}
+
+void test_memcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(mem, dst, src, n) \
+ do { \
+ struct MA { char a5[5]; int i; } ma; \
+ sink (&ma); /* Initialize arrays. */ \
+ memcpy (dst, src, n); \
+ sink (&ma); \
+ } while (0)
+
+ ptrdiff_t i = SR (1, 2);
+
+ TM (ma.a5, ma.a5 + i, ma.a5, 1);
+ TM (ma.a5, ma.a5 + i, ma.a5, 3);
+ TM (ma.a5, ma.a5 + i, ma.a5, 5);
+ TM (ma.a5, ma.a5 + i, ma.a5, 7); /* diagnosed with -Warray-bounds=2 */
+}
+
+void test_memmove_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC memmove
+
+ T (char, 1, a + SR (DIFF_MIN + 1, -1), s, n); /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]+ with type .char ?\\\[1]" } */
+ T (char, 1, a + SR (-2, -1), s, n); /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+ T (char, 1, a + SR (-2, 0), s, n);
+
+ const int *pi = (const int*)s;
+ T (int, 2, a + SR (-1, 1), pi, n);
+ T (int, 2, a + SR (-1, 2), pi, n);
+ T (int, 2, a + SR ( 0, 2), pi, n);
+ T (int, 2, a + SR ( 0, 3), pi, n);
+ T (int, 2, a + SR ( 1, 3), pi, n);
+ T (int, 2, a + SR ( 2, 3), pi, n);
+
+ T (int32_t, 2, a + SR ( 3, 4), pi, n); /* { dg-warning "offset \\\[12, 16] is out of the bounds \\\[0, 8] of object .\[^\n\r]+. with type .int32_t ?\\\[2]." } */
+}
+
+
+void test_mempcpy_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC mempcpy
+
+ /* Verify that invalid offsets into an array of known size are
+ detected. */
+
+ T (char, 1, a + SR (DIFF_MIN, -1), s, n); /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds" "mempcpy" } */
+T (char, 1, a + SR (-2, -1), s, n); /* { dg-warning "offset \\\[-2, -1] is out of the bounds" "mempcpy" } */
+ T (char, 1, a + SR (-2, 0), s, n);
+
+ T (char, 1, a + UR (0, 1), s, n);
+ T (char, 1, a + UR (0, 2), s, n);
+ T (char, 1, a + UR (1, 2), s, n);
+ T (char, 1, a + UR (2, 3), s, n); /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object " "mempcpy" } */
+ T (char, 1, a + UR (2, DIFF_MAX), s, n); /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object" "mempcpy" } */
+
+ /* Offsets in excess of DIFF_MAX are treated as negative even if
+ they appear as large positive in the source. It would be nice
+ if they retained their type but unfortunately that's not how
+ it works so be prepared for both in case it ever gets fixed. */
+ T (char, 1, a + UR (3, SIZE_MAX), s, n); /* { dg-warning "offset \\\[3, -1] is out of the bounds \\\[0, 1] of object " "mempcpy" } */
+
+ /* Verify that invalid offsets into an array of unknown size are
+ detected. */
+ extern char arr[];
+ T (char, 1, arr + SR (DIFF_MIN, 0), s, n);
+ T (char, 1, arr + SR (DIFF_MIN, -1), s, n); /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object" "mempcpy" } */
+ T (char, 1, arr + SR (DIFF_MIN, 1), s, n);
+ T (char, 1, arr + SR (DIFF_MIN, DIFF_MAX), s, n);
+ T (char, 1, arr + SR ( -2, -1), s, n); /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object" "mempcpy" } */
+ T (char, 1, arr + SR ( -1, 0), s, n);
+ T (char, 1, arr + SR ( -1, 1), s, n);
+ T (char, 1, arr + SR ( -1, DIFF_MAX), s, n);
+ T (char, 1, arr + SR ( 0, 1), s, n);
+ T (char, 1, arr + SR ( 0, DIFF_MAX), s, n);
+ T (char, 1, arr + SR ( 1, 2), s, n);
+ T (char, 1, arr + SR ( 1, DIFF_MAX), s, n);
+
+ /* Verify that all offsets via a pointer to an uknown object are
+ accepted. */
+
+ /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+ the pointer to which the offset is applied can be at a positive
+ offset from the beginning of an object. */
+ T (char, 1, d + SR (DIFF_MIN, 0), s, n);
+ T (char, 1, d + SR (DIFF_MIN, -1), s, n);
+ T (char, 1, d + SR (DIFF_MIN, 1), s, n);
+ T (char, 1, d + SR (DIFF_MIN, DIFF_MAX), s, n);
+ T (char, 1, d + SR ( -2, -1), s, n);
+ T (char, 1, d + SR ( -1, 0), s, n);
+ T (char, 1, d + SR ( -1, 1), s, n);
+ T (char, 1, d + SR ( -1, DIFF_MAX), s, n);
+ T (char, 1, d + SR ( 0, 1), s, n);
+ T (char, 1, d + SR ( 0, DIFF_MAX), s, n);
+ T (char, 1, d + SR ( 1, 2), s, n);
+ T (char, 1, d + SR ( 1, DIFF_MAX), s, n);
+}
+
+#define TI(type, N, init, dst, src) do { \
+ type UNIQUE_NAME (a)[N] = init; \
+ type *a = UNIQUE_NAME (a); \
+ type *pd = (dst); \
+ const type *ps = (src); \
+ FUNC (pd, ps); \
+ sink (a, pd, ps, s); \
+ } while (0)
+
+void test_strcpy_bounds (char *d, const char *s)
+{
+#undef FUNC
+#define FUNC strcpy
+
+ ptrdiff_t i;
+
+ TI (char, 1, "", a, a + SR (DIFF_MIN, 0));
+ TI (char, 1, "", a, a + SR (-1, 0));
+ TI (char, 1, "", a, a + SR (-1, 1));
+ TI (char, 1, "", a, a + SR (0, 1));
+ TI (char, 1, "", a, a + SR (0, DIFF_MAX - 1));
+ TI (char, 2, "0", a, a + SR (0, DIFF_MAX - 1));
+ TI (char, 2, "0", a, a + SR (1, DIFF_MAX - 1));
+ /* The following needs a warning for reading past the end. */
+ TI (char, 2, "0", a, a + SR (2, DIFF_MAX - 1));
+ TI (char, 2, "0", a, a + SR (3, DIFF_MAX - 1)); /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]." "strcpy" } */
+
+ TI (char, 3, "01", a, a + SR (0, DIFF_MAX - 1));
+ TI (char, 3, "01", a, a + SR (1, DIFF_MAX - 1));
+ TI (char, 3, "01", a, a + SR (2, DIFF_MAX - 1));
+ /* The following needs a warning for reading past the end. */
+ TI (char, 3, "01", a, a + SR (3, DIFF_MAX - 1));
+ TI (char, 3, "01", a, a + SR (4, DIFF_MAX - 1)); /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]." "strcpy" } */
+
+ TI (char, 4, "012", a, a + SR (DIFF_MAX - 2, DIFF_MAX - 1)); /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]." "strcpy" } */
+
+
+ TI (char, 1, "", a + SR (DIFF_MIN, 0), s);
+ TI (char, 1, "", a + SR (-1, 0), s);
+ TI (char, 1, "", a + SR (-1, 1), s);
+ TI (char, 1, "", a + SR (0, 1), s);
+ TI (char, 1, "", a + SR (0, DIFF_MAX - 1), s);
+ TI (char, 2, "", a + SR (0, DIFF_MAX - 1), s);
+ TI (char, 2, "", a + SR (1, DIFF_MAX - 1), s);
+ /* The following is diagnosed not because the initial source offset
+ it out of bounds (it isn't) but because the final source offset
+ after the access has completed, is. It would be clearer if
+ the warning mentioned the final offset. */
+ TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s); /* { dg-warning "forming offset 3 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]." "strcpy" } */
+ TI (char, 2, "", a + SR (3, DIFF_MAX - 1), s); /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]." "strcpy" } */
+
+ TI (char, 3, "", a + SR (0, DIFF_MAX - 1), s);
+ TI (char, 3, "", a + SR (1, DIFF_MAX - 1), s);
+ TI (char, 3, "", a + SR (2, DIFF_MAX - 1), s);
+ TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s); /* { dg-warning "forming offset 4 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]." "strcpy" } */
+ TI (char, 3, "", a + SR (4, DIFF_MAX - 1), s); /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]." "strcpy" } */
+
+ TI (char, 4, "", a + SR (DIFF_MAX - 2, DIFF_MAX - 1), s); /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]." "strcpy" } */
+}
+
+struct MA
+{
+ int i;
+ char a5[5];
+ char a11[11];
+};
+
+struct MA2
+{
+ struct MA ma3[3];
+ struct MA ma5[5];
+ char ax[];
+};
+
+struct MA3
+{
+ struct MA2 ma5[3];
+ struct MA2 ma7[7];
+};
+
+void test_strcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(mem, init, dst, src) \
+ do { \
+ struct MA ma; \
+ strcpy (ma.mem, init); \
+ strcpy (dst, src); \
+ sink (&ma); \
+ } while (0)
+
+ ptrdiff_t i = SR (1, 2);
+
+ TM (a5, "0", ma.a5 + i, ma.a5);
+ TM (a5, "01", ma.a5 + i, ma.a5);
+ TM (a5, "012", ma.a5 + i, ma.a5);
+ TM (a5, "0123", ma.a5 + i, ma.a5); /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char\\\[5]. at offset 4" "strcpy" { xfail *-*-* } } */
+
+ TM (a11, "0", ma.a5, ma.a11);
+ TM (a11, "01", ma.a5, ma.a11);
+ TM (a11, "012", ma.a5, ma.a11);
+ TM (a11, "0123", ma.a5, ma.a11);
+ TM (a11, "01234", ma.a5, ma.a11); /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+ TM (a11, "012345", ma.a5, ma.a11); /* { dg-warning "offset \\\[10, 11] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+ TM (a11, "0123456", ma.a5, ma.a11); /* { dg-warning "offset \\\[10, 12] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+
+ TM (a11, "0123456", ma.a11 + i, "789abcd");
+}
+
+void test_strcpy_bounds_memarray_var (struct MA *pma,
+ struct MA2 *pma2,
+ struct MA3 *pma3,
+ const char *s, size_t n)
+{
+#undef TM
+#define TM(dst, src) do { \
+ strcpy (dst, src); \
+ sink (dst, src); \
+ } while (0)
+
+ TM (pma->a5, s);
+ TM (pma->a5 + 0, s);
+ TM (pma->a5 + 1, s);
+ TM (pma->a5 + 4, s);
+
+ /* The following forms a pointer during the call that's outside
+ the bounds of the array it was derived from (pma->a5) so
+ it should be diagnosed but the representation of the pointer
+ addition doesn't contain information to distinguish it from
+ the valid pma->a11 + 1 so this is an XFAIL. */
+ TM (pma->a5 + 5, s); /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+ /* The following also forms an out-of-bounds pointer but similar
+ to the above, there is no reliable way to distinguish it from
+ (char*)&pma[1].i + 1 so this too is not diagnosed. */
+ TM (pma->a5 + sizeof *pma + 1, s); /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+ TM (pma->a5 - 1, s); /* { dg-warning "offset -1 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+ TM (pma[1].a5, s);
+ TM (pma[2].a5 + 0, s);
+ TM (pma[3].a5 + 1, s);
+ TM (pma[4].a5 + 4, s);
+
+
+ extern struct MA3 ma3[3];
+ TM (ma3[0].ma5[0].ma3[0].a5 + 6, s);
+}
--- /dev/null
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+ in calls to built-in functions.
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds=2 -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+#if __cplusplus
+# define restrict __restrict
+extern "C" {
+#endif
+
+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
+
+struct MA { char a5[5], a7[7]; };
+
+void sink (void*, ...);
+
+void test_memcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(mem, dst, src, n) \
+ do { \
+ struct MA ma; \
+ sink (&ma); /* Initialize arrays. */ \
+ memcpy (dst, src, n); \
+ sink (&ma); \
+ } while (0)
+
+ ptrdiff_t j = SR (1, 2);
+
+ TM (ma.a5, ma.a5 + j, ma.a5, 1);
+ TM (ma.a5, ma.a5 + j, ma.a5, 3);
+ TM (ma.a5, ma.a5 + j, ma.a5, 5);
+ TM (ma.a5, ma.a5 + j, ma.a5, 7); /* { dg-warning "offset \\\[6, 8] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
+ TM (ma.a5, ma.a5 + j, ma.a5, 9); /* { dg-warning "offset \\\[6, 10] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
+}
+
+void test_strcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(a5init, a7init, dst, src) \
+ do { \
+ struct MA ma = { a5init, a7init }; \
+ strcpy (dst, src); \
+ sink (&ma); \
+ } while (0)
+
+ ptrdiff_t i = SR (1, 2);
+
+ TM ("0", "", ma.a5 + i, ma.a5);
+ TM ("01", "", ma.a5 + i, ma.a5);
+ TM ("012", "", ma.a5 + i, ma.a5);
+ TM ("0123", "", ma.a5 + i, ma.a5); /* { dg-warning "offset 6 from the object at .ma. is out of the bounds of referenced subobject .a5. with type .char\\\[5]. at offset 0" "strcpy" { xfail *-*-* } } */
+ TM ("", "012345", ma.a7 + i, ma.a7); /* { dg-warning "offset 13 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" } */
+}
--- /dev/null
+/* Exercise that -Warray-bounds is handled correctly for subobjects.
+ Test case derived from the halt_fast_timekeeper function in Linux
+ kernel/time/timekeeping.c.
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds=2 -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
+
+struct A
+{
+ int i;
+ void *p;
+ int j;
+};
+
+struct B
+{
+ struct A a;
+
+ int i;
+};
+
+void sink (void*);
+
+static void halt_fast_timekeeper (struct B *b)
+{
+ static struct A a;
+
+ struct A *pa = &b->a;
+
+ __builtin_memcpy (&a, pa, sizeof *pa); /* { dg-bogus "\\\[-Warray-bounds" } */
+ sink (&a);
+}
+
+struct C { int i; struct B b; } c;
+
+void timekeeping_suspend (void)
+{
+ struct B *p = &c.b;
+
+ halt_fast_timekeeper (p);
+}
{ dg-require-effective-target alloca }
{ dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" } */
-#define SIZE_MAX __SIZE_MAX__
-#define DIFF_MAX __PTRDIFF_MAX__
-#define DIFF_MIN (-DIFF_MAX - 1)
+#include "../gcc.dg/range.h"
#define offsetof(T, m) __builtin_offsetof (T, m)
-typedef __PTRDIFF_TYPE__ ssize_t;
-typedef __SIZE_TYPE__ size_t;
-
-extern ssize_t signed_value (void)
-{
- extern volatile ssize_t signed_value_source;
- return signed_value_source;
-}
-
-extern size_t unsigned_value (void)
-{
- extern volatile size_t unsigned_value_source;
- return unsigned_value_source;
-}
-
-ssize_t signed_range (ssize_t min, ssize_t max)
-{
- ssize_t val = signed_value ();
- return val < min || max < val ? min : val;
-}
-
typedef struct AX { int n; char ax[]; } AX;
typedef struct A1 { int i; char a1[1]; } A1;
T (ax_7[DIFF_MAX / 2][0]); /* { dg-warning "array subscript \[0-9\]+ is above array bounds" } */
T (ax_7[SIZE_MAX][0]); /* { dg-warning "array subscript \[0-9\]+ is above array bounds" } */
- ssize_t i = R (DIFF_MIN, -1);
+ ptrdiff_t i = R (DIFF_MIN, -1);
T (ax_7[i][0]); /* { dg-warning "array subscript -1 is below array bounds" } */
T (ax_7[R (DIFF_MIN, -1)][0]); /* { dg-warning "array subscript -1 is below array bounds" } */
--- /dev/null
+/* PR 35503 - Warn about restricted pointers
+ Test to exercise that -Wrestrict warnings are issued for memory and
+ sring functions when they are declared in system headers (i.e., not
+ just when they are explicitly declared in the source file.)
+ Also verify that the warnings are issued even for calls where the
+ source of the aliasing violation is in a different function than
+ the restricted call.
+ { dg-do compile }
+ { dg-options "-O2 -Wrestrict" } */
+
+#include <string.h>
+
+void wrap_memcpy (void *d, const void *s, size_t n)
+{
+ memcpy (d, s, n); /* { dg-warning "source argument is the same as destination" "memcpy" } */
+}
+
+void call_memcpy (void *d, size_t n)
+{
+ const void *s = d;
+ wrap_memcpy (d, s, n);
+}
+
+
+void wrap_strcat (char *d, const char *s)
+{
+ strcat (d, s); /* { dg-warning "source argument is the same as destination" "strcat" } */
+}
+
+void call_strcat (char *d)
+{
+ const char *s = d;
+ wrap_strcat (d, s);
+}
+
+
+void wrap_strcpy (char *d, const char *s)
+{
+ strcpy (d, s); /* { dg-warning "source argument is the same as destination" "strcpy" } */
+}
+
+void call_strcpy (char *d)
+{
+ const char *s = d;
+ wrap_strcpy (d, s);
+}
+
+
+void wrap_strncat (char *d, const char *s, size_t n)
+{
+ strncat (d, s, n); /* { dg-warning "source argument is the same as destination" "strncat" } */
+}
+
+void call_strncat (char *d, size_t n)
+{
+ const char *s = d;
+ wrap_strncat (d, s, n);
+}
+
+
+void wrap_strncpy (char *d, const char *s, size_t n)
+{
+ strncpy (d, s, n); /* { dg-warning "source argument is the same as destination" "strncpy" } */
+}
+
+void call_strncpy (char *d, size_t n)
+{
+ const char *s = d;
+ wrap_strncpy (d, s, n);
+}
--- /dev/null
+/* PR 35503 - Warn about restricted pointers
+ { dg-do compile }
+ { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+#if !defined LINE
+# define LINE 0
+#endif
+
+#if __cplusplus
+# define restrict __restrict
+extern "C" {
+#endif
+
+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
+
+void sink (void*, ...);
+
+struct MemArrays
+{
+ char a8[8];
+ char a16[16];
+ char ax[];
+};
+
+/* Exercise memcpy with constant or known arguments. */
+
+void test_memcpy_cst (void *d, const void *s)
+{
+#undef T
+#define T(dst, src, n) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; \
+ void *pd = (dst); \
+ const void *ps = (src); \
+ memcpy (pd, ps, (n)); \
+ sink (a, pd, ps); \
+ } \
+ } while (0)
+
+ T (a, a, 0);
+ T (a, s = a, 3); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+ /* This isn't detected because memcpy calls with small power-of-2 sizes
+ are intentionally folded into safe copies equivalent to memmove.
+ It's marked xfail only because there is value in detecting such
+ invalid calls for portability, and as a reminder of why it isn't
+ diagnosed. */
+ T (a, a + 1, 1); /* { dg-warning "\\\[-Wrestrict" "memcpy with a small power of 2 size" { xfail *-*-* } } */
+ T (a, a + 3, 3);
+ T (a, a + 3, 5); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+ {
+ char a[3] = { 1, 2, 3 };
+
+ /* Verify that a call to memcpy with an exact overlap is diagnosed
+ (also tested above) but an excplicit one to __builtin_memcpy is
+ not. See bug 32667 for the rationale. */
+ (memcpy)(a, a, sizeof a); /* { dg-warning "source argument is the same as destination" "memcpy" } */
+ sink (a);
+
+ __builtin_memcpy (a, a, sizeof a);
+ sink (a);
+ }
+
+ {
+ 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];
+ /* The following is only diagnosed for sizes that aren't small
+ powers of 2. */
+ memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+ 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] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+ 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" "memcpy" } */
+ sink (&x);
+
+ d = x.a + 4;
+ s = x.b;
+ memcpy (d, s, sizeof x.a); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+ 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, 3); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+ 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" "memcpy" } */
+ sink (&x);
+ }
+
+ {
+#undef T
+#define T(dst, src, n) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; \
+ 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 "accessing 5 bytes at offsets 0 and 1 overlaps 4 bytes at offset 1" "memcpy" } */
+ T (a, a + 2, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "memcpy" } */
+ T (a, a + 3, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and 3 overlaps 2 bytes at offset 3" "memcpy" } */
+
+ T (a + 1, a, 5); /* { dg-warning "accessing 5 bytes at offsets 1 and 0 overlaps 4 bytes at offset 1" "memcpy" } */
+ T (a + 2, a, 5); /* { dg-warning "accessing 5 bytes at offsets 2 and 0 overlaps 3 bytes at offset 2" "memcpy" } */
+ T (a + 3, a, 5); /* { dg-warning "accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3" "memcpy" } */
+ }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+ a determinate range. */
+
+void test_memcpy_range (char *d, size_t sz)
+{
+#undef T
+#define T(dst, src, n) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; \
+ void *pd = (dst); \
+ const void *ps = (src); \
+ memcpy (pd, ps, (n)); \
+ sink (a, pd, ps); \
+ } \
+ } while (0)
+
+ ptrdiff_t ir = SR (2, 3);
+ T (a + ir, a, 0);
+ T (a + ir, a, 1);
+ T (a + ir, a, 2);
+ T (a + ir, a, 3);
+ /* The following fails because the size is a small power of 2. */
+ T (a + ir, a, 4); /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 2]" "memcpy" { xfail *-*-*} } */
+ T (a + ir, a, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
+
+ T (d + ir, d, 0);
+ T (d + ir, d, 1);
+ T (d + ir, d, 2);
+ T (d + ir, d, 3);
+ T (d + ir, d, 4); /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 1 byte at offset 3" "bug 79220" { xfail *-*-* } } */
+ T (d + ir, d, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
+
+ /* Because the size is constant and a power of 2 the following is
+ folded too early to detect the overlap. */
+ T (d + ir, d, 4); /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+ T (d + ir, d, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
+
+ /* Exercise the full range of size_t. */
+ T (d + sz, d, 0);
+ T (d + sz, d, 1);
+ T (d + sz, d, 9);
+
+ T (a, a + 1, SR (0, 1));
+ T (a, a + 1, SR (0, 2));
+ T (a, a + 1, SR (1, 2));
+ T (a, a + 1, SR (2, 3)); /* { dg-warning "accessing between 2 and 3 bytes at offsets 0 and 1 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+ T (a, a + 1, UR (2, DIFF_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+ T (a, a + 1, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+ T (a, a + 2, SR (2, 3));
+ T (a, a + 2, SR (3, 4)); /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and 2 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+ T (a, a + 3, SR (3, 4));
+ T (a, a + 3, SR (4, 5)); /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and 3 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+ T (a, a + 3, SR (5, 6)); /* { dg-warning "accessing between 5 and 6 bytes at offsets 0 and 3 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+ T (a + 1, a, SR (0, 1));
+ T (a + 1, a, SR (0, 2));
+ T (a + 1, a, SR (1, 2));
+ T (a + 1, a, SR (2, 3)); /* { dg-warning "accessing between 2 and 3 bytes at offsets 1 and 0 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+ T (a + 1, a, UR (2, DIFF_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+ T (a + 1, a, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+ T (a + 2, a, SR (2, 3));
+ T (a + 2, a, SR (3, 4)); /* { dg-warning "accessing between 3 and 4 bytes at offsets 2 and 0 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+ T (a + 3, a, SR (3, 4));
+ T (a + 3, a, SR (4, 5)); /* { dg-warning "accessing between 4 and 5 bytes at offsets 3 and 0 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+ T (a + 3, a, SR (5, 6)); /* { dg-warning "accessing between 5 and 6 bytes at offsets 3 and 0 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+ ir = SR (2, 5);
+ T (a, a + ir, 4);
+ T (a, a + ir, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] overlaps between 1 and 3 bytes at offset \\\[2, 4]" "memcpy" } */
+ T (a, a + ir, 6); /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] overlaps between 3 and 4 bytes at offset \\\[2, 3]" "memcpy" } */
+
+ /* Below, there are two possible regions for the source of the copy:
+ 1) one just before the high end of the address space that's 3
+ bytes large close to the lower end of the offset range, and
+ 2) another in the [DIFF_MIN, -8] range from D and so at least
+ 8 bytes in size
+ A copy from (1) overlaps but one from (2) does not. Verify that
+ the copy is not diagnosed. (This test case was reduced from
+ the Linux kernel.) */
+ T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 5);
+ T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 6);
+ T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 7);
+ T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 9);
+
+ T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 5);
+ T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 6);
+ T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 7);
+ T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 9);
+
+ {
+ /* Create an offset in the range [0, -1]. */
+ size_t o = sz << 1;
+ T (d, d + o, 12345);
+ T (d + o, d, 23456);
+ }
+
+ /* Exercise memcpy with both destination and source pointer offsets
+ in some known range. */
+ size_t n = UR (3, 4);
+ T (a + SR (1, 3), a + SR (1, 3), n); /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[1, 3] overlaps between 1 and 4 bytes at offset \\\[1, 3]" "memcpy" } */
+
+ /* This is an interesting case:
+ memcpy (a + i, a + j, n) with i in [1, 3], j in [2, 3], and n in [3, 4]
+ we have the following possibilities ('^' denotesthe destination offset,
+ '*' marks the overlap, and '?' is the possible overlap for large n):
+ i j | a = 012345678 SIZ OFF (size and offset of the overlap)
+ 1 2 | ^**? 2-3 2
+ 1 3 | ^ *? 1-2 3
+ 2 2 | ***? 3-4 2
+ 2 3 | ^**? 2-3 3
+ 3 3 | ***? 3-4 3
+ There are two ways to present the results:
+ 1) overlaps between 1 and 4 bytes at offset [2, 3]
+ 2) overlaps between 1 and 4 bytes at offset 2. */
+ T (a + SR (1, 3), a + SR (2, 3), n); /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[2, 3] overlaps between 1 and 4 bytes at offset \\\[2, 3]" "memcpy" } */
+ T (a + SR (1, 3), a + SR (3, 4), n);
+
+ T (a + SR (2, 3), a + SR (3, 4), n); /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[2, 3] and \\\[3, 4] overlaps between 1 and 4 bytes at offset \\\[3, 4]" "memcpy" } */
+
+ T (a + SR (1, 3), a + SR (4, 5), n);
+ T (a + SR (2, 3), a + SR (4, 5), n);
+ T (a + SR (3, 4), a + SR (4, 5), n); /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[3, 4] and \\\[4, 5] overlaps between 1 and 4 bytes at offset \\\[4, 5]" "memcpy" } */
+
+ /* Exercise the full range of size_t. */
+ T (d, d + sz, 0);
+ T (d, d + sz, 1);
+ T (d, d + sz, 9);
+}
+
+/* Exercise memcpy with offset and/or size in a determinate anti-range. */
+
+void test_memcpy_anti_range (char *d, const char *s)
+{
+ T (d, d + SAR (0, 3), 1);
+ T (d, d + SAR (0, 3), 2);
+ T (d, d + SAR (0, 3), 3);
+ T (d, d + SAR (0, 3), DIFF_MAX - 2); /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
+ T (d, d + SAR (0, 3), DIFF_MAX - 1); /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
+ T (d, d + SAR (0, 3), DIFF_MAX); /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+
+ T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX)); /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" } */
+
+ /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
+ doesn't trigger a warning. */
+ T (d, s, UAR (1, DIFF_MAX - 1));
+ T (d, s, UAR (1, DIFF_MAX));
+ T (d, s, UAR (1, SIZE_MAX - 1));
+
+ /* This causes the last dg-warning test to fail for some reason.
+ T (d, s, UAR (1, SIZE_MAX)); */
+}
+
+/* Verify calls to memcpy() where the combination of offsets in some
+ range and size is such that either overlap is unavoidable or one
+ or both offsets would exceed the maximum size of an object
+ (DIFF_MAX). */
+
+void test_memcpy_range_exceed (char *d, const char *s)
+{
+ /* 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. */
+ ptrdiff_t i = SR (3, 5);
+ size_t n = UR (4, 6);
+
+ T (a, a + i, n);
+ T (a + i, a, n);
+ /* Ditto for objects of unknown sizes. */
+ T (d, d + i, n);
+ T (d + i, d, n);
+
+ /* Verify that a warning is issued for a copy between two regions
+ whose aggregate size would exceed DIFF_MAX if it were to not
+ overlap. */
+ T (d, s, DIFF_MAX / 2);
+ T (d, s, DIFF_MAX / 2 + 1); /* { dg-warning "overlaps 1 byte" "memcpy" } */
+ T (d, s, DIFF_MAX / 2 + 2); /* { dg-warning "overlaps 3 bytes" "memcpy" } */
+ T (d, s, DIFF_MAX / 2 + 3); /* { dg-warning "overlaps 5 bytes" "memcpy" } */
+
+ i = SR (DIFF_MAX - 2, DIFF_MAX);
+
+ /* Verify a warning for an out-of-bounds offset range and constant
+ size addition. */
+ T (d, d + i, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+ T (d + i, d, 3); /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+ T (d + 1, d + i, 3); /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+ T (d + i, d + 1, 3); /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+ /* Verify that the warnings above are independent of whether the source
+ and destination are known to be based on the same object. */
+ T (d, s + i, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+ T (d + i, s, 3); /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+ T (d + 1, s + i, 3); /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+ T (d + i, s + 1, 3); /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+#if __SIZEOF_SIZE_T__ == 8
+ /* Verfiy the offset and size computation is correct. The overlap
+ offset mentioned in the warning plus sthe size of the access must
+ not exceed DIFF_MAX. */
+ T (d, d + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+ T (d + i, d, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+
+ T (d, s + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+ T (d + i, s, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+#elif __SIZEOF_SIZE_T__ == 4
+ T (d, d + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+ T (d + i, d, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+
+ T (d, s + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+ T (d + i, s, 5); /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+#endif
+
+ ptrdiff_t j = SR (DIFF_MAX - 9, DIFF_MAX - 1);
+ i = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+ n = UR (4, 5);
+ T (d + i, d + j, n);
+
+ n = UR (4, DIFF_MAX - 1);
+ T (d + i, d + j, n);
+
+ n = UR (4, SIZE_MAX - 1);
+ T (d + i, d + j, n);
+
+ j = SR (DIFF_MAX - 8, DIFF_MAX - 1);
+ T (d + i, d + j, n);
+
+ j = SR (DIFF_MAX - 7, DIFF_MAX - 1);
+ T (d + i, d + j, n); /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+ j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+ T (d + i, d + j, n); /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+ n = UR (3, DIFF_MAX);
+ T (d + i, d + j, n);
+
+ j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+ T (d + i, d + j, n);
+
+ j = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+ T (d + i, d + j, n); /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+ j = SR (DIFF_MAX - 4, DIFF_MAX - 1);
+ T (d + i, d + j, n); /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+ j = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+ T (d + i, d + j, n); /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+}
+
+/* Exercise memcpy with destination and source of unknown size. */
+
+void test_memcpy_var (char *d, const char *s)
+{
+ size_t n = unsigned_value ();
+
+ memcpy (d, d, 0);
+ sink (d);
+
+ memcpy (d, d, n); /* { dg-warning "source argument is the same as destination" "memcpy" } */
+ sink (d);
+
+ memcpy (d, &d[0], n); /* { dg-warning "source argument is the same as destination" "memcpy" } */
+ sink (d);
+
+ memcpy (&d[0], d, n); /* { dg-warning "source argument is the same as destination" "memcpy" } */
+ sink (d);
+
+ s = d;
+ memcpy (d, s, n); /* { dg-warning "source argument is the same as destination" "memcpy" } */
+ sink (d);
+
+ /* The following overlaps if n is greater than 1. */
+ s = d + 1;
+ memcpy (d, s, n);
+ sink (d);
+
+ s = d + n;
+ memcpy (d, s, n);
+ sink (d);
+
+ s = d + signed_value ();
+ memcpy (d, s, unsigned_value ());
+ 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" "memcpy" } */
+ sink (d);
+
+ s = d + 5;
+ n = 7;
+ memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+ n = UR (0, 1);
+ s = d;
+ memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+}
+
+
+void test_memcpy_memarrray (struct MemArrays *p)
+{
+#undef T
+#define T(dst, src, n) do { \
+ if (!LINE || LINE == __LINE__) { \
+ void *pd = (dst); \
+ const void *ps = (src); \
+ memcpy (pd, ps, (n)); \
+ sink (pd, ps); \
+ } \
+ } while (0)
+
+ T (p->a8, p->a8, 0);
+ T (p->a8, p->a8 + 1, 1);
+ T (p->a8, p->a8 + 2, 2);
+ T (p->a8, p->a8 + 8, 1);
+
+ T (p->a8, p->a8 + 2, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+}
+
+/* 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 { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[9] = init; \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strcat (pd, ps); \
+ sink (a, pd, ps); \
+ } \
+ } while (0)
+
+ T ("0", a, a); /* { dg-warning "source argument is the same as destination" "strcat" } */
+ T ("01", a, a); /* { dg-warning "source argument is the same as destination" "strcat" } */
+ T ("012", a, a); /* { dg-warning "source argument is the same as destination" "strcat" } */
+ /* The 3 bytes "12\0" being appended to "012" overwrite the final NUL. */
+ T ("012", a, a + 1); /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 1 byte at offset 3" "strcat" } */
+ T ("012", a, a + 2); /* { dg-warning "accessing 2 bytes at offsets 0 and 2 overlaps 1 byte at offset 3" "strcat" } */
+ /* The nul copied from a[3] to a[3] overwrites itself so this is
+ diagnosed. */
+ T ("012", a, a + 3); /* { dg-warning "accessing 1 byte at offsets 0 and 3 overlaps 1 byte at offset 3" "strcat" } */
+
+ T ("012", a, a + 4);
+ T ("012", a, a + 5);
+ T ("012", a, a + 6);
+ T ("012", a, a + 7);
+ T ("012", a, a + 8);
+
+ T ("0", a + 1, a); /* { dg-warning "accessing 2 bytes at offsets 1 and 0 overlaps 1 byte at offset 1" "strcat" } */
+ T ("0", a + 2, a);
+
+ /* The first of the two offsets in the diagnostic for strcat is that
+ of the first write into the destination, not that of the initial
+ read from it to compute its length. */
+ T ("01", a + 1, a); /* { dg-warning "accessing 3 bytes at offsets 1 and 0 overlaps 1 byte at offset 2" "strcat" } */
+ T ("01", a + 2, a); /* { dg-warning "accessing 3 bytes at offsets 2 and 0 overlaps 1 byte at offset 2" "strcat" } */
+ T ("01", a + 3, a);
+
+ T ("012", a + 1, a); /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 1 byte at offset 3" "strcat" } */
+ T ("012", a + 2, a); /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 1 byte at offset 3" "strcat" } */
+ T ("012", a + 3, a); /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3 " "strcat" } */
+ T ("012", a + 4, a);
+ T ("012", a + 5, a);
+
+ /* Verify that the obviously benign cases below aren't diagnosed. */
+ T ("012", a, "012");
+ T ("012", a, s);
+ T ("01234567", 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 { \
+ if (!LINE || LINE == __LINE__) { \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strcat (pd, ps); \
+ sink (pd, ps); \
+ } \
+ } while (0)
+
+ T (d, d); /* { dg-warning "source argument is the same as destination" "strcat" } */
+ T (d, d + 1); /* { dg-warning "accessing 0 or more bytes at offsets 0 and 1 may overlap 1 byte" "strcat" } */
+ T (d, d + 2); /* { dg-warning "accessing 0 or more bytes at offsets 0 and 2 may overlap 1 byte" "strcat" } */
+ T (d, d + 999); /* { dg-warning "accessing 0 or more bytes at offsets 0 and 999 may overlap 1 byte" "strcat" } */
+ T (d, d + -99); /* { dg-warning "accessing 0 or more bytes at offsets 0 and -99 may overlap 1 byte" "strcat" } */
+
+ size_t n = unsigned_value ();
+
+ T (d + n, d + n); /* { dg-warning "\\\[-Wrestrict" "strcat" } */
+
+ /* Verify that the obviously benign cases below aren't diagnosed. */
+ T (d, "012");
+ T (d + 1, "0123");
+ T (d + n, "01234");
+ T (d, s);
+ T (d + 1, s);
+ T (d + n, s);
+
+ /* Since the offset is unknown the overlap in the call below, while
+ possible, is certainly not inevitable. Conservatively, it should
+ not be diagnosed. For safety, an argument for diagnosing can be
+ made. It's a judgment call, partly determined by the effort and
+ complexity of treating this case differently from other similar
+ to it. */
+ T (d, d + n); /* { dg-warning "may overlap" "strcat" } */
+}
+
+/* Exercise strcpy with constant or known arguments. */
+
+void test_strcpy_cst (ptrdiff_t i)
+{
+#undef T
+#define T(init, dst, src) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[8] = init; \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strcpy (pd, ps); \
+ sink (a, pd, ps); \
+ } \
+ } while (0)
+
+ T ("012", a, a); /* { dg-warning "source argument is the same as destination" "strcpy" } */
+ T ("012", a, a + 1); /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
+ T ("012", a, a + 2);
+ 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);
+
+ /* 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" } */
+
+ /* The '5' copied from s[2] to d[2] overwrites s[0]. */
+ T ("01234", a, a + 2); /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" } */
+
+ /* This happens to be safe in GCC but it's still wrong. */
+ T ("012", a, a); /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+ T ("012", a + 1, a); /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 3 bytes at offset 1" "strcpy" } */
+ 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
+ for writing past the end. */
+ T ("012", a + sizeof a, 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(N, init, dst, src) \
+ do { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[N] = init; \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strcpy (pd, ps); \
+ sink (a, pd, ps); \
+ } \
+ } while (0)
+
+ ptrdiff_t r;
+
+ r = SR (0, 1);
+ T (8, "0", a + r, a); /* { dg-warning "accessing between 1 and 2 bytes at offsets \\\[0, 1] and 0 overlaps up to 2 bytes at offset \\\[0, 1]" "strcpy" { xfail *-*-*} } */
+
+ r = SR (2, 5);
+ T (8, "01", a + r, a); /* { dg-warning "accessing 3 bytes at offsets \\\[2, 5] and 0 may overlap 1 byte at offset 2" } */
+ T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[2, 5] and 0 may overlap up to 2 bytes at offset \\\[3, 2]" "strcpy" } */
+
+ /* The highest offset to which to copy without overflowing the 8-byte
+ destination is 3 and that overlaps 2 bytes. */
+ T (8, "0123", a + r, a); /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "strcpy" } */
+
+ /* With a 9-byte destination the highest offset is 4 and that still
+ overlaps 1 byte (the final NUL). */
+ T (9, "0123", a + r, a); /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 1 and 3 bytes at offset \\\[2, 4]" "strcpy" } */
+
+ /* With a 10-byte buffer it's possible to copy all 5 bytes without
+ overlap at (a + 5). Copying at offsets 2 through 4 overflows
+ between 3 and 1 bytes, respectively. */
+ T (10, "0123", a + r, a); /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 may overlap up to 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+
+ r = SR (3, 4);
+ T (8, "01", a + r, a);
+ T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[3, 4] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+ /* The highest offset to which to copy without overflowing the 8-byte
+ destination is 3 and that overlaps 2 bytes. */
+ T (8, "0123", a + r, a); /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps 2 bytes at offset 3" "strcpy" } */
+
+ /* With a 9-byte destination the highest offset is 4 and that still
+ overlaps 1 byte (the final NUL). */
+ T (9, "0123", a + r, a); /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 4]" "strcpy" } */
+
+ /* With a 10-byte buffer it's possible to copy all 5 bytes without
+ overlap at (a + 5). Copying at offsets 2 through 4 overflows
+ between 3 and 1 bytes, respectively. */
+ T (10, "0123", a + r, a); /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 4]" "strcpy" } */
+
+ T (8, "01", a, a + r);
+ T (8, "012", a, a + r);
+ T (8, "0123", a, a + r);
+ T (8, "01234", a, a + r);
+
+ /* With the smaller offset of 3 the final NUL definitely overlaps
+ the '4' at a[3], but with the larger offset of 4 there is no
+ overlap, so the warning is a "may overlap" and the size of
+ the overlap is 1 byte. */
+ T (8, "012345", a, a + r); /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and \\\[3, 4] may overlap 1 byte at offset 3" "strcpy" } */
+ T (8, "0123456", a, a + r); /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and \\\[3, 4] may overlap up to 2 bytes at offset 3" "strcpy" } */
+
+ 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" } */
+
+ 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" } */
+
+ /* Exercise the full range of ptrdiff_t. */
+ r = signed_value ();
+
+ /* The overlap in the cases below isn't inevitable but it is diagnosed
+ because it is possible and so the code is considered unsafe. */
+ T (8, "", a, a + r); /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
+ T (8, "0", a + r, a); /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
+ T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+
+ T (8, "", a, a + r); /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
+ T (8, "0", a, a + r); /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
+ T (8, "012", a, a + r); /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+}
+
+/* Exercise strcpy with destination and/or source of unknown lengthu. */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strcpy (pd, ps); \
+ sink (pd, ps); \
+ } \
+ } while (0)
+
+ T (d, s);
+
+ T (d, &d[0]); /* { dg-warning "source argument is the same as destination" "strcpy" } */
+ T (&d[0], d); /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+ s = d;
+ T (d, s); /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+ /* 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 = SR (2, 3);
+ T (d, d + r);
+
+ /* The following overlaps only if strlen (s + n) >= n so it's not
+ diagnosed. */
+ s = d + signed_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 { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[9] = init; \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strncpy (pd, ps, (size)); \
+ sink (a, pd, ps); \
+ } \
+ } while (0)
+
+ T ("012", a, a, 0);
+ T ("012", a, a, 1); /* { dg-warning "source argument is the same as destination " "strncpy" } */
+
+ T ("012", a, a + 1, 1);
+ T ("012", a, a + 1, 2); /* { dg-warning "accessing 2 bytes at offsets 0 and 1 overlaps 1 byte at offset 1" "strncpy" } */
+ T ("012", a, a + 1, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strncpy" } */
+ T ("012", a, a + 1, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and 1 overlaps 3 bytes at offset 1" "strncpy" } */
+ T ("012", a, a + 1, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 3 bytes at offset 1" "strncpy" } */
+ T ("012", a, a + 1, 6); /* { dg-warning "accessing 6 bytes at offsets 0 and 1 overlaps 3 bytes at offset 1" "strncpy" } */
+
+ T ("012", a, a + 2, 1);
+ T ("012", a, a + 2, 2);
+ /* The third written byte (nul) overwrites a[2]. */
+ T ("012", a, a + 2, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+ T ("012", a, a + 2, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+ T ("012", a, a + 2, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+
+ T ("0123", a, a + 2, 1);
+ T ("0123", a, a + 2, 2);
+ /* The terminating nul written to a[2] overwrites s[0]. */
+ T ("0123", a, a + 2, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+ T ("0123", a, a + 2, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+ T ("0123", a, a + 2, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "strncpy" } */
+ T ("0123", a, a + 2, 6); /* { dg-warning "accessing 6 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "strncpy" } */
+
+ T ("01234", a, a + 2, 1);
+ T ("01234", a, a + 2, 2);
+ T ("01234", a, a + 2, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+ /* The '5' copied from s[2] to d[2] overwrites s[0]. */
+ T ("01234", a, a + 2, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+ T ("01234", a, a + 2, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "strncpy" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range. */
+
+void test_strncpy_range (char *d, size_t n)
+{
+#undef T
+#define T(init, dst, src, size) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char a[9] = init; \
+ strncpy ((dst), (src), (size)); \
+ sink (a, (dst), (src)); \
+ } \
+ } while (0)
+
+ ptrdiff_t i;
+
+ i = SR (0, 1);
+ T ("0123", a, a + i, 0);
+ T ("0123", a, a + i, 1);
+ /* Offset in the range [0, i] is represented as a PHI (&a, &a + i)
+ that the implementation isn't equipped to handle yet. */
+ T ("0123", a, a + i, 2); /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[0, 1] may overlap 1 byte at offset 1" "strncpy" { xfail *-*-* } } */
+
+ i = SR (1, 5);
+ T ("0123", a, a + i, 0);
+ T ("0123", a, a + i, 1);
+ T ("0123", a, a + i, 2); /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[1, 5] may overlap 1 byte at offset 1" "strncpy" } */
+ T ("0123", a, a + i, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[1, 5] may overlap up to 2 bytes at offset \\\[2, 1]" "strncpy" } */
+ T ("0123", a, a + i, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[1, 5] may overlap up to 3 bytes at offset \\\[3, 1]" "strncpy" } */
+ T ("0123", a, a + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[1, 5] may overlap up to 4 bytes at offset \\\[4, 1]" "strncpy" } */
+
+ i = SR (2, 5);
+ T ("0123", a, a + i, 0);
+ T ("0123", a, a + i, 1);
+ T ("0123", a, a + i, 2);
+ T ("0123", a, a + i, 3); /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[2, 5] may overlap 1 byte at offset 2" "strncpy" } */
+ T ("0123", a, a + i, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[2, 5] may overlap up to 2 bytes at offset \\\[3, 2]" "strncpy" } */
+ T ("0123", a, a + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+ T ("0123", a, a + i, 6); /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+
+ i = SR (3, 5);
+ T ("0123", a, a + i, 0);
+ T ("0123", a, a + i, 1);
+ T ("0123", a, a + i, 2);
+ T ("0123", a, a + i, 3);
+ T ("0123", a, a + i, 4); /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[3, 5] may overlap 1 byte at offset 3" "strncpy" } */
+ T ("0123", a, a + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+ T ("0123", a, a + i, 6); /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+
+ i = SR (4, 5);
+ T ("0123", a, a + i, 0);
+ T ("0123", a, a + i, 1);
+ T ("0123", a, a + i, 2);
+ T ("0123", a, a + i, 3);
+ T ("0123", a, a + i, 4);
+ T ("0123", a, a + i, 5); /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+ T ("0123", a, a + i, 6); /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+
+ /* 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
+ like so ('*' denotes the terminating NUL, '.' the appended NUL
+ that's not copied from the source):
+ a: 01234567* (also indicates offset)
+ i = 4: 4567 none
+ 4567* overlaps 1 at offset 4
+ 4567*. overlaps 2 at offset 4
+ i = 5: 567* none
+ 567*. none
+ 567*.. overlaps 1 at offset 5 */
+ T ("01234567", a, a + i, UR (4, 6)); /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+ /* Ditto for objects of unknown sizes. */
+ T ("01234567", d, d + i, UR (4, 6)); /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+ T ("01234567", a, a + i, UR (6, 7)); /* { dg-warning "accessing between 6 and 7 bytes at offsets 0 and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[4, 5]" "strncpy" } */
+
+ /* The following overlaps except in the unlikely case that value ()
+ is zero, so it's diagnosed. */
+ T ("012", a, a, n); /* { dg-warning "source argument is the same as destination " "strncpy" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length. */
+
+void test_strncpy_var (char *d, const char *s, size_t n)
+{
+#undef T
+#define T(dst, src, size) do { \
+ if (!LINE || LINE == __LINE__) { \
+ char *pd = (dst); \
+ const char *ps = (src); \
+ strncpy (pd, ps, (size)); \
+ sink (pd, ps); \
+ } \
+ } while (0)
+
+ T (d, s, 1);
+ T (d, s, n);
+
+ T (d, d, 1); /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+ T (d, d, n); /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+
+ T (d, d + 1, 1);
+ T (d, d + 1, 2); /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+ T (d + 1, d, 1);
+ T (d + 1, d, 2); /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+struct MemberArrays
+{
+ char a[7];
+ 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);
+}
/* Test -Wsizeof-pointer-memaccess warnings. */
/* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
/* { dg-require-effective-target alloca } */
typedef __SIZE_TYPE__ size_t;
/* Test -Wsizeof-pointer-memaccess warnings. */
/* { dg-do compile } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
/* { dg-require-effective-target alloca } */
#define bos(ptr) __builtin_object_size (ptr, 1)
// Test -Wsizeof-pointer-memaccess warnings.
// { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
// Test just twice, once with -O0 non-fortified, once with -O2 fortified.
// { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } }
// { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
// Test -Wsizeof-pointer-memaccess warnings.
// { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
// Test just twice, once with -O0 non-fortified, once with -O2 fortified,
// suppressing buffer overflow warnings.
// { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } }
/* { dg-do compile } */
-/* { dg-options "-O2 -Wall" } */
+/* { dg-options "-O2 -Wall -Wno-array-bounds" } */
#include "Wobjsize-1.h"
--- /dev/null
+/* Test to verify that the temporary doesn't trigger a bogus -Warray-bounds
+ warning. Distilled from libat_exchange_large_inplace in libatomic/gexch.c.
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+typedef typeof (sizeof 0) size_t;
+
+extern void *memcpy (void*, const void*, size_t);
+
+void libat_exchange_large_inplace (size_t n, void *mptr, void *vptr)
+{
+ char temp[1024];
+
+ size_t i = 0;
+
+ for (i = 0; n >= 1024; i += 1024, n -= 1024)
+ {
+ memcpy (temp, mptr + i, 1024);
+
+ /* The memcpy call below results in the following:
+ unsigned long ivtmp.7;
+
+ ivtmp.7_4 = (unsigned long) mptr_9(D);
+ ...
+ <bb 4>
+ # ivtmp.7_22 = PHI <ivtmp.7_4(3), ivtmp.7_5(4)>
+ ...
+ _1 = (void *) ivtmp.7_22;
+ ...
+ memcpy (_1, _2, 1024);
+
+ Treating _1 as a pointer results in the bogus:
+ warning: 'memcpy' offset 0 is out of the bounds [0, 8] of object 'ivtmp.7' with type 'long unsigned int' [-Warray-bounds]
+ memcpy (mptr + i, vptr + i, 1024);
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+ memcpy (mptr + i, vptr + i, 1024);
+
+ memcpy (vptr + i, temp, 1024);
+ }
+}
--- /dev/null
+/* Test to verify that VLAs are handled gracefully by -Wrestrict
+ { dg-do compile }
+ { dg-options "-O2 -Wrestrict" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+#define memcpy(d, s, n) __builtin_memcpy (d, s, n)
+#define strcpy(d, s) __builtin_strcpy (d, s)
+
+void test_vla (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+ char a[n];
+ char b[n];
+
+ strcpy (a, s1);
+ strcpy (b, s2);
+
+ memcpy (d, i ? a : b, n);
+}
+
+
+void test_vla_member (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+ struct S
+ {
+ char a[n];
+ char b[n];
+ } s;
+
+ strcpy (s.a, s1);
+ strcpy (s.b, s2);
+
+ memcpy (d, i ? s.a : s.b, n);
+}
/* Test -Wsizeof-pointer-memaccess warnings. */
/* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
/* { dg-require-effective-target alloca } */
typedef __SIZE_TYPE__ size_t;
/* PR tree-optimization/80669 - Bad -Wstringop-overflow warnings for stpncpy
{ dg-do compile }
- { dg-options "-O2 -Wall -Wno-stringop-truncation" } */
+ { dg-options "-O2 -Wall -Wno-array-bounds -Wno-restrict -Wno-stringop-truncation" } */
#define SIZE_MAX __SIZE_MAX__
vx = stpcpy (&buf2[18], "a");
vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "writing 3" "stpcpy" } */
strncpy (&buf2[18], "a", 2);
- strncpy (&buf2[18], "a", 3); /* { dg-warning "writing 3 bytes into a region of size 2" "strncpy" } */
+
+ /* Both warnings below are equally meaningful. */
+ strncpy (&buf2[18], "a", 3); /* { dg-warning "(writing 3 bytes into a region of size 2|specified bound 3 exceeds destination size 2)" "strncpy" } */
+
strncpy (&buf2[18], "abc", 2);
strncpy (&buf2[18], "abc", 3); /* { dg-warning "writing 3 " "strncpy" } */
memset (buf2, '\0', sizeof (buf2));
test2 (const H h)
{
char c;
- strncpy (&c, str, 3); /* { dg-warning "writing 3 bytes into a region of size 1" "strncpy" } */
+ strncpy (&c, str, 3); /* { dg-warning "(writing 3 bytes into a region of size 1|specified bound 3 exceeds destination size 1)" "strncpy" } */
struct { char b[4]; } x;
sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */
--- /dev/null
+/* Test to verify that overlapping memcpy with const sizes that are powers
+ of two are folded into into the same code as memmove, but that they
+ are diagnosed nonetheless.
+ { dg-do compile }
+ { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+ __builtin_memcpy (a + 1, a, 2); /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+ __builtin_memcpy (a + 2, a, 4); /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+ __builtin_memcpy (a + 3, a, 8); /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+ __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+ __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+ __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+ { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
-/* 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" } */
{
return __builtin___mempcpy_chk (&a, &a, x, 0);
}
+
+/* The calls above violate strict aliasing. Eliminate the -Wrestrict
+ warnings they trigger.
+ { dg-prune-output "\\\[-Wrestrict]" } */
/* PR middle-end/79223 - missing -Wstringop-overflow on a memmove overflow
{ dg-do compile }
- { dg-additional-options "-O2 -Wall -std=gnu99" } */
+ { dg-additional-options "-O2 -Wall -Wno-array-bounds -std=gnu99" } */
typedef __SIZE_TYPE__ size_t;
/* PR other/81345 - -Wall resets -Wstringop-overflow to 1 from the default 2
{ dg-do compile }
- { dg-options "-O2 -Wall" } */
+ { dg-options "-O2 -Wall -Wno-array-bounds" } */
char a[3];
--- /dev/null
+#ifndef RANGE_H
+
+/* Definitions of helper functions and macros to create expressions
+ in a specified range. Not all the symbols declared here are
+ defined. */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX __PTRDIFF_MAX__
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+typedef __INT32_TYPE__ int32_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+
+static inline ptrdiff_t signed_value (void)
+{
+ extern volatile ptrdiff_t signed_value_source;
+ return signed_value_source;
+}
+
+static inline size_t unsigned_value (void)
+{
+ extern volatile size_t unsigned_value_source;
+ return unsigned_value_source;
+}
+
+static inline ptrdiff_t signed_range (ptrdiff_t min, ptrdiff_t max)
+{
+ ptrdiff_t val = signed_value ();
+ return val < min || max < val ? min : val;
+}
+
+static inline ptrdiff_t signed_anti_range (ptrdiff_t min, ptrdiff_t max)
+{
+ ptrdiff_t val = signed_value ();
+ return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+static inline size_t unsigned_range (size_t min, size_t max)
+{
+ size_t val = unsigned_value ();
+ return val < min || max < val ? min : val;
+}
+
+static inline size_t unsigned_anti_range (size_t min, size_t max)
+{
+ size_t val = unsigned_value ();
+ return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+#endif /* RANGE_H */
/* Test -Wsizeof-pointer-memaccess warnings. */
/* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
/* Test just twice, once with -O0 non-fortified, once with -O2 fortified. */
/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
--- /dev/null
+/* { dg-do compile { target { ! x32 } } }
+ { dg-require-effective-target mempcpy }
+ { dg-options "-O2 -Wrestrict -fcheck-pointer-bounds -mmpx" } */
+
+#define USE_GNU
+#include "../../gcc.dg/strlenopt.h"
+
+/* There is no BUILT_IN_ST{P,R}NCPY_CHKP or BUILT_IN_STRNCAT_CHKP
+ so the test for them below are XFAIL. */
+char *stpncpy (char *__restrict, const char *__restrict, size_t);
+char *strncpy (char *__restrict, const char *__restrict, size_t);
+char *strncat (char *__restrict, const char *__restrict, size_t);
+
+
+char a[8];
+
+void test_memcpy (void)
+{
+ memcpy (a, a + 1, 3); /* { dg-warning ".memcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+void test_memmove (void)
+{
+ memmove (a, a + 1, 3);
+}
+
+void* test_mempcpy (void)
+{
+ return mempcpy (a, a + 1, 3); /* { dg-warning ".mempcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+char* test_stpcpy (void)
+{
+ strcpy (a, "0123456");
+ return stpcpy (a, a + 2); /* { dg-warning ".stpcpy\.chkp. accessing 6 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" } */
+}
+
+char* test_stpncpy (void)
+{
+ strcpy (a, "0123456");
+
+ /* There is no BUILT_IN_STPNCPY_CHKP so this isn't handled. */
+ return stpncpy (a, a + 2, sizeof a); /* { dg-warning ".stpcpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strcpy (void)
+{
+ strcpy (a, "0123456");
+ strcpy (a, a + 1); /* { dg-warning ".strcpy\.chkp. accessing 7 bytes at offsets 0 and 1 overlaps 6 bytes at offset 1" } */
+}
+
+void test_strcat (int n)
+{
+ strcat (a, a + 3); /* { dg-warning ".strcat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" } */
+}
+
+void test_strncat (int n)
+{
+ strncat (a, a + 3, sizeof a); /* { dg-warning ".strncat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strncpy (int n)
+{
+ strcpy (a, "0123456");
+
+ /* There is no BUILT_IN_STRNCPY_CHKP so this isn't handled. */
+ strncpy (a, a + 2, sizeof a); /* { dg-warning ".strncpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 5 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
extern gimple_opt_pass *make_pass_local_pure_const (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_nothrow (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tracer (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_restrict (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_unused_result (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_diagnose_tm_blocks (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_lower_tm (gcc::context *ctxt);
#include "ssa.h"
#include "cgraph.h"
#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "gimple-fold.h"
} laststmt;
static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
+static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
/* Return:
handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
{
int idx, didx;
- tree src, dst, srclen, len, lhs, args, type, fn, oldlen;
+ tree src, dst, srclen, len, lhs, type, fn, oldlen;
bool success;
gimple *stmt = gsi_stmt (*gsi);
strinfo *si, *dsi, *olddsi, *zsi;
}
}
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));
+ check_bounds_or_overlap (as_a <gcall *>(stmt), olddsi->ptr, src,
+ oldlen, NULL_TREE);
+ }
+
return;
}
if (zsi != NULL)
zsi->dont_invalidate = true;
- if (fn == NULL_TREE)
- return;
-
- args = TYPE_ARG_TYPES (TREE_TYPE (fn));
- type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+ if (fn)
+ {
+ tree args = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+ }
+ else
+ type = size_type_node;
len = fold_convert_loc (loc, type, unshare_expr (srclen));
len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
+
+ /* Set the no-warning bit on the transformed statement? */
+ bool set_no_warning = false;
+
+ if (const strinfo *chksi = olddsi ? olddsi : dsi)
+ if (si
+ && !check_bounds_or_overlap (as_a <gcall *>(stmt), chksi->ptr, si->ptr,
+ NULL_TREE, len))
+ {
+ gimple_set_no_warning (stmt, true);
+ set_no_warning = true;
+ }
+
+ if (fn == NULL_TREE)
+ return;
+
len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
GSI_SAME_STMT);
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
}
else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
fprintf (dump_file, "not possible.\n");
+
+ if (set_no_warning)
+ gimple_set_no_warning (stmt, true);
+}
+
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+ for out-of-bounds offsets or overlapping access, and to see if the
+ size argument is derived from a call to strlen() on the source argument,
+ and if so, issue an appropriate warning. */
+
+static void
+handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
+{
+ /* Same as stxncpy(). */
+ handle_builtin_stxncpy (bcode, gsi);
}
/* Return true if LEN depends on a call to strlen(SRC) in an interesting
return false;
}
-/* Check the size argument to the built-in forms of stpncpy and strncpy
- to see if it's derived from calling strlen() on the source argument
- and if so, issue a warning. */
+/* Check the arguments to the built-in forms of stpncpy and strncpy for
+ out-of-bounds offsets or overlapping access, and to see if the size
+ is derived from calling strlen() on the source argument, and if so,
+ issue the appropriate warning. */
static void
handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
bool with_bounds = gimple_call_with_bounds_p (stmt);
+ tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2);
+ tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+
+ int didx = get_stridx (dst);
+ if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
+ {
+ /* Compute the size of the destination string including the NUL. */
+ if (sidst->nonzero_chars)
+ {
+ tree type = TREE_TYPE (sidst->nonzero_chars);
+ dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+ build_int_cst (type, 1));
+ }
+ dst = sidst->ptr;
+ }
+
+ int sidx = get_stridx (src);
+ strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
+ if (sisrc)
+ {
+ /* strncat() and strncpy() can modify the source string by writing
+ over the terminating nul so SISRC->DONT_INVALIDATE must be left
+ clear. */
+
+ /* Compute the size of the source string including the NUL. */
+ if (sisrc->nonzero_chars)
+ {
+ tree type = TREE_TYPE (sisrc->nonzero_chars);
+ srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+ build_int_cst (type, 1));
+ }
+
+ src = sisrc->ptr;
+ }
+ else
+ srcsize = NULL_TREE;
+
+ if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, src,
+ dstsize, srcsize))
+ {
+ gimple_set_no_warning (stmt, true);
+ return;
+ }
/* If the length argument was computed from strlen(S) for some string
S retrieve the strinfo index for the string (PSS->FIRST) alonng with
return;
}
- int sidx = get_stridx (src);
- strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
-
- /* strncat() and strncpy() can modify the source string by writing
- over the terminating nul so SISRC->DONT_INVALIDATE must be left
- clear. */
-
/* Retrieve the strinfo data for the string S that LEN was computed
from as some function F of strlen (S) (i.e., LEN need not be equal
to strlen(S)). */
}
}
-/* Check the size argument to the built-in forms of strncat to see if
- it's derived from calling strlen() on the source argument and if so,
- issue a warning. */
-
-static void
-handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
-{
- /* Same as stxncpy(). */
- handle_builtin_stxncpy (bcode, gsi);
-}
-
/* 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
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 srclen, args, type, fn, objsz, endptr;
bool success;
gimple *stmt = gsi_stmt (*gsi);
strinfo *si, *dsi;
- location_t loc;
+ location_t loc = gimple_location (stmt);
bool with_bounds = gimple_call_with_bounds_p (stmt);
- src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
- dst = gimple_call_arg (stmt, 0);
- lhs = gimple_call_lhs (stmt);
+ tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+ tree dst = gimple_call_arg (stmt, 0);
+
+ /* Bail if the source is the same as destination. It will be diagnosed
+ elsewhere. */
+ if (operand_equal_p (src, dst, 0))
+ return;
+
+ tree lhs = gimple_call_lhs (stmt);
didx = get_stridx (dst);
if (didx < 0)
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);
+ }
+
+ /* Set the no-warning bit on the transformed statement? */
+ bool set_no_warning = false;
+
if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
{
+ {
+ /* 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
+ used that as both sizes. */
+ tree slen = srclen;
+ if (slen)
+ {
+ tree type = TREE_TYPE (slen);
+ slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1));
+ }
+
+ tree sptr = si && si->ptr ? si->ptr : src;
+
+ if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
+ NULL_TREE, slen))
+ {
+ gimple_set_no_warning (stmt, true);
+ set_no_warning = true;
+ }
+ }
+
/* 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. */
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;
+ tree dstlen = dsi->nonzero_chars;
endptr = dsi->endptr;
dsi = unshare_strinfo (dsi);
if (fn == NULL_TREE)
return;
- len = NULL_TREE;
+ if (dsi && dstlen)
+ {
+ tree type = TREE_TYPE (dstlen);
+
+ /* Compute the size of the source sequence, including the nul. */
+ tree srcsize = srclen ? srclen : size_zero_node;
+ srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
+
+ tree sptr = si && si->ptr ? si->ptr : src;
+
+ if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
+ dstlen, srcsize))
+ {
+ gimple_set_no_warning (stmt, true);
+ set_no_warning = true;
+ }
+ }
+
+ tree len = NULL_TREE;
if (srclen != NULL_TREE)
{
args = TYPE_ARG_TYPES (TREE_TYPE (fn));
}
else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
fprintf (dump_file, "not possible.\n");
+
+ if (set_no_warning)
+ gimple_set_no_warning (stmt, true);
}
/* Handle a call to malloc or calloc. */
}
}
-/* Attempt to optimize a single statement at *GSI using string length
- knowledge. */
+/* Attempt to check for validity of the performed access a single statement
+ at *GSI using string length knowledge, and to optimize it. */
static bool
-strlen_optimize_stmt (gimple_stmt_iterator *gsi)
+strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
{
gimple *stmt = gsi_stmt (*gsi);
/* Attempt to optimize individual statements. */
for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
- if (strlen_optimize_stmt (&gsi))
+ if (strlen_check_and_optimize_stmt (&gsi))
gsi_next (&gsi);
bb->aux = stridx_to_strinfo;