Commit ee92e7ba by Martin Sebor Committed by Martin Sebor

PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk

PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk
PR middle-end/77784 - duplicate warning for snprintf when n > object size
PR middle-end/78149 - missing warning on strncpy buffer overflow due to an excessive bound
PR middle-end/78138 - missing warnings on buffer overflow with non-constant source length

gcc/c-family/ChangeLog:

	PR c/53562
	PR middle-end/77784
	PR middle-end/78149
	PR middle-end/78138
	* c.opt (-Wstringop-overflow): New option.

gcc/ChangeLog:

	PR middle-end/77784
	PR middle-end/78149
	PR middle-end/78138
	
	* builtins.c (expand_builtin_strcat, expand_builtin_strncat): New
	functions.
	(compute_dest_size, get_size_range, check_sizes, check_strncat_sizes)
	(check_memop_sizes): Same.
	(expand_builtin_memcpy): Call check memop_sizes.
	(expand_builtin_mempcpy): Same.
	(expand_builtin_memset): Same,
	(expand_builtin_bzero): Same.
	(expand_builtin_memory_chk): Call check_sizes.
	(expand_builtin_strcpy): Same.
	(expand_builtin_strncpy): Same.
	(maybe_emit_sprintf_chk_warning): Same.
	(expand_builtin): Handle strcat and strncat.
	(fini_object_sizes): Reset pointers.
	(compute_object_size): New function.
	* gimple-ssa-sprintf.c (pass_sprintf_length::handle_gimple_call):
	Avoid issuing warnings also issued during built-in expansion.
	* doc/invoke.texi (Warning Options): Document -Wstringop-overflow.

gcc/testsuite/ChangeLog:

	PR middle-end/77784
	PR middle-end/78149
	PR middle-end/78138

	* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust expected diagnostic.
	* g++.dg/ext/builtin-object-size3.C (bar): Same.
	* g++.dg/ext/strncpy-chk1.C: Same.
	* g++.dg/opt/memcpy1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* gcc.c-torture/compile/pr55569.c: Disable -Wstringop-overflow.
	* gcc.dg/Wobjsize-1.c: Adjust expected diagnostic.
	* gcc.dg/attr-alloc_size.c: Same.
	* gcc.dg/builtin-stringop-chk-1.c: Adjust expected diagnostic.
	* gcc.dg/builtin-stringop-chk-2.c: Same.
	* gcc.dg/builtin-stringop-chk-4.c: New test.
	* gcc.dg/builtin-strncat-chk-1.c: Adjust expected diagnostic.
	* gcc.dg/memcpy-2.c: Same.
	* gcc.dg/pr40340-1.c: Same.
	* gcc.dg/pr40340-2.c (main): Same.
	* gcc.dg/pr40340-5.c (main): Same.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
	* gcc.dg/torture/pr71132.c: Disable -Wstringop-overflow.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust text of expected
	warning.
	* gfortran.dg/char_length_3.f90: Prune expected warnings.
	* gfortran.dg/pr38868.f: Add expected warnings.

From-SVN: r243419
parent fc3f36f9
2016-12-07 Martin Sebor <msebor@redhat.com>
PR middle-end/77784
PR middle-end/78149
PR middle-end/78138
* builtins.c (expand_builtin_strcat, expand_builtin_strncat): New
functions.
(compute_dest_size, get_size_range, check_sizes, check_strncat_sizes)
(check_memop_sizes): Same.
(expand_builtin_memcpy): Call check memop_sizes.
(expand_builtin_mempcpy): Same.
(expand_builtin_memset): Same,
(expand_builtin_bzero): Same.
(expand_builtin_memory_chk): Call check_sizes.
(expand_builtin_strcpy): Same.
(expand_builtin_strncpy): Same.
(maybe_emit_sprintf_chk_warning): Same.
(expand_builtin): Handle strcat and strncat.
(fini_object_sizes): Reset pointers.
(compute_object_size): New function.
* gimple-ssa-sprintf.c (pass_sprintf_length::handle_gimple_call):
Avoid issuing warnings also issued during built-in expansion.
* doc/invoke.texi (Warning Options): Document -Wstringop-overflow.
2016-12-07 Michael Meissner <meissner@linux.vnet.ibm.com> 2016-12-07 Michael Meissner <meissner@linux.vnet.ibm.com>
PR target/72717 PR target/72717
...@@ -67,7 +67,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -67,7 +67,7 @@ along with GCC; see the file COPYING3. If not see
#include "internal-fn.h" #include "internal-fn.h"
#include "case-cfn-macros.h" #include "case-cfn-macros.h"
#include "gimple-fold.h" #include "gimple-fold.h"
#include "intl.h"
struct target_builtins default_target_builtins; struct target_builtins default_target_builtins;
#if SWITCHABLE_TARGET #if SWITCHABLE_TARGET
...@@ -125,9 +125,11 @@ static rtx expand_builtin_mempcpy (tree, rtx, machine_mode); ...@@ -125,9 +125,11 @@ static rtx expand_builtin_mempcpy (tree, rtx, machine_mode);
static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode); static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode);
static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx, static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx,
machine_mode, int, tree); machine_mode, int, tree);
static rtx expand_builtin_strcat (tree, rtx);
static rtx expand_builtin_strcpy (tree, rtx); static rtx expand_builtin_strcpy (tree, rtx);
static rtx expand_builtin_strcpy_args (tree, tree, rtx); static rtx expand_builtin_strcpy_args (tree, tree, rtx);
static rtx expand_builtin_stpcpy (tree, rtx, machine_mode); static rtx expand_builtin_stpcpy (tree, rtx, machine_mode);
static rtx expand_builtin_strncat (tree, rtx);
static rtx expand_builtin_strncpy (tree, rtx); static rtx expand_builtin_strncpy (tree, rtx);
static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode); static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode);
static rtx expand_builtin_memset (tree, rtx, machine_mode); static rtx expand_builtin_memset (tree, rtx, machine_mode);
...@@ -3010,6 +3012,292 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp) ...@@ -3010,6 +3012,292 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
return dest_addr; return dest_addr;
} }
/* Fill the 2-element RANGE array with the minimum and maximum values
EXP is known to have and return true, otherwise null and return
false. */
static bool
get_size_range (tree exp, tree range[2])
{
if (tree_fits_uhwi_p (exp))
{
range[0] = range[1] = exp;
return true;
}
if (TREE_CODE (exp) == SSA_NAME)
{
wide_int min, max;
enum value_range_type range_type = get_range_info (exp, &min, &max);
if (range_type == VR_RANGE)
{
/* Interpret the bound in the variable's type. */
range[0] = wide_int_to_tree (TREE_TYPE (exp), min);
range[1] = wide_int_to_tree (TREE_TYPE (exp), max);
return true;
}
else if (range_type == VR_ANTI_RANGE)
{
/* FIXME: Handle anti-ranges. */
}
}
range[0] = NULL_TREE;
range[1] = NULL_TREE;
return false;
}
/* Try to verify that the sizes and lengths of the arguments to a string
manipulation function given by EXP are within valid bounds and that
the operation does not lead to buffer overflow. Arguments other than
EXP may be null. When non-null, the arguments have the following
meaning:
SIZE is the user-supplied size argument to the function (such as in
memcpy(d, s, SIZE) or strncpy(d, s, SIZE). It specifies the exact
number of bytes to write.
MAXLEN is the user-supplied bound on the length of the source sequence
(such as in strncat(d, s, N). It specifies the upper limit on the number
of bytes to write.
STR is the source string (such as in strcpy(d, s)) when the epxression
EXP is a string function call (as opposed to a memory call like memcpy).
As an exception, STR can also be an integer denoting the precomputed
length of the source string.
OBJSIZE 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).
When SIZE 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.. */
static bool
check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
{
/* 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);
tree slen = NULL_TREE;
/* Set to true when the exact number of bytes written by a string
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 (str)
{
/* STR is normally a pointer to string but as a special case
it can be an integer denoting the length of a string. */
if (TREE_CODE (TREE_TYPE (str)) == POINTER_TYPE)
{
/* Try to determine the range of lengths the source string
refers to. If it can be determined add one to it for
the terminating nul. Otherwise, set it to one for
the same reason. */
tree lenrange[2];
get_range_strlen (str, lenrange);
if (lenrange[0])
slen = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
size_one_node);
else
{
at_least_one = true;
slen = size_one_node;
}
}
else
slen = str;
}
if (!size && !maxlen)
{
/* When the only available piece of data is the object size
there is nothing to do. */
if (!slen)
return true;
/* Otherwise, when the length of the source sequence is known
(as with with strlen), set SIZE to it. */
size = slen;
}
if (!objsize)
objsize = 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);
tree range[2] = { NULL_TREE, NULL_TREE };
if (size)
get_size_range (size, range);
/* First check the number of bytes to be written against the maximum
object size. */
if (range[0] && tree_int_cst_lt (maxobjsize, range[0]))
{
location_t loc = tree_nonartificial_location (exp);
if (range[0] == range[1])
warning_at (loc, opt,
"%K%qD: specified size %wu "
"exceeds maximum object size %wu",
exp, get_callee_fndecl (exp),
tree_to_uhwi (range[0]),
tree_to_uhwi (maxobjsize));
else
warning_at (loc, opt,
"%K%qD: specified size between %wu and %wu "
"exceeds maximum object size %wu",
exp, get_callee_fndecl (exp),
tree_to_uhwi (range[0]),
tree_to_uhwi (range[1]),
tree_to_uhwi (maxobjsize));
return false;
}
/* 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]
&& ((tree_fits_uhwi_p (objsize)
&& tree_int_cst_lt (objsize, range[0]))
|| (tree_fits_uhwi_p (size)
&& tree_int_cst_lt (size, range[0]))))
{
unsigned HOST_WIDE_INT uwir0 = tree_to_uhwi (range[0]);
location_t loc = tree_nonartificial_location (exp);
if (at_least_one)
warning_at (loc, opt,
"%K%qD: writing at least %wu byte into a region "
"of size %wu overflows the destination",
exp, get_callee_fndecl (exp), uwir0,
tree_to_uhwi (objsize));
else if (range[0] == range[1])
warning_at (loc, opt,
(uwir0 == 1
? G_("%K%qD: writing %wu byte into a region "
"of size %wu overflows the destination")
: G_("%K%qD writing %wu bytes into a region "
"of size %wu overflows the destination")),
exp, get_callee_fndecl (exp), uwir0,
tree_to_uhwi (objsize));
else
warning_at (loc, opt,
"%K%qD: writing between %wu and %wu bytes "
"into a region of size %wu overflows "
"the destination",
exp, get_callee_fndecl (exp), uwir0,
tree_to_uhwi (range[1]), tree_to_uhwi (objsize));
/* 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)
{
get_size_range (maxlen, range);
if (range[0] && objsize && tree_fits_uhwi_p (objsize))
{
location_t loc = tree_nonartificial_location (exp);
if (tree_int_cst_lt (maxobjsize, range[0]))
{
/* Warn about crazy big sizes first since that's more
likely to be meaningful than saying that the bound
is greater than the object size if both are big. */
if (range[0] == range[1])
warning_at (loc, opt,
"%K%qD: specified bound %wu "
"exceeds maximum object size %wu",
exp, get_callee_fndecl (exp),
tree_to_uhwi (range[0]),
tree_to_uhwi (maxobjsize));
else
warning_at (loc, opt,
"%K%qD: specified bound between %wu and %wu "
" exceeds maximum object size %wu",
exp, get_callee_fndecl (exp),
tree_to_uhwi (range[0]),
tree_to_uhwi (range[1]),
tree_to_uhwi (maxobjsize));
return false;
}
if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
{
if (range[0] == range[1])
warning_at (loc, opt,
"%K%qD: specified bound %wu "
"exceeds the size %wu of the destination",
exp, get_callee_fndecl (exp),
tree_to_uhwi (range[0]),
tree_to_uhwi (objsize));
else
warning_at (loc, opt,
"%K%qD: specified bound between %wu and %wu "
" exceeds the size %wu of the destination",
exp, get_callee_fndecl (exp),
tree_to_uhwi (range[0]),
tree_to_uhwi (range[1]),
tree_to_uhwi (objsize));
return false;
}
}
}
return true;
}
/* Helper to compute the size of the object referenced by the DEST
expression which must of of pointer type, using Object Size type
OSTYPE (only the least significant 2 bits are used). Return
the size of the object if successful or NULL when the size cannot
be determined. */
static inline tree
compute_dest_size (tree dest, int ostype)
{
unsigned HOST_WIDE_INT size;
if (compute_builtin_object_size (dest, ostype & 3, &size))
return build_int_cst (sizetype, size);
return NULL_TREE;
}
/* Helper to determine and check the sizes of the source and the destination
of calls to __builtin_{bzero,memcpy,memset} calls. Use Object Size type-0
regardless of the OPT_Wstringop_overflow_ setting. Returns true on success
(no overflow or invalid sizes), false otherwise. */
static bool
check_memop_sizes (tree exp, tree dest, 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 destination object using
type-0 Object Size regardless of the object size type specified
by the option. */
tree objsize = compute_dest_size (dest, 0);
return check_sizes (OPT_Wstringop_overflow_, exp,
size, /*maxlen=*/NULL_TREE, /*str=*/NULL_TREE, objsize);
}
/* Expand a call EXP to the memcpy builtin. /* Expand a call EXP to the memcpy builtin.
Return NULL_RTX if we failed, the caller should emit a normal call, Return NULL_RTX if we failed, the caller should emit a normal call,
otherwise try to get the result in TARGET, if convenient (and in otherwise try to get the result in TARGET, if convenient (and in
...@@ -3021,13 +3309,14 @@ expand_builtin_memcpy (tree exp, rtx target) ...@@ -3021,13 +3309,14 @@ expand_builtin_memcpy (tree exp, rtx target)
if (!validate_arglist (exp, if (!validate_arglist (exp,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX; return NULL_RTX;
else
{ tree dest = CALL_EXPR_ARG (exp, 0);
tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1);
tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2);
tree len = CALL_EXPR_ARG (exp, 2);
return expand_builtin_memcpy_args (dest, src, len, target, exp); check_memop_sizes (exp, dest, len);
}
return expand_builtin_memcpy_args (dest, src, len, target, exp);
} }
/* Expand an instrumented call EXP to the memcpy builtin. /* Expand an instrumented call EXP to the memcpy builtin.
...@@ -3075,15 +3364,20 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode) ...@@ -3075,15 +3364,20 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
if (!validate_arglist (exp, if (!validate_arglist (exp,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX; return NULL_RTX;
else
{ tree dest = CALL_EXPR_ARG (exp, 0);
tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1);
tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2);
tree len = CALL_EXPR_ARG (exp, 2);
return expand_builtin_mempcpy_args (dest, src, len, /* Avoid expanding mempcpy into memcpy when the call is determined
target, mode, /*endp=*/ 1, to overflow the buffer. This also prevents the same overflow
exp); from being diagnosed again when expanding memcpy. */
} if (!check_memop_sizes (exp, dest, len))
return NULL_RTX;
return expand_builtin_mempcpy_args (dest, src, len,
target, mode, /*endp=*/ 1,
exp);
} }
/* Expand an instrumented call EXP to the mempcpy builtin. /* Expand an instrumented call EXP to the mempcpy builtin.
...@@ -3255,6 +3549,33 @@ expand_movstr (tree dest, tree src, rtx target, int endp) ...@@ -3255,6 +3549,33 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
return target; return target;
} }
/* Do some very basic size validation of a call to the strcpy builtin
given by EXP. Return NULL_RTX to have the built-in expand to a call
to the library function. */
static rtx
expand_builtin_strcat (tree exp, rtx)
{
if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
|| !warn_stringop_overflow)
return NULL_RTX;
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
/* There is no way here to determine the length of the string in
the destination to which the SRC string is being appended so
just diagnose cases when the souce string is longer than
the destination object. */
tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
check_sizes (OPT_Wstringop_overflow_,
exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
return NULL_RTX;
}
/* Expand expression EXP, which is a call to the strcpy builtin. Return /* Expand expression EXP, which is a call to the strcpy builtin. Return
NULL_RTX if we failed the caller should emit a normal call, otherwise NULL_RTX if we failed the caller should emit a normal call, otherwise
try to get the result in TARGET, if convenient (and in mode MODE if that's try to get the result in TARGET, if convenient (and in mode MODE if that's
...@@ -3263,13 +3584,20 @@ expand_movstr (tree dest, tree src, rtx target, int endp) ...@@ -3263,13 +3584,20 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
static rtx static rtx
expand_builtin_strcpy (tree exp, rtx target) expand_builtin_strcpy (tree exp, rtx target)
{ {
if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{ return NULL_RTX;
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1); tree dest = CALL_EXPR_ARG (exp, 0);
return expand_builtin_strcpy_args (dest, src, target); tree src = CALL_EXPR_ARG (exp, 1);
}
return NULL_RTX; if (warn_stringop_overflow)
{
tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
check_sizes (OPT_Wstringop_overflow_,
exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
}
return expand_builtin_strcpy_args (dest, src, target);
} }
/* Helper function to do the actual work for expand_builtin_strcpy. The /* Helper function to do the actual work for expand_builtin_strcpy. The
...@@ -3377,6 +3705,131 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset, ...@@ -3377,6 +3705,131 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
return c_readstr (str + offset, mode); return c_readstr (str + offset, mode);
} }
/* Helper to check the sizes of sequences and the destination of calls
to __builtin_strncat and __builtin___strncat_chk. Returns true on
success (no overflow or invalid sizes), false otherwise. */
static bool
check_strncat_sizes (tree exp, tree objsize)
{
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
tree maxlen = CALL_EXPR_ARG (exp, 2);
/* Try to determine the range of lengths that the source expression
refers to. */
tree lenrange[2];
get_range_strlen (src, lenrange);
/* Try to verify that the destination is big enough for the shortest
string. */
if (!objsize && warn_stringop_overflow)
{
/* If it hasn't been provided by __strncat_chk, try to determine
the size of the destination object into which the source is
being copied. */
objsize = compute_dest_size (dest, warn_stringop_overflow - 1);
}
/* Add one for the terminating nul. */
tree srclen = (lenrange[0]
? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
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))
{
warning_at (EXPR_LOCATION (exp), OPT_Wstringop_overflow_,
"specified bound %wu "
"equals the size of the destination",
tree_to_uhwi (maxlen));
return false;
}
if (!srclen
|| (maxlen && tree_fits_uhwi_p (maxlen)
&& tree_fits_uhwi_p (srclen)
&& tree_int_cst_lt (maxlen, srclen)))
srclen = maxlen;
/* The number of bytes to write is LEN but check_sizes will also
check SRCLEN if LEN's value isn't known. */
return check_sizes (OPT_Wstringop_overflow_,
exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
}
/* Similar to expand_builtin_strcat, do some very basic size validation
of a call to the strcpy builtin given by EXP. Return NULL_RTX to have
the built-in expand to a call to the library function. */
static rtx
expand_builtin_strncat (tree exp, rtx)
{
if (!validate_arglist (exp,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
|| !warn_stringop_overflow)
return NULL_RTX;
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
/* The upper bound on the number of bytes to write. */
tree maxlen = CALL_EXPR_ARG (exp, 2);
/* The length of the source sequence. */
tree slen = c_strlen (src, 1);
/* Try to determine the range of lengths that the source expression
refers to. */
tree lenrange[2];
if (slen)
lenrange[0] = lenrange[1] = slen;
else
get_range_strlen (src, lenrange);
/* Try to verify that the destination is big enough for the shortest
string. First try to determine the size of the destination object
into which the source is being copied. */
tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
/* Add one for the terminating nul. */
tree srclen = (lenrange[0]
? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
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))
{
warning_at (EXPR_LOCATION (exp), OPT_Wstringop_overflow_,
"specified bound %wu "
"equals the size of the destination",
tree_to_uhwi (maxlen));
return NULL_RTX;
}
if (!srclen
|| (maxlen && tree_fits_uhwi_p (maxlen)
&& tree_fits_uhwi_p (srclen)
&& tree_int_cst_lt (maxlen, srclen)))
srclen = maxlen;
/* 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);
return NULL_RTX;
}
/* Expand expression EXP, which is a call to the strncpy builtin. Return /* Expand expression EXP, which is a call to the strncpy builtin. Return
NULL_RTX if we failed the caller should emit a normal call. */ NULL_RTX if we failed the caller should emit a normal call. */
...@@ -3390,9 +3843,33 @@ expand_builtin_strncpy (tree exp, rtx target) ...@@ -3390,9 +3843,33 @@ expand_builtin_strncpy (tree exp, rtx target)
{ {
tree dest = CALL_EXPR_ARG (exp, 0); tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1); tree src = CALL_EXPR_ARG (exp, 1);
/* The number of bytes to write (not the maximum). */
tree len = CALL_EXPR_ARG (exp, 2); tree len = CALL_EXPR_ARG (exp, 2);
/* The length of the source sequence. */
tree slen = c_strlen (src, 1); tree slen = c_strlen (src, 1);
if (warn_stringop_overflow)
{
/* Try to determine the range of lengths that the source expression
refers to. */
tree lenrange[2];
if (slen)
lenrange[0] = lenrange[1] = slen;
else
{
get_range_strlen (src, lenrange);
slen = lenrange[0];
}
tree destsize = compute_dest_size (dest,
warn_stringop_overflow - 1);
/* The number of bytes to write is LEN but check_sizes will also
check SLEN if LEN's value isn't known. */
check_sizes (OPT_Wstringop_overflow_,
exp, len, /*maxlen=*/NULL_TREE, slen, destsize);
}
/* We must be passed a constant len and src parameter. */ /* We must be passed a constant len and src parameter. */
if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen)) if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
return NULL_RTX; return NULL_RTX;
...@@ -3480,13 +3957,14 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode) ...@@ -3480,13 +3957,14 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
if (!validate_arglist (exp, if (!validate_arglist (exp,
POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX; return NULL_RTX;
else
{ tree dest = CALL_EXPR_ARG (exp, 0);
tree dest = CALL_EXPR_ARG (exp, 0); tree val = CALL_EXPR_ARG (exp, 1);
tree val = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2);
tree len = CALL_EXPR_ARG (exp, 2);
return expand_builtin_memset_args (dest, val, len, target, mode, exp); check_memop_sizes (exp, dest, len);
}
return expand_builtin_memset_args (dest, val, len, target, mode, exp);
} }
/* Expand expression EXP, which is an instrumented call to the memset builtin. /* Expand expression EXP, which is an instrumented call to the memset builtin.
...@@ -3667,20 +4145,21 @@ expand_builtin_memset_args (tree dest, tree val, tree len, ...@@ -3667,20 +4145,21 @@ expand_builtin_memset_args (tree dest, tree val, tree len,
static rtx static rtx
expand_builtin_bzero (tree exp) expand_builtin_bzero (tree exp)
{ {
tree dest, size;
location_t loc = EXPR_LOCATION (exp);
if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX; return NULL_RTX;
dest = CALL_EXPR_ARG (exp, 0); tree dest = CALL_EXPR_ARG (exp, 0);
size = CALL_EXPR_ARG (exp, 1); tree size = CALL_EXPR_ARG (exp, 1);
check_memop_sizes (exp, dest, size);
/* New argument list transforming bzero(ptr x, int y) to /* New argument list transforming bzero(ptr x, int y) to
memset(ptr x, int 0, size_t y). This is done this way memset(ptr x, int 0, size_t y). This is done this way
so that if it isn't expanded inline, we fallback to so that if it isn't expanded inline, we fallback to
calling bzero instead of memset. */ calling bzero instead of memset. */
location_t loc = EXPR_LOCATION (exp);
return expand_builtin_memset_args (dest, integer_zero_node, return expand_builtin_memset_args (dest, integer_zero_node,
fold_convert_loc (loc, fold_convert_loc (loc,
size_type_node, size), size_type_node, size),
...@@ -6205,12 +6684,24 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, ...@@ -6205,12 +6684,24 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
return target; return target;
break; break;
case BUILT_IN_STRCAT:
target = expand_builtin_strcat (exp, target);
if (target)
return target;
break;
case BUILT_IN_STRCPY: case BUILT_IN_STRCPY:
target = expand_builtin_strcpy (exp, target); target = expand_builtin_strcpy (exp, target);
if (target) if (target)
return target; return target;
break; break;
case BUILT_IN_STRNCAT:
target = expand_builtin_strncat (exp, target);
if (target)
return target;
break;
case BUILT_IN_STRNCPY: case BUILT_IN_STRNCPY:
target = expand_builtin_strncpy (exp, target); target = expand_builtin_strncpy (exp, target);
if (target) if (target)
...@@ -9052,22 +9543,22 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, ...@@ -9052,22 +9543,22 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
len = CALL_EXPR_ARG (exp, 2); len = CALL_EXPR_ARG (exp, 2);
size = CALL_EXPR_ARG (exp, 3); size = CALL_EXPR_ARG (exp, 3);
if (! tree_fits_uhwi_p (size)) bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
exp, len, /*maxlen=*/NULL_TREE,
/*str=*/NULL_TREE, size);
if (!tree_fits_uhwi_p (size))
return NULL_RTX; return NULL_RTX;
if (tree_fits_uhwi_p (len) || integer_all_onesp (size)) if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
{ {
tree fn; /* Avoid transforming the checking call to an ordinary one when
an overflow has been detected or when the call couldn't be
if (! integer_all_onesp (size) && tree_int_cst_lt (size, len)) validated because the size is not constant. */
{ if (!sizes_ok && !integer_all_onesp (size) && tree_int_cst_lt (size, len))
warning_at (tree_nonartificial_location (exp), return NULL_RTX;
0, "%Kcall to %D will always overflow destination buffer",
exp, get_callee_fndecl (exp));
return NULL_RTX;
}
fn = NULL_TREE; tree fn = NULL_TREE;
/* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
mem{cpy,pcpy,move,set} is available. */ mem{cpy,pcpy,move,set} is available. */
switch (fcode) switch (fcode)
...@@ -9153,68 +9644,68 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, ...@@ -9153,68 +9644,68 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
static void static void
maybe_emit_chk_warning (tree exp, enum built_in_function fcode) maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
{ {
int is_strlen = 0; /* The source string. */
tree len, size; tree srcstr = NULL_TREE;
location_t loc = tree_nonartificial_location (exp); /* The size of the destination object. */
tree objsize = NULL_TREE;
/* The string that is being concatenated with (as in __strcat_chk)
or null if it isn't. */
tree catstr = NULL_TREE;
/* 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;
switch (fcode) switch (fcode)
{ {
case BUILT_IN_STRCPY_CHK: case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY_CHK: case BUILT_IN_STPCPY_CHK:
/* For __strcat_chk the warning will be emitted only if overflowing srcstr = CALL_EXPR_ARG (exp, 1);
by at least strlen (dest) + 1 bytes. */ objsize = CALL_EXPR_ARG (exp, 2);
break;
case BUILT_IN_STRCAT_CHK: case BUILT_IN_STRCAT_CHK:
len = CALL_EXPR_ARG (exp, 1); /* For __strcat_chk the warning will be emitted only if overflowing
size = CALL_EXPR_ARG (exp, 2); by at least strlen (dest) + 1 bytes. */
is_strlen = 1; catstr = CALL_EXPR_ARG (exp, 0);
srcstr = CALL_EXPR_ARG (exp, 1);
objsize = CALL_EXPR_ARG (exp, 2);
break; break;
case BUILT_IN_STRNCAT_CHK: case BUILT_IN_STRNCAT_CHK:
catstr = CALL_EXPR_ARG (exp, 0);
srcstr = CALL_EXPR_ARG (exp, 1);
maxlen = CALL_EXPR_ARG (exp, 2);
objsize = CALL_EXPR_ARG (exp, 3);
break;
case BUILT_IN_STRNCPY_CHK: case BUILT_IN_STRNCPY_CHK:
case BUILT_IN_STPNCPY_CHK: case BUILT_IN_STPNCPY_CHK:
len = CALL_EXPR_ARG (exp, 2); srcstr = CALL_EXPR_ARG (exp, 1);
size = CALL_EXPR_ARG (exp, 3); maxlen = CALL_EXPR_ARG (exp, 2);
objsize = CALL_EXPR_ARG (exp, 3);
break; break;
case BUILT_IN_SNPRINTF_CHK: case BUILT_IN_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK: case BUILT_IN_VSNPRINTF_CHK:
len = CALL_EXPR_ARG (exp, 1); maxlen = CALL_EXPR_ARG (exp, 1);
size = CALL_EXPR_ARG (exp, 3); objsize = CALL_EXPR_ARG (exp, 3);
break; break;
default: default:
gcc_unreachable (); gcc_unreachable ();
} }
if (!len || !size) if (catstr && maxlen)
return;
if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
return;
if (is_strlen)
{ {
len = c_strlen (len, 1); /* Check __strncat_chk. There is no way to determine the length
if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size)) of the string to which the source string is being appended so
just warn when the length of the source string is not known. */
if (!check_strncat_sizes (exp, objsize))
return; return;
} }
else if (fcode == BUILT_IN_STRNCAT_CHK)
{
tree src = CALL_EXPR_ARG (exp, 1);
if (! src || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
return;
src = c_strlen (src, 1);
if (! src || ! tree_fits_uhwi_p (src))
{
warning_at (loc, 0, "%Kcall to %D might overflow destination buffer",
exp, get_callee_fndecl (exp));
return;
}
else if (tree_int_cst_lt (src, size))
return;
}
else if (! tree_fits_uhwi_p (len) || ! tree_int_cst_lt (size, len))
return;
warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer", check_sizes (OPT_Wstringop_overflow_, exp,
exp, get_callee_fndecl (exp)); /*size=*/NULL_TREE, maxlen, srcstr, objsize);
} }
/* Emit warning if a buffer overflow is detected at compile time /* Emit warning if a buffer overflow is detected at compile time
...@@ -9268,10 +9759,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) ...@@ -9268,10 +9759,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
else else
return; return;
if (! tree_int_cst_lt (len, size)) /* Add one for the terminating nul. */
warning_at (tree_nonartificial_location (exp), len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
0, "%Kcall to %D will always overflow destination buffer", check_sizes (OPT_Wstringop_overflow_,
exp, get_callee_fndecl (exp)); exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
} }
/* Emit warning if a free is called with address of a variable. */ /* Emit warning if a free is called with address of a variable. */
......
2016-12-07 Martin Sebor <msebor@redhat.com>
PR c/53562
PR middle-end/77784
PR middle-end/78149
PR middle-end/78138
* c.opt (-Wstringop-overflow): New option.
2016-12-02 Maxim Ostapenko <m.ostapenko@samsung.com> 2016-12-02 Maxim Ostapenko <m.ostapenko@samsung.com>
* c-attribs.c (asan odr indicator): New attribute. * c-attribs.c (asan odr indicator): New attribute.
......
...@@ -684,6 +684,16 @@ Wsizeof-array-argument ...@@ -684,6 +684,16 @@ Wsizeof-array-argument
C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1) C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
Warn when sizeof is applied on a parameter declared as an array. Warn when sizeof is applied on a parameter declared as an array.
Wstringop-overflow
C ObjC C++ ObjC++ Warning Alias(Wstringop-overflow=, 2, 0)
Warn about buffer overflow in string manipulation functions like memcpy
and strcpy.
Wstringop-overflow=
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow) Init(2) Warning
Under the control of Object Size type, warn about buffer overflow in string
manipulation functions like memcpy and strcpy.
Wsuggest-attribute=format Wsuggest-attribute=format
C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
Warn about functions which might be candidates for format attributes. Warn about functions which might be candidates for format attributes.
......
...@@ -304,6 +304,7 @@ Objective-C and Objective-C++ Dialects}. ...@@ -304,6 +304,7 @@ Objective-C and Objective-C++ Dialects}.
-Wsizeof-pointer-memaccess -Wsizeof-array-argument @gol -Wsizeof-pointer-memaccess -Wsizeof-array-argument @gol
-Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
-Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol -Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
-Wstringop-overflow=@var{n} @gol
-Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
-Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol -Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
-Wmissing-format-attribute -Wsubobject-linkage @gol -Wmissing-format-attribute -Wsubobject-linkage @gol
...@@ -4936,6 +4937,86 @@ comparisons, so this warning level gives a very large number of ...@@ -4936,6 +4937,86 @@ comparisons, so this warning level gives a very large number of
false positives. false positives.
@end table @end table
@item -Wstringop-overflow
@itemx -Wstringop-overflow=@var{type}
@opindex Wstringop-overflow
@opindex Wno-stringop-overflow
Warn for calls to string manipulation functions such as @code{memcpy} and
@code{strcpy} that are determined to overflow the destination buffer. The
optional argument is one greater than the type of Object Size Checking to
perform to determine the size of the destination. @xref{Object Size Checking}.
The argument is meaningful only for functions that operate on character arrays
but not for raw memory functions like @code{memcpy} which always make use
of Object Size type-0. The option also warns for calls that specify a size
in excess of the largest possible object or at most @code{SIZE_MAX / 2} bytes.
The option produces the best results with optimization enabled but can detect
a small subset of simple buffer overflows even without optimization in
calls to the GCC built-in functions like @code{__builtin_memcpy} that
correspond to the standard functions. In any case, the option warns about
just a subset of buffer overflows detected by the corresponding overflow
checking built-ins. For example, the option will issue a warning for
the @code{strcpy} call below because it copies at least 5 characters
(the string @code{"blue"} including the terminating NUL) into the buffer
of size 4.
@smallexample
enum Color @{ blue, purple, yellow @};
const char* f (enum Color clr)
@{
static char buf [4];
const char *str;
switch (clr)
@{
case blue: str = "blue"; break;
case purple: str = "purple"; break;
case yellow: str = "yellow"; break;
@}
return strcpy (buf, str); // warning here
@}
@end smallexample
Option @option{-Wstringop-overflow=2} is enabled by default.
@table @gcctabopt
@item -Wstringop-overflow
@item -Wstringop-overflow=1
@opindex Wstringop-overflow
@opindex Wno-stringop-overflow
The @option{-Wstringop-overflow=1} option uses type-zero Object Size Checking
to determine the sizes of destination objects. This is the default setting
of the option. At this setting the option will not warn for writes past
the end of subobjects of larger objects accessed by pointers unless the
size of the largest surrounding object is known. When the destination may
be one of several objects it is assumed to be the largest one of them. On
Linux systems, when optimization is enabled at this setting the option warns
for the same code as when the @code{_FORTIFY_SOURCE} macro is defined to
a non-zero value.
@item -Wstringop-overflow=2
The @option{-Wstringop-overflow=2} option uses type-one Object Size Checking
to determine the sizes of destination objects. At this setting the option
will warn about overflows when writing to members of the largest complete
objects whose exact size is known. It will, however, not warn for excessive
writes to the same members of unknown objects referenced by pointers since
they may point to arrays containing unknown numbers of elements.
@item -Wstringop-overflow=3
The @option{-Wstringop-overflow=3} option uses type-two Object Size Checking
to determine the sizes of destination objects. At this setting the option
warns about overflowing the smallest object or data member. This is the
most restrictive setting of the option that may result in warnings for safe
code.
@item -Wstringop-overflow=4
The @option{-Wstringop-overflow=4} option uses type-three Object Size Checking
to determine the sizes of destination objects. At this setting the option
will warn about overflowing any data members, and when the destination is
one of several objects it uses the size of the largest of them to decide
whether to issue a warning. Similarly to @option{-Wstringop-overflow=3} this
setting of the option may result in warnings for benign code.
@end table
@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]}
@opindex Wsuggest-attribute= @opindex Wsuggest-attribute=
@opindex Wno-suggest-attribute= @opindex Wno-suggest-attribute=
......
...@@ -774,7 +774,23 @@ get_width_and_precision (const conversion_spec &spec, ...@@ -774,7 +774,23 @@ get_width_and_precision (const conversion_spec &spec,
if (spec.star_width) if (spec.star_width)
{ {
if (TREE_CODE (spec.star_width) == INTEGER_CST) if (TREE_CODE (spec.star_width) == INTEGER_CST)
width = abs (tree_to_shwi (spec.star_width)); {
width = tree_to_shwi (spec.star_width);
if (width < 0)
{
if (width == HOST_WIDE_INT_MIN)
{
/* Avoid undefined behavior due to negating a minimum.
This case will be diagnosed since it will result in
more than INT_MAX bytes on output, either by the
directive itself (when INT_MAX < HOST_WIDE_INT_MAX)
or by the format function itself. */
width = HOST_WIDE_INT_MAX;
}
else
width = -width;
}
}
else else
width = HOST_WIDE_INT_MIN; width = HOST_WIDE_INT_MIN;
} }
...@@ -1261,9 +1277,9 @@ format_floating (const conversion_spec &spec, int width, int prec) ...@@ -1261,9 +1277,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
res.range.min = 2 + (prec < 0 ? 6 : prec); res.range.min = 2 + (prec < 0 ? 6 : prec);
/* Compute the maximum just once. */ /* Compute the maximum just once. */
static const int f_max[] = { const int f_max[] = {
format_floating_max (double_type_node, 'f'), format_floating_max (double_type_node, 'f', prec),
format_floating_max (long_double_type_node, 'f') format_floating_max (long_double_type_node, 'f', prec)
}; };
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl]; res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl];
...@@ -1279,9 +1295,9 @@ format_floating (const conversion_spec &spec, int width, int prec) ...@@ -1279,9 +1295,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
res.range.min = 2 + (prec < 0 ? 6 : prec); res.range.min = 2 + (prec < 0 ? 6 : prec);
/* Compute the maximum just once. */ /* Compute the maximum just once. */
static const int g_max[] = { const int g_max[] = {
format_floating_max (double_type_node, 'g'), format_floating_max (double_type_node, 'g', prec),
format_floating_max (long_double_type_node, 'g') format_floating_max (long_double_type_node, 'g', prec)
}; };
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl]; res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl];
...@@ -2743,19 +2759,27 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -2743,19 +2759,27 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
{ {
dstsize = tree_to_uhwi (size); dstsize = tree_to_uhwi (size);
/* No object can be larger than SIZE_MAX bytes (half the address /* No object can be larger than SIZE_MAX bytes (half the address
space) on the target. This imposes a limit that's one byte space) on the target.
less than that.
The functions are defined only for output of at most INT_MAX The functions are defined only for output of at most INT_MAX
bytes. Specifying a bound in excess of that limit effectively bytes. Specifying a bound in excess of that limit effectively
defeats the bounds checking (and on some implementations such defeats the bounds checking (and on some implementations such
as Solaris cause the function to fail with EINVAL). */ as Solaris cause the function to fail with EINVAL). */
if (dstsize >= target_size_max () / 2) if (dstsize > target_size_max () / 2)
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_, {
"specified destination size %wu is too large", /* Avoid warning if -Wstringop-overflow is specified since
dstsize); it also warns for the same thing though only for the
checking built-ins. */
if ((idx_objsize == HOST_WIDE_INT_M1U
|| !warn_stringop_overflow))
warning_at (gimple_location (info.callstmt),
OPT_Wformat_length_,
"specified bound %wu exceeds maximum object size "
"%wu",
dstsize, target_size_max () / 2);
}
else if (dstsize > target_int_max ()) else if (dstsize > target_int_max ())
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_, warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
"specified destination size %wu exceeds %<INT_MAX %>", "specified bound %wu exceeds %<INT_MAX %>",
dstsize); dstsize);
} }
else if (TREE_CODE (size) == SSA_NAME) else if (TREE_CODE (size) == SSA_NAME)
...@@ -2800,10 +2824,15 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -2800,10 +2824,15 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
info.objsize = dstsize < objsize ? dstsize : objsize; info.objsize = dstsize < objsize ? dstsize : objsize;
if (info.bounded if (info.bounded
&& dstsize < target_size_max () / 2 && objsize < dstsize) && dstsize < target_size_max () / 2 && objsize < dstsize
/* Avoid warning if -Wstringop-overflow is specified since
it also warns for the same thing though only for the
checking built-ins. */
&& (idx_objsize == HOST_WIDE_INT_M1U
|| !warn_stringop_overflow))
{ {
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_, warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
"specified size %wu exceeds the size %wu " "specified bound %wu exceeds the size %wu "
"of the destination object", dstsize, objsize); "of the destination object", dstsize, objsize);
} }
} }
......
2016-12-07 Martin Sebor <msebor@redhat.com>
PR middle-end/77784
PR middle-end/78149
PR middle-end/78138
* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust expected diagnostic.
* g++.dg/ext/builtin-object-size3.C (bar): Same.
* g++.dg/ext/strncpy-chk1.C: Same.
* g++.dg/opt/memcpy1.C: Same.
* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
* gcc.c-torture/compile/pr55569.c: Disable -Wstringop-overflow.
* gcc.dg/Wobjsize-1.c: Adjust expected diagnostic.
* gcc.dg/attr-alloc_size.c: Same.
* gcc.dg/builtin-stringop-chk-1.c: Adjust expected diagnostic.
* gcc.dg/builtin-stringop-chk-2.c: Same.
* gcc.dg/builtin-stringop-chk-4.c: New test.
* gcc.dg/builtin-strncat-chk-1.c: Adjust expected diagnostic.
* gcc.dg/memcpy-2.c: Same.
* gcc.dg/pr40340-1.c: Same.
* gcc.dg/pr40340-2.c (main): Same.
* gcc.dg/pr40340-5.c (main): Same.
* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
* gcc.dg/torture/pr71132.c: Disable -Wstringop-overflow.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust text of expected
warning.
* gfortran.dg/char_length_3.f90: Prune expected warnings.
* gfortran.dg/pr38868.f: Add expected warnings.
2016-12-07 Michael Meissner <meissner@linux.vnet.ibm.com> 2016-12-07 Michael Meissner <meissner@linux.vnet.ibm.com>
PR target/72717 PR target/72717
......
...@@ -481,4 +481,4 @@ f4 (char *x, char **y, int z, char w[64]) ...@@ -481,4 +481,4 @@ f4 (char *x, char **y, int z, char w[64])
stpncpy (x, s3, sizeof (s3)); stpncpy (x, s3, sizeof (s3));
} }
/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */ /* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
...@@ -20,7 +20,7 @@ bar () ...@@ -20,7 +20,7 @@ bar ()
{ {
int *p = new int; int *p = new int;
int *q = new int[4]; int *q = new int[4];
MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1); // { dg-warning "will always overflow destination buffer" } MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1); // { dg-warning "writing" }
MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1); // { dg-warning "will always overflow destination buffer" } MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1); // { dg-warning "writing" }
baz (p, q); baz (p, q);
} }
...@@ -9,7 +9,7 @@ struct B { char z[50]; }; ...@@ -9,7 +9,7 @@ struct B { char z[50]; };
inline void inline void
foo (char *dest, const char *__restrict src, __SIZE_TYPE__ n) foo (char *dest, const char *__restrict src, __SIZE_TYPE__ n)
{ {
__builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0)); // { dg-warning "will always overflow" } __builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0)); // { dg-warning "specified bound 36 exceeds the size 35 of the destination" }
} }
void bar (const char *, int); void bar (const char *, int);
......
...@@ -59,7 +59,10 @@ namespace CS ...@@ -59,7 +59,10 @@ namespace CS
} }
uint8 Clip () uint8 Clip ()
{ {
__builtin_memcpy (this->OutP, InP, OutV * sizeof (csVector2)); // OutV is initialized to SIZE_MAX in the ctor above causing
// the multiplication below to produce a very large number
// in excess of the maximum possible object size (SIZE_MAX/2).
__builtin_memcpy (this->OutP, InP, OutV * sizeof (csVector2)); // { dg-warning "specified size \[0-9\]+ exceeds maximum object size" }
} }
}; };
} }
......
...@@ -713,4 +713,4 @@ f4 (char *x, char **y, int z, char w[64]) ...@@ -713,4 +713,4 @@ f4 (char *x, char **y, int z, char w[64])
return z; return z;
} }
// { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } // { dg-prune-output "\[\n\r\]*overflows\[\n\r\]*" }
// Test -Wsizeof-pointer-memaccess warnings. // Test -Wsizeof-pointer-memaccess warnings.
// { dg-do compile } // { dg-do compile }
// { dg-options "-Wall -Wno-sizeof-array-argument" } // { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" }
// Test just twice, once with -O0 non-fortified, once with -O2 fortified. // Test just twice, once with -O0 non-fortified, once with -O2 fortified,
// suppressing buffer overflow warnings.
// { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } } // { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } }
// { dg-skip-if "" { *-*-* } { "-flto" } { "" } } // { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
......
/* { dg-options "-ftree-vectorize" } */ /* { dg-options "-Wno-stringop-overflow -ftree-vectorize" } */
int *bar (void); int *bar (void);
void void
...@@ -6,6 +6,10 @@ foo (void) ...@@ -6,6 +6,10 @@ foo (void)
{ {
long x; long x;
int *y = bar (); int *y = bar ();
for (x = -1 / sizeof (int); x; --x, ++y)
*y = 0; /* The loop below may be optimized to a call to memset with a size
that's in excess of the maximum object size. This is diagnosed
by the -Wstringop-overflow option. */
for (x = -1 / sizeof (int); x; --x, ++y)
*y = 0;
} }
...@@ -10,6 +10,6 @@ int main(int argc, char **argv) ...@@ -10,6 +10,6 @@ int main(int argc, char **argv)
return 0; return 0;
} }
/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 6 } */ /* { dg-warning "writing" "" { target *-*-* } 6 } */
/* { dg-message "file included" "included" { target *-*-* } 0 } */ /* { dg-message "file included" "included" { target *-*-* } 0 } */
/* { dg-message "inlined from" "inlined" { target *-*-* } 0 } */ /* { dg-message "inlined from" "inlined" { target *-*-* } 0 } */
...@@ -22,15 +22,15 @@ test (void) ...@@ -22,15 +22,15 @@ test (void)
strcpy (p, "Hello"); strcpy (p, "Hello");
p = malloc1 (6); p = malloc1 (6);
strcpy (p, "Hello"); strcpy (p, "Hello");
strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6); p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6);
strcpy (p, "World"); strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
p = calloc1 (2, 5); p = calloc1 (2, 5);
strcpy (p, "World"); strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5); p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
strcpy (p, "World"); strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
} }
...@@ -8,7 +8,10 @@ ...@@ -8,7 +8,10 @@
extern void abort (void); extern void abort (void);
#include "../gcc.c-torture/execute/builtins/chk.h" #include "../gcc.c-torture/execute/builtins/chk.h"
#include <stdarg.h>
#define va_list __builtin_va_list
#define va_start __builtin_va_start
#define va_end __builtin_va_end
volatile void *vx; volatile void *vx;
char buf1[20]; char buf1[20];
...@@ -22,60 +25,61 @@ test (int arg, ...) ...@@ -22,60 +25,61 @@ test (int arg, ...)
char *p = &buf1[10], *q; char *p = &buf1[10], *q;
memcpy (&buf2[19], "ab", 1); memcpy (&buf2[19], "ab", 1);
memcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "memcpy" } */ memcpy (&buf2[19], "ab", 2); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy" } */
vx = mempcpy (&buf2[19], "ab", 1); vx = mempcpy (&buf2[19], "ab", 1);
vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "mempcpy" } */ vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "writing 2 " "mempcpy" } */
memmove (&buf2[18], &buf1[10], 2); memmove (&buf2[18], &buf1[10], 2);
memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "will always overflow" "memmove" } */ memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "writing 3 " "memmove" } */
memset (&buf2[16], 'a', 4); memset (&buf2[16], 'a', 4);
memset (&buf2[15], 'b', 6); /* { dg-warning "will always overflow" "memset" } */ memset (&buf2[15], 'b', 6); /* { dg-warning "writing 6 " "memset" } */
strcpy (&buf2[18], "a"); strcpy (&buf2[18], "a");
strcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcpy" } */ strcpy (&buf2[18], "ab"); /* { dg-warning "writing 3 " "strcpy" } */
vx = stpcpy (&buf2[18], "a"); vx = stpcpy (&buf2[18], "a");
vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "stpcpy" } */ vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "writing 3" "stpcpy" } */
strncpy (&buf2[18], "a", 2); strncpy (&buf2[18], "a", 2);
strncpy (&buf2[18], "a", 3); /* { dg-warning "will always overflow" "strncpy" } */ strncpy (&buf2[18], "a", 3); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "strncpy" } */
strncpy (&buf2[18], "abc", 2); strncpy (&buf2[18], "abc", 2);
strncpy (&buf2[18], "abc", 3); /* { dg-warning "will always overflow" "strncpy" } */ strncpy (&buf2[18], "abc", 3); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "strncpy" } */
memset (buf2, '\0', sizeof (buf2)); memset (buf2, '\0', sizeof (buf2));
strcat (&buf2[18], "a"); strcat (&buf2[18], "a");
memset (buf2, '\0', sizeof (buf2)); memset (buf2, '\0', sizeof (buf2));
strcat (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcat" } */ strcat (&buf2[18], "ab"); /* { dg-warning "writing 3 " "strcat" } */
sprintf (&buf2[18], "%s", buf1); sprintf (&buf2[18], "%s", buf1);
sprintf (&buf2[18], "%s", "a"); sprintf (&buf2[18], "%s", "a");
sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "will always overflow" "sprintf" } */ sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "writing 3 " "sprintf" } */
sprintf (&buf2[18], "a"); sprintf (&buf2[18], "a");
sprintf (&buf2[18], "ab"); /* { dg-warning "will always overflow" "sprintf" } */ sprintf (&buf2[18], "ab"); /* { dg-warning "writing 3 " "sprintf" } */
snprintf (&buf2[18], 2, "%d", x); snprintf (&buf2[18], 2, "%d", x);
/* N argument to snprintf is the size of the buffer. /* N argument to snprintf is the size of the buffer.
Although this particular call wouldn't overflow buf2, Although this particular call wouldn't overflow buf2,
incorrect buffer size was passed to it and therefore incorrect buffer size was passed to it and therefore
we want a warning and runtime failure. */ we want a warning and runtime failure. */
snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "will always overflow" "snprintf" } */ snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "snprintf" } */
va_start (ap, arg); va_start (ap, arg);
vsprintf (&buf2[18], "a", ap); vsprintf (&buf2[18], "a", ap);
va_end (ap); va_end (ap);
va_start (ap, arg); va_start (ap, arg);
vsprintf (&buf2[18], "ab", ap); /* { dg-warning "will always overflow" "vsprintf" } */ vsprintf (&buf2[18], "ab", ap); /* { dg-warning "writing 3" "vsprintf" } */
va_end (ap); va_end (ap);
va_start (ap, arg); va_start (ap, arg);
vsnprintf (&buf2[18], 2, "%s", ap); vsnprintf (&buf2[18], 2, "%s", ap);
va_end (ap); va_end (ap);
va_start (ap, arg); va_start (ap, arg);
/* See snprintf above. */ /* See snprintf above. */
vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "will always overflow" "vsnprintf" } */ vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" "vsnprintf" } */
va_end (ap); va_end (ap);
p = p + 10; p = p + 10;
memset (p, 'd', 0); memset (p, 'd', 0);
q = strcpy (p, ""); /* { dg-warning "will always overflow" "strcpy" } */ q = strcpy (p, ""); /* { dg-warning "writing 1 " "strcpy" } */
/* This invokes undefined behavior, since we are past the end of buf1. */ /* This invokes undefined behavior, since we are past the end of buf1. */
p = p + 10; p = p + 10;
memset (p, 'd', 1); /* { dg-warning "will always overflow" "memset" } */ memset (p, 'd', 1); /* { dg-warning "writing 1 " "memset" } */
memset (q, 'd', 0); memset (q, 'd', 0);
memset (q, 'd', 1); /* { dg-warning "will always overflow" "memset" } */ memset (q, 'd', 1); /* { dg-warning "writing 1 " "memset" } */
q = q - 10; q = q - 10;
memset (q, 'd', 10); memset (q, 'd', 10);
} }
...@@ -90,26 +94,26 @@ void ...@@ -90,26 +94,26 @@ void
test2 (const H h) test2 (const H h)
{ {
char c; char c;
strncpy (&c, str, 3); /* { dg-warning "will always overflow" "strncpy" } */ strncpy (&c, str, 3); /* { dg-warning "specified bound 3 exceeds the size 1 of the destination" "strncpy" } */
struct { char b[4]; } x; struct { char b[4]; } x;
sprintf (x.b, "%s", "ABCD"); /* { dg-warning "will always overflow" "sprintf" } */ sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */
unsigned int i; unsigned int i;
memcpy (&i, &h, sizeof (h)); /* { dg-warning "will always overflow" "memcpy" } */ memcpy (&i, &h, sizeof (h)); /* { dg-warning "writing 16 " "memcpy" } */
unsigned char buf[21]; unsigned char buf[21];
memset (buf + 16, 0, 8); /* { dg-warning "will always overflow" "memset" } */ memset (buf + 16, 0, 8); /* { dg-warning "writing 8 " "memset" } */
typedef struct { int i, j, k, l; } S; typedef struct { int i, j, k, l; } S;
S *s[3]; S *s[3];
memset (s, 0, sizeof (S) * 3); /* { dg-warning "will always overflow" "memset" } */ memset (s, 0, sizeof (S) * 3); /* { dg-warning "writing 48 " "memset" } */
struct T { char a[8]; char b[4]; char c[10]; } t; struct T { char a[8]; char b[4]; char c[10]; } t;
stpcpy (t.c,"Testing..."); /* { dg-warning "will always overflow" "stpcpy" } */ stpcpy (t.c,"Testing..."); /* { dg-warning "writing" "stpcpy" } */
char b1[7]; char b1[7];
char b2[4]; char b2[4];
memset (b1, 0, sizeof (b1)); memset (b1, 0, sizeof (b1));
memset (b2, 0, sizeof (b1)); /* { dg-warning "will always overflow" "memset" } */ memset (b2, 0, sizeof (b1)); /* { dg-warning "writing 7" "memset" } */
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
/* { dg-options "-O2 -ftrack-macro-expansion=0" } */ /* { dg-options "-O2 -ftrack-macro-expansion=0" } */
#include "../gcc.c-torture/execute/builtins/chk.h" #include "../gcc.c-torture/execute/builtins/chk.h"
void *bar (int); void *bar (int);
extern void *malloc (__SIZE_TYPE__); extern void *malloc (__SIZE_TYPE__);
...@@ -115,7 +115,7 @@ baz (const struct A *x, const unsigned char *z) ...@@ -115,7 +115,7 @@ baz (const struct A *x, const unsigned char *z)
else else
do do
{ {
memcpy (e, d, 513); /* { dg-warning "will always overflow" "memcpy" } */ memcpy (e, d, 513); /* { dg-warning "writing" "memcpy" } */
e += 4; e += 4;
} }
while (--h); while (--h);
......
/* Test exercising buffer overflow warnings emitted for raw memory and
string manipulation builtins involving ranges of sizes and strings
of varying lengths. */
/* { dg-do compile } */
/* { dg-options "-O2 -ftrack-macro-expansion=0" } */
#define INT_MAX __INT_MAX__
#define PTRDIFF_MAX __PTRDIFF_MAX__
#define SIZE_MAX __SIZE_MAX__
typedef __PTRDIFF_TYPE__ ptrdiff_t;
typedef __SIZE_TYPE__ size_t;
static const size_t ssize_max = SIZE_MAX / 2;
static const size_t size_max = SIZE_MAX;
extern signed char schar_val;
extern signed short sshrt_val;
extern signed int sint_val;
extern signed long slong_val;
extern unsigned char uchar_val;
extern unsigned short ushrt_val;
extern unsigned int uint_val;
extern unsigned long ulong_val;
#define memcpy(d, s, n) (memcpy ((d), (s), (n)), sink ((d)))
extern void* (memcpy)(void*, const void*, size_t);
#define mempcpy(d, s, n) (mempcpy ((d), (s), (n)), sink ((d)))
extern void* (mempcpy)(void*, const void*, size_t);
#define memset(d, c, n) (memset ((d), (c), (n)), sink ((d)))
extern void* (memset)(void*, int, size_t);
#define bzero(d, n) (bzero ((d), (n)), sink ((d)))
extern void (bzero)(void*, size_t);
#define strcat(d, s) (strcat ((d), (s)), sink ((d)))
extern char* (strcat)(char*, const char*);
#define strncat(d, s, n) (strncat ((d), (s), (n)), sink ((d)))
extern char* (strncat)(char*, const char*, size_t);
#define strcpy(d, s) (strcpy ((d), (s)), sink ((d)))
extern char* (strcpy)(char*, const char*);
#define strncpy(d, s, n) (strncpy ((d), (s), (n)), sink ((d)))
extern char* (strncpy)(char*, const char*, size_t);
void sink (void*);
/* Function to "generate" a random number each time it's called. Declared
(but not defined) and used to prevent GCC from making assumptions about
their values based on the variables uses in the tested expressions. */
size_t random_unsigned_value (void);
ptrdiff_t random_signed_value (void);
/* Return a random unsigned value between MIN and MAX. */
static inline size_t
unsigned_range (size_t min, size_t max)
{
const size_t val = random_unsigned_value ();
return val < min || max < val ? min : val;
}
/* Return a random signed value between MIN and MAX. */
static inline ptrdiff_t
signed_range (ptrdiff_t min, ptrdiff_t max)
{
const ptrdiff_t val = random_signed_value ();
return val < min || max < val ? min : val;
}
/* For brevity. */
#define UR(min, max) unsigned_range (min, max)
#define SR(min, max) signed_range (min, max)
/* UReturn a pointer to constant string whose length is at least MINLEN
and at most 10. */
static inline const char*
string_range (size_t minlen)
{
static const char str[] = "0123456789";
const size_t len = unsigned_range (minlen, sizeof str - 1);
switch (len)
{
case 10: return "0123456789";
case 9: return "012345678";
case 8: return "01234567";
case 7: return "0123456";
case 6: return "012345";
case 5: return "01234";
case 4: return "0123";
case 3: return "012";
case 2: return "01";
case 1: return "0";
case 0: return "";
}
}
#define S(minlen) string_range (minlen)
/* Test memcpy with a number of bytes bounded by a known range. */
void test_memcpy_range (void *d, const void *s)
{
char buf[5];
memcpy (buf, s, UR (0, 5));
memcpy (buf, s, UR (1, 5));
memcpy (buf, s, UR (2, 5));
memcpy (buf, s, UR (3, 5));
memcpy (buf, s, UR (4, 5));
memcpy (buf, s, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
memcpy (buf + 5, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" } */
memcpy (buf + size_max, s, UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */
memcpy (buf, s, UR (ssize_max, size_max)); /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
memcpy (buf, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
memcpy (buf, s, UR (size_max - 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
/* Exercise memcpy into a destination of unknown size with excessive
number of bytes. */
memcpy (d, s, UR (ssize_max, size_max));
memcpy (d, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
memcpy (buf, s, SR (-1, 1));
memcpy (buf, s, SR (-3, 2));
memcpy (buf, s, SR (-5, 3));
memcpy (buf, s, SR (-7, 4));
memcpy (buf, s, SR (-9, 5));
memcpy (buf, s, SR (-11, 6));
memcpy (d, s, SR (-1, 1));
memcpy (d, s, SR (-3, 2));
memcpy (d, s, SR (-5, 3));
memcpy (d, s, SR (-7, 4));
memcpy (d, s, SR (-9, 5));
memcpy (d, s, SR (-11, 6));
memcpy (buf, s, SR (-2, -1)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
memcpy (d, s, SR (-2, -1)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
/* Even though the following calls are bounded by the range of N's
type they must not cause a warning for obvious reasons. */
memcpy (buf, s, schar_val);
memcpy (buf, s, sshrt_val);
memcpy (buf, s, sint_val);
memcpy (buf, s, slong_val);
memcpy (buf, s, uchar_val);
memcpy (buf, s, ushrt_val);
memcpy (buf, s, uint_val);
memcpy (buf, s, ulong_val);
memcpy (buf, s, schar_val + 1);
memcpy (buf, s, sshrt_val + 2);
memcpy (buf, s, sint_val + 3);
memcpy (buf, s, slong_val + 4);
memcpy (d, s, uchar_val + 5);
memcpy (d, s, ushrt_val + 6);
memcpy (d, s, uint_val + 7);
memcpy (d, s, ulong_val + 8);
memcpy (d, s, schar_val);
memcpy (d, s, sshrt_val);
memcpy (d, s, sint_val);
memcpy (d, s, slong_val);
memcpy (d, s, uchar_val);
memcpy (d, s, ushrt_val);
memcpy (d, s, uint_val);
memcpy (d, s, ulong_val);
memcpy (d, s, schar_val + 1);
memcpy (d, s, sshrt_val + 2);
memcpy (d, s, sint_val + 3);
memcpy (d, s, slong_val + 4);
memcpy (d, s, uchar_val + 5);
memcpy (d, s, ushrt_val + 6);
memcpy (d, s, uint_val + 7);
memcpy (d, s, ulong_val + 8);
}
/* Test mempcpy with a number of bytes bounded by a known range. */
void test_mempcpy_range (void *d, const void *s)
{
char buf[5];
mempcpy (buf, s, UR (0, 5));
mempcpy (buf, s, UR (1, 5));
mempcpy (buf, s, UR (2, 5));
mempcpy (buf, s, UR (3, 5));
mempcpy (buf, s, UR (4, 5));
mempcpy (buf, s, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
mempcpy (buf, s, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
mempcpy (buf, s, UR (ssize_max, size_max)); /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
mempcpy (buf, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
mempcpy (buf, s, UR (size_max - 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
/* Exercise mempcpy into a destination of unknown size with excessive
number of bytes. */
mempcpy (d, s, UR (ssize_max, size_max));
mempcpy (d, s, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
}
/* Test memset with a number of bytes bounded by a known range. */
void test_memset_range (void *d)
{
char buf[5];
memset (buf, 0, UR (0, 5));
memset (buf, 0, UR (1, 5));
memset (buf, 0, UR (2, 5));
memset (buf, 0, UR (3, 5));
memset (buf, 0, UR (4, 5));
memset (buf, 0, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
memset (buf, 0, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
memset (buf, 0, UR (ssize_max, size_max)); /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
memset (buf, 0, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
memset (buf, 0, UR (size_max - 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
/* Exercise memset into a destination of unknown size with excessive
number of bytes. */
memset (d, 0, UR (ssize_max, size_max));
memset (d, 0, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
}
/* Test bzero with a number of bytes bounded by a known range. */
void test_bzero_range (void *d)
{
char buf[5];
bzero (buf, UR (0, 5));
bzero (buf, UR (1, 5));
bzero (buf, UR (2, 5));
bzero (buf, UR (3, 5));
bzero (buf, UR (4, 5));
bzero (buf, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
bzero (buf, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
bzero (buf, UR (ssize_max, size_max)); /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 overflows the destination" } */
bzero (buf, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
bzero (buf, UR (size_max - 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
/* Exercise bzero into a destination of unknown size with excessive
number of bytes. */
bzero (d, UR (ssize_max, size_max));
bzero (d, UR (ssize_max + 1, size_max)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
}
/* Test strcat with an argument referencing a non-constant string of
lengths in a known range. */
void test_strcat_range (void)
{
char buf[5] = "";
strcat (buf, S (0));
strcat (buf, S (1));
strcat (buf, S (2));
strcat (buf, S (3));
strcat (buf, S (4));
strcat (buf, S (5)); /* { dg-warning "writing 6 bytes into a region of size 5 " } */
{
/* The implementation of the warning isn't smart enough to determine
the length of the string in the buffer so it assumes it's empty
and issues the warning basically for the same cases as strcat. */
char buf2[5] = "12";
strcat (buf2, S (4)); /* { dg-warning "writing 5 bytes into a region of size 3" "strcat to a non-empty string" { xfail *-*-* } } */
}
}
/* Verify that strcpy with an unknown source string doesn't cause
warnings unless the destination has zero size. */
void test_strcpy (const char *src)
{
struct A { char a[2]; char b[3]; } a;
strcpy (a.a, src);
strcpy (a.a + 1, src);
/* There must be enough room in the destination for the terminating
nul, otherwise verify that a warning is issued.
The following works as expected with __builtin___strcpy_chk and
__builtin_object_size because they see that the offset is from
the a.a array. When optimization is enabled, it isn't detected
by __bultin_strcpy (when __builtin_object_size isn't called
explicitly) because by the time it's seen the offset has been
transformed to one from the beginning of the whole object, i.e.,
as if it had been written as (char*)&a + 2 . Then the destination
size is taken to be the rest of the whole object. It is detected
by __builtin_strcpy when optimization is not enabled because then
the &a.a + 2 expression is preserved. But without optimization
an ordinary call to strcpy isn't transformed to __builtin_strcpy
and so it can't be detected here (since the rest of the test
relies on optimization). */
strcpy (a.a + 2, src); /* { dg-warning "writing at least 1 byte into a region of size 0 " "strcpy into empty substring" { xfail *-*-* } } */
/* This does work. */
strcpy (a.a + 5, src); /* { dg-warning "writing at least 1 byte into a region of size 0 " } */
/* As does this. */
strcpy (a.a + 17, src); /* { dg-warning "writing at least 1 byte into a region of size 0 " } */
}
/* Test strcpy with a non-constant source string of length in a known
range. */
void test_strcpy_range (void)
{
char buf[5];
strcpy (buf, S (0));
strcpy (buf, S (1));
strcpy (buf, S (2));
strcpy (buf, S (4));
strcpy (buf, S (5)); /* { dg-warning "writing 6 bytes into a region of size 5 " } */
strcpy (buf, S (6)); /* { dg-warning "writing 7 bytes into a region of size 5 " } */
strcpy (buf, S (7)); /* { dg-warning "writing 8 bytes into a region of size 5 " } */
strcpy (buf, S (8)); /* { dg-warning "writing 9 bytes into a region of size 5 " } */
strcpy (buf, S (9)); /* { dg-warning "writing 10 bytes into a region of size 5 " } */
strcpy (buf, S (10)); /* { dg-warning "writing 11 bytes into a region of size 5 " } */
strcpy (buf + 5, S (0)); /* { dg-warning "writing 1 byte into a region of size 0 " } */
strcpy (buf + 17, S (0)); /* { dg-warning "writing 1 byte into a region of size 0 " } */
}
/* Test strncat with an argument referencing a non-constant string of
lengths in a known range. */
void test_strncat_range (void)
{
char buf[5] = "";
strncat (buf, S (0), 0);
strncat (buf, S (0), 1);
strncat (buf, S (0), 2);
strncat (buf, S (0), 3);
strncat (buf, S (0), 4);
strncat (buf + 5, S (0), 0);
strncat (buf + 5, S (0), 1); /* { dg-warning "specified bound 1 exceeds the size 0 of the destination " } */
strncat (buf + 5, S (1), 1); /* { dg-warning "specified bound 1 exceeds the size 0 of the destination " } */
/* Strncat always appends a terminating null after copying the N
characters so the following triggers a warning pointing out
that specifying sizeof(buf) as the upper bound may cause
the nul to overflow the destination. */
strncat (buf, S (0), 5); /* { dg-warning "specified bound 5 equals the size of the destination" } */
strncat (buf, S (0), 6); /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
strncat (buf, S (1), 0);
strncat (buf, S (1), 1);
strncat (buf, S (1), 2);
strncat (buf, S (1), 3);
strncat (buf, S (1), 4);
strncat (buf, S (1), 5); /* { dg-warning "specified bound 5 equals the size of the destination" } */
strncat (buf, S (1), 6); /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
strncat (buf, S (2), 6); /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
/* The following could just as well say "writing 6 bytes into a region
of size 5. Either would be correct and probably equally as clear
in this case. But when the length of the source string is not known
at all then the bound warning seems clearer. */
strncat (buf, S (5), 6); /* { dg-warning "specified bound 6 exceeds the size 5 of the destination " } */
strncat (buf, S (7), 6); /* { dg-warning "specified bound 6 exceeds the size 5 of the destination" } */
{
/* The implementation of the warning isn't smart enough to determine
the length of the string in the buffer so it assumes it's empty
and issues the warning basically for the same cases as strncpy. */
char buf2[5] = "12";
strncat (buf2, S (4), 4); /* { dg-warning "writing 5 bytes into a region of size 3" "strncat to a non-empty string" { xfail *-*-* } } */
}
}
/* Test strncat_chk with an argument referencing a non-constant string
of lengths in a known range. */
void test_strncat_chk_range (char *d)
{
char buf[5] = "";
#define strncat_chk(d, s, n) \
__builtin___strncat_chk ((d), (s), (n), __builtin_object_size (d, 1));
strncat_chk (buf, S (0), 1);
strncat_chk (buf, S (0), 2);
strncat_chk (buf, S (0), 3);
strncat_chk (buf, S (0), 4);
strncat_chk (buf, S (0), 5); /* { dg-warning "specified bound 5 equals the size of the destination " } */
strncat_chk (buf, S (5), 1);
strncat_chk (buf, S (5), 2);
strncat_chk (buf, S (5), 3);
strncat_chk (buf, S (5), 4);
strncat_chk (buf, S (5), 5); /* { dg-warning "specified bound 5 equals the size of the destination " } */
strncat_chk (buf, S (5), 10); /* { dg-warning "specified bound \[0-9\]+ exceeds the size 5 of the destination " } */
strncat_chk (d, S (5), size_max); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */
}
/* Test strncpy with a non-constant source string of length in a known
range and a constant number of bytes. */
void test_strncpy_string_range (char *d)
{
char buf[5];
strncpy (buf, S (0), 0);
strncpy (buf, S (0), 1);
strncpy (buf, S (0), 2);
strncpy (buf, S (0), 3);
strncpy (buf, S (0), 4);
strncpy (buf, S (0), 5);
strncpy (buf, S (0), 6); /* { dg-warning "writing 6 bytes into a region of size 5 " } */
strncpy (buf, S (6), 4);
strncpy (buf, S (7), 5);
strncpy (buf, S (8), 6); /* { dg-warning "writing 6 bytes into a region of size 5 " } */
strncpy (buf, S (1), ssize_max - 1); /* { dg-warning "writing \[0-9\]+ bytes into a region of size 5" } */
strncpy (buf, S (2), ssize_max); /* { dg-warning "writing \[0-9\]+ bytes into a region of size 5" } */
strncpy (buf, S (3), ssize_max + 1); /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
strncpy (buf, S (4), size_max); /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
/* Exercise strncpy into a destination of unknown size with a valid
and invalid constant number of bytes. */
strncpy (d, S (1), ssize_max - 1);
strncpy (d, S (2), ssize_max);
strncpy (d, S (3), ssize_max + 1); /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
strncpy (d, S (4), size_max); /* { dg-warning "specified size \[0-9\]+ exceeds maximum object size" } */
}
/* Test strncpy with a non-constant source string of length in a known
range and a non-constant number of bytes also in a known range. */
void test_strncpy_string_count_range (char *dst, const char *src)
{
char buf[5];
strncpy (buf, S (0), UR (0, 1));
strncpy (buf, S (0), UR (0, 2));
strncpy (buf, S (0), UR (0, 3));
strncpy (buf, S (0), UR (0, 4));
strncpy (buf, S (0), UR (0, 5));
strncpy (buf, S (0), UR (0, 6));
strncpy (buf, S (0), UR (1, 6));
strncpy (buf, S (0), UR (2, 6));
strncpy (buf, S (0), UR (3, 6));
strncpy (buf, S (0), UR (4, 6));
strncpy (buf, S (0), UR (5, 6));
strncpy (buf, S (9), UR (0, 1));
strncpy (buf, S (8), UR (0, 2));
strncpy (buf, S (7), UR (0, 3));
strncpy (buf, S (6), UR (0, 4));
strncpy (buf, S (8), UR (0, 5));
strncpy (buf, S (7), UR (0, 6));
strncpy (buf, S (6), UR (1, 6));
strncpy (buf, S (5), UR (2, 6));
strncpy (buf, S (9), UR (3, 6));
strncpy (buf, S (8), UR (4, 6));
strncpy (buf, S (7), UR (5, 6));
strncpy (buf, S (0), UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 " } */
strncpy (buf, S (1), UR (7, 8)); /* { dg-warning "writing between 7 and 8 bytes into a region of size 5 " } */
strncpy (buf, S (2), UR (ssize_max, ssize_max + 1)); /* { dg-warning "writing between \[0-9\]+ and \[0-9\]+ bytes into a region of size 5 " } */
strncpy (buf, S (2), UR (ssize_max + 1, ssize_max + 2)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
strncpy (buf + 5, S (0), UR (0, 1));
strncpy (buf + 5, S (1), UR (0, 1));
strncpy (buf + 5, S (0), UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 " } */
strncpy (buf + 5, S (1), UR (1, 2)); /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 " } */
strncpy (buf, src, UR (0, 1));
strncpy (buf, src, UR (0, 2));
strncpy (buf, src, UR (0, 3));
strncpy (buf, src, UR (0, 4));
strncpy (buf, src, UR (0, 5));
strncpy (buf, src, UR (0, 6));
strncpy (buf, src, UR (1, 6));
strncpy (buf, src, UR (2, 6));
strncpy (buf, src, UR (3, 6));
strncpy (buf, src, UR (4, 6));
strncpy (buf, src, UR (5, 6));
strncpy (buf, src, UR (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 " } */
/* Exercise strncpy into a destination of unknown size with a valid
and invalid constant number of bytes. */
strncpy (dst, S (0), UR (5, 6));
strncpy (dst, S (1), UR (6, 7));
strncpy (dst, S (2), UR (7, 8));
strncpy (dst, S (3), UR (ssize_max, ssize_max + 1));
strncpy (dst, S (4), UR (ssize_max + 1, ssize_max + 2)); /* { dg-warning "specified size between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
}
/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wstringop-overflow=1" } */
#define offsetof(type, mem) __builtin_offsetof (type, mem)
/* Return the number of bytes from member MEM of TYPE to the end
of object OBJ. */
#define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
typedef __SIZE_TYPE__ size_t;
extern void* memcpy (void*, const void*, size_t);
extern void* memset (void*, int, __SIZE_TYPE__);
struct A { char a, b; };
struct B { struct A a; char c, d; };
/* Function to call to "escape" pointers from tests below to prevent
GCC from assuming the values of the objects they point to stay
the unchanged. */
void escape (void*, ...);
/* Function to "generate" a random number each time it's called. Declared
(but not defined) and used to prevent GCC from making assumptions about
their values based on the variables uses in the tested expressions. */
size_t random_unsigned_value (void);
/* Return a random unsigned value between MIN and MAX. */
static inline size_t
range (size_t min, size_t max)
{
const size_t val = random_unsigned_value ();
return val < min || max < val ? min : val;
}
/* Verify that writing past the end of a local array is diagnosed. */
void test_memop_warn_local (const void *src)
{
size_t n;
n = range (8, 32);
struct A a[2];
memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src);
/* At -Wrawmem-overflow=1 the destination is considered to be
the whole array and its size is therefore sizeof a. */
memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src);
/* Verify the same as above but by writing into the first mmeber
of the first element of the array. */
memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
escape (a, src);
n = range (12, 32);
struct B b[2];
memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" } */
escape (b);
/* The following idiom of clearing multiple members of a struct is
used in a few places in the Linux kernel. Verify that a warning
is issued for it when it writes past the end of the array object. */
memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" } */
escape (b);
memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" } */
escape (b);
memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" } */
escape (b);
memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" } */
escape (b);
memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" } */
escape (b);
memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" } */
escape (b);
/* Same as above but clearing just elements of the second element
of the array. */
memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" } */
escape (b);
memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" } */
escape (b);
memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" } */
escape (b);
}
/* Verify that writing past the end of a dynamically allocated array
of known size is diagnosed. */
void test_memop_warn_alloc (const void *src)
{
size_t n;
n = range (8, 32);
struct A *a = __builtin_malloc (sizeof *a * 2);
memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
escape (a, src);
/* At -Wrawmem-overflow=1 the destination is considered to be
the whole array and its size is therefore sizeof a. */
memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
escape (a, src);
/* Verify the same as above but by writing into the first mmeber
of the first element of the array. */
memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
escape (a, src);
n = range (12, 32);
struct B *b = __builtin_malloc (sizeof *b * 2);
memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
/* The following idiom of clearing multiple members of a struct is
used in a few places in the Linux kernel. Verify that a warning
is issued for it when it writes past the end of the array object. */
memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
/* Same as above but clearing just elements of the second element
of the array. */
memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
escape (b);
}
void test_memop_nowarn (const void *src)
{
struct B b[2];
size_t n = range (sizeof b, 32);
/* Verify that clearing the whole array is not diagnosed regardless
of whether the expression pointing to its beginning is obtained
from the array itself or its first member(s). */
memcpy (b, src, n);
escape (b);
memcpy (&b[0], src, n);
escape (b);
memcpy (&b[0].a, src, n);
escape (b, src);
memcpy (&b[0].a.a, src, n);
escape (b, src);
/* Clearing multiple elements of an array of structs. */
memset (&b[0].a.b, 0, sizeof b - offsetof (struct B, a.b));
escape (b);
memset (&b->a.b, 0, sizeof b - offsetof (struct B, a.b));
escape (b);
memset (&b[0].c, 0, sizeof b - offsetof (struct B, c));
escape (b);
memset (&b->c, 0, sizeof b - offsetof (struct B, c));
escape (b);
memset (&b[0].d, 0, sizeof b - offsetof (struct B, d));
escape (b);
memset (&b->d, 0, sizeof b - offsetof (struct B, d));
escape (b);
/* Same as above but clearing just elements of the second element
of the array. */
memset (&b[1].a.b, 0, sizeof b[1] - offsetof (struct B, a.b));
escape (b);
memset (&b[1].c, 0, sizeof b[1] - offsetof (struct B, c));
escape (b);
memset (&b[1].d, 0, sizeof b[1] - offsetof (struct B, d));
escape (b);
}
/* The foollowing function could specify in its API that it takes
an array of exactly two elements, as shown below. Verify that
writing into both elements is not diagnosed. */
void test_memop_nowarn_arg (struct A[2], const void*);
void test_memop_nowarn_arg (struct A *a, const void *src)
{
memcpy (a, src, 2 * sizeof *a);
escape (a, src);
memcpy (a, src, range (2 * sizeof *a, 123));
escape (a, src);
}
struct C { char a[3], b; };
struct D { struct C c; char d, e; };
extern char* strncpy (char*, const char*, __SIZE_TYPE__);
void test_stringop_warn (void)
{
size_t n = range (2 * sizeof (struct D) + 1, 33);
struct C c[2];
/* Similarly, at -Wstringop-overflow=1 the destination is considered
to be the whole array and its size is therefore sizeof c. */
strncpy (c[0].a, "123", n); /* { dg-warning "writing between 13 and 33 bytes into a region of size 8 overflows the destination" } */
escape (c);
}
void test_stringop_nowarn (void)
{
struct D d[2];
strncpy (d[0].c.a, "123", range (sizeof d, 32));
escape (d);
}
/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -Wstringop-overflow=2" } */
#define offsetof(type, mem) __builtin_offsetof (type, mem)
/* Return the number of bytes from member MEM of TYPE to the end
of object OBJ. */
#define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
typedef __SIZE_TYPE__ size_t;
extern void* memcpy (void*, const void*, size_t);
extern void* memset (void*, int, __SIZE_TYPE__);
struct A { char a, b; };
struct B { struct A a; char c, d; };
/* Function to call to "escape" pointers from tests below to prevent
GCC from assuming the values of the objects they point to stay
the unchanged. */
void escape (void*, ...);
/* Function to "generate" a random number each time it's called. Declared
(but not defined) and used to prevent GCC from making assumptions about
their values based on the variables uses in the tested expressions. */
size_t random_unsigned_value (void);
/* Return a random unsigned value between MIN and MAX. */
static inline size_t
range (size_t min, size_t max)
{
const size_t val = random_unsigned_value ();
return val < min || max < val ? min : val;
}
void test_memop_warn_object (const void *src)
{
unsigned n = range (17, 29);
struct A a[2];
/* At both -Wstringop-overflow=2, like at 1, the destination of functions
that operate on raw memory is considered to be the whole array and its
size is therefore sizeof a. */
memcpy (&a[0], src, n); /* { dg-warning "writing between 17 and 29 bytes into a region of size 4 overflows the destination" } */
escape (a);
}
void test_memop_warn_subobject (const void *src)
{
unsigned n = range (17, 31);
struct B b[2];
/* At -Wrawmem-overflow=2 the destination is considered to be
the member sobobject of the first array element and its size
is therefore sizeof b[0].a. */
memcpy (&b[0].a, src, n); /* { dg-warning "writing between 17 and 31 bytes into a region of size 8 overflows the destination" } */
escape (b);
}
void test_memop_nowarn_subobject (void)
{
struct B b[2];
/* The following idiom of clearing multiple members of a struct
has been seen in a few places in the Linux kernel. Verify
that a warning is not issued for it. */
memset (&b[0].c, 0, sizeof b[0] - offsetof (struct B, c));
escape (b);
}
struct C { char a[3], b; };
struct D { struct C c; char d, e; };
extern char* strncpy (char*, const char*, __SIZE_TYPE__);
void test_stringop_warn_object (const char *str)
{
unsigned n = range (2 * sizeof (struct D), 32);
struct C c[2];
/* Similarly, at -Wstringop-overflow=2 the destination is considered
to be the array member of the first element of the array c and its
size is therefore sizeof c[0].a. */
strncpy (c[0].a, "123", n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
escape (c);
strncpy (c[0].a, str, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
escape (c);
}
void test_stringop_warn_subobject (const char *src)
{
unsigned n = range (2 * sizeof (struct D), 32);
struct D d[2];
/* Same as above. */
strncpy (d[0].c.a, "123", n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
escape (d);
strncpy (d[0].c.a, src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 3 overflows the destination" } */
escape (d);
}
...@@ -24,15 +24,15 @@ test (int arg, ...) ...@@ -24,15 +24,15 @@ test (int arg, ...)
*p = 0; *p = 0;
strncat (p, "abcdefghi", 10); strncat (p, "abcdefghi", 10);
*p = 0; *p = 0;
strncat (p, "abcdefghij", 10); /* { dg-warning "will always overflow" } */ strncat (p, "abcdefghij", 10); /* { dg-warning "writing 11 bytes into a region of size 10 overflows the destination" } */
*p = 0; *p = 0;
strncat (p, "abcdefgh", 11); strncat (p, "abcdefgh", 11);
*p = 0; *p = 0;
strncat (p, "abcdefghijkl", 11); /* { dg-warning "will always overflow" } */ strncat (p, "abcdefghijkl", 11); /* { dg-warning "specified bound 11 exceeds the size 10 of the destination" } */
*p = 0; *p = 0;
strncat (p, q, 9); strncat (p, q, 9);
*p = 0; *p = 0;
strncat (p, q, 10); /* { dg-warning "might overflow" } */ strncat (p, q, 10); /* { dg-warning "specified bound 10 equals the size of the destination" } */
*p = 0; *p = 0;
strncat (p, q, 11); /* { dg-warning "might overflow" } */ strncat (p, q, 11); /* { dg-warning "specified bound 11 exceeds the size 10 of the destination" } */
} }
...@@ -106,7 +106,7 @@ int ...@@ -106,7 +106,7 @@ int
foo8 () foo8 ()
{ {
char base[100]; char base[100];
memcpy ((void *)base, (const void *)pg0, 105); memcpy ((void *)base, (const void *)pg0, 105); /* { dg-warning "writing 105 bytes into a region of size 100" } */
return (int)(base[32]); return (int)(base[32]);
} }
......
...@@ -7,7 +7,7 @@ typedef __SIZE_TYPE__ size_t; ...@@ -7,7 +7,7 @@ typedef __SIZE_TYPE__ size_t;
extern inline __attribute__((gnu_inline, always_inline, artificial)) void * extern inline __attribute__((gnu_inline, always_inline, artificial)) void *
memcpy (void *__restrict dest, const void *__restrict src, size_t len) memcpy (void *__restrict dest, const void *__restrict src, size_t len)
{ {
return __builtin___memcpy_chk (dest, /* { dg-warning "will always overflow destination buffer" } */ return __builtin___memcpy_chk (dest, /* { dg-warning "writing" } */
src, len, __builtin_object_size (dest, 0)); src, len, __builtin_object_size (dest, 0));
} }
......
...@@ -20,5 +20,5 @@ main (void) ...@@ -20,5 +20,5 @@ main (void)
return 0; return 0;
} }
/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */ /* { dg-warning "writing" "" { target *-*-* } 10 } */
/* { dg-message "file included" "In file included" { target *-*-* } 0 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
...@@ -12,5 +12,5 @@ main (void) ...@@ -12,5 +12,5 @@ main (void)
return 0; return 0;
} }
/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */ /* { dg-warning "writing" "" { target *-*-* } 10 } */
/* { dg-message "file included" "In file included" { target *-*-* } 0 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
...@@ -13,5 +13,5 @@ main (void) ...@@ -13,5 +13,5 @@ main (void)
return 0; return 0;
} }
/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */ /* { dg-warning "writing" "" { target *-*-* } 10 } */
/* { dg-message "file included" "In file included" { target *-*-* } 0 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */
...@@ -710,4 +710,4 @@ f4 (char *x, char **y, int z, char w[64]) ...@@ -710,4 +710,4 @@ f4 (char *x, char **y, int z, char w[64])
return z; return z;
} }
/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */ /* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-additional-options "-Wno-stringop-overflow" } */
/* The loop below writes past the end of the global object a.
When the loop is transformed into a call to memcpy the buffer
overflow is detected and diagnosed by the -Wstringop-overflow
option enabled by default. */
typedef unsigned size_t; typedef unsigned size_t;
struct { struct {
......
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-std=c99 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */ /* { dg-options "-Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
/* { dg-require-effective-target int32plus } */ /* { dg-require-effective-target int32plus } */
/* When debugging, define LINE to the line number of the test case to exercise /* When debugging, define LINE to the line number of the test case to exercise
...@@ -1456,9 +1456,7 @@ void test_vsprintf_chk_int (__builtin_va_list va) ...@@ -1456,9 +1456,7 @@ void test_vsprintf_chk_int (__builtin_va_list va)
void test_snprintf_c_const (char *d) void test_snprintf_c_const (char *d)
{ {
T (-1, "%c", 0); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */ T (-1, "%c", 0); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
__builtin_snprintf (d, INT_MAX, "%c", 0); /* { dg-warning "specified destination size 2147483647 is too large" "ilp32" { target { ilp32 } } } */
/* Verify the full text of the diagnostic for just the distinct messages /* Verify the full text of the diagnostic for just the distinct messages
and use abbreviations in subsequent test cases. */ and use abbreviations in subsequent test cases. */
...@@ -1506,9 +1504,9 @@ void test_snprintf_chk_c_const (void) ...@@ -1506,9 +1504,9 @@ void test_snprintf_chk_c_const (void)
/* Verify that specifying a size of the destination buffer that's /* Verify that specifying a size of the destination buffer that's
bigger than its actual size (normally determined and passed to bigger than its actual size (normally determined and passed to
the function by __builtin_object_size) is diagnosed. */ the function by __builtin_object_size) is diagnosed. */
__builtin___snprintf_chk (buffer, 3, 0, 2, " "); /* { dg-warning "always overflow|specified size 3 exceeds the size 2 of the destination" } */ __builtin___snprintf_chk (buffer, 3, 0, 2, " "); /* { dg-warning "specified bound 3 exceeds the size 2 of the destination" } */
T (-1, "%c", 0); /* { dg-warning "specified destination size \[^ \]* is too large" } */ T (-1, "%c", 0); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
T (0, "%c", 0); T (0, "%c", 0);
T (0, "%c%c", 0, 0); T (0, "%c%c", 0, 0);
...@@ -1619,7 +1617,7 @@ void test_vsprintf_int (__builtin_va_list va) ...@@ -1619,7 +1617,7 @@ void test_vsprintf_int (__builtin_va_list va)
void test_vsnprintf_s (__builtin_va_list va) void test_vsnprintf_s (__builtin_va_list va)
{ {
T (-1, "%s"); /* { dg-warning "specified destination size \[^ \]* is too large" } */ T (-1, "%s"); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
T (0, "%s"); T (0, "%s");
T (1, "%s"); T (1, "%s");
...@@ -1642,9 +1640,9 @@ void test_vsnprintf_chk_s (__builtin_va_list va) ...@@ -1642,9 +1640,9 @@ void test_vsnprintf_chk_s (__builtin_va_list va)
/* Verify that specifying a size of the destination buffer that's /* Verify that specifying a size of the destination buffer that's
bigger than its actual size (normally determined and passed to bigger than its actual size (normally determined and passed to
the function by __builtin_object_size) is diagnosed. */ the function by __builtin_object_size) is diagnosed. */
__builtin___vsnprintf_chk (buffer, 123, 0, 122, "%-s", va); /* { dg-warning "always overflow|specified size 123 exceeds the size 122 of the destination object" } */ __builtin___vsnprintf_chk (buffer, 123, 0, 122, "%-s", va); /* { dg-warning "specified bound 123 exceeds the size 122 of the destination" } */
__builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va); /* { dg-warning "always overflow|destination size .\[0-9\]+. is too large" } */ __builtin___vsnprintf_chk (buffer, __SIZE_MAX__, 0, 2, "%-s", va); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
T (0, "%s"); T (0, "%s");
T (1, "%s"); T (1, "%s");
......
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-std=c99 -O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */ /* { dg-options "-O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
typedef __SIZE_TYPE__ size_t; typedef __SIZE_TYPE__ size_t;
...@@ -248,34 +248,34 @@ void test_too_large (char *d, int x, __builtin_va_list va) ...@@ -248,34 +248,34 @@ void test_too_large (char *d, int x, __builtin_va_list va)
const size_t imax = __INT_MAX__; const size_t imax = __INT_MAX__;
const size_t imax_p1 = imax + 1; const size_t imax_p1 = imax + 1;
__builtin_snprintf (d, imax, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */ __builtin_snprintf (d, imax, "%c", x);
__builtin_snprintf (d, imax_p1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */ __builtin_snprintf (d, imax_p1, "%c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */ /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
__builtin_vsnprintf (d, imax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */ __builtin_vsnprintf (d, imax, "%c", va);
__builtin_vsnprintf (d, imax_p1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */ __builtin_vsnprintf (d, imax_p1, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */ /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
__builtin___snprintf_chk (d, imax, 0, imax, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */ __builtin___snprintf_chk (d, imax, 0, imax, "%c", x);
__builtin___snprintf_chk (d, imax_p1, 0, imax_p1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */ __builtin___snprintf_chk (d, imax_p1, 0, imax_p1, "%c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
/* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX + 1" { target { ilp32 } } .-1 } */ /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
__builtin___vsnprintf_chk (d, imax, 0, imax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" "INT_MAX" { target ilp32 } } */ __builtin___vsnprintf_chk (d, imax, 0, imax, "%c", va);
__builtin___vsnprintf_chk (d, imax_p1, 0, imax_p1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */ __builtin___vsnprintf_chk (d, imax_p1, 0, imax_p1, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */ /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
const size_t ptrmax = __PTRDIFF_MAX__; const size_t ptrmax = __PTRDIFF_MAX__;
const size_t ptrmax_m1 = ptrmax - 1; const size_t ptrmax_m1 = ptrmax - 1;
__builtin_snprintf (d, ptrmax_m1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */ __builtin_snprintf (d, ptrmax_m1, "%c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
__builtin_snprintf (d, ptrmax, " %c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */ __builtin_snprintf (d, ptrmax, " %c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
__builtin_vsnprintf (d, ptrmax_m1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */ __builtin_vsnprintf (d, ptrmax_m1, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
__builtin_vsnprintf (d, ptrmax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */ __builtin_vsnprintf (d, ptrmax, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
__builtin___snprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */ __builtin___snprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
__builtin___snprintf_chk (d, ptrmax, 0, ptrmax, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */ __builtin___snprintf_chk (d, ptrmax, 0, ptrmax, "%c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
__builtin___vsnprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */ __builtin___vsnprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */
__builtin___vsnprintf_chk (d, ptrmax, 0, ptrmax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */ __builtin___vsnprintf_chk (d, ptrmax, 0, ptrmax, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
} }
...@@ -47,3 +47,6 @@ ...@@ -47,3 +47,6 @@
y(1) = 'hello world' y(1) = 'hello world'
end subroutine end subroutine
end end
! Remove -Wstringop-overflow warnings.
! { dg-prune-output "overflows the destination" }
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
ANER(1)='A ' ANER(1)='A '
ANER(2)=' ' ANER(2)=' '
LINE=' ' LINE=' '
LINE(78:80)='xyz' LINE(78:80)='xyz' ! { dg-warning "writing 3 bytes into a region of size 2" }
WRITE(*,'(A82)') "'"//LINE//"'" WRITE(*,'(A82)') "'"//LINE//"'"
END END
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment