mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
fc3f36f9a1
commit
ee92e7bafb
|
|
@ -1,3 +1,28 @@
|
|||
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>
|
||||
|
||||
PR target/72717
|
||||
|
|
|
|||
675
gcc/builtins.c
675
gcc/builtins.c
|
|
@ -67,7 +67,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "internal-fn.h"
|
||||
#include "case-cfn-macros.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
#include "intl.h"
|
||||
|
||||
struct target_builtins default_target_builtins;
|
||||
#if SWITCHABLE_TARGET
|
||||
|
|
@ -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_args (tree, tree, tree, rtx,
|
||||
machine_mode, int, tree);
|
||||
static rtx expand_builtin_strcat (tree, rtx);
|
||||
static rtx expand_builtin_strcpy (tree, rtx);
|
||||
static rtx expand_builtin_strcpy_args (tree, tree, rtx);
|
||||
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 builtin_memset_gen_str (void *, HOST_WIDE_INT, 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)
|
|||
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.
|
||||
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
|
||||
|
|
@ -3021,13 +3309,14 @@ expand_builtin_memcpy (tree exp, rtx target)
|
|||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
else
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
return expand_builtin_memcpy_args (dest, src, len, target, exp);
|
||||
}
|
||||
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
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.
|
||||
|
|
@ -3075,15 +3364,20 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
|
|||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
else
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
return expand_builtin_mempcpy_args (dest, src, len,
|
||||
target, mode, /*endp=*/ 1,
|
||||
exp);
|
||||
}
|
||||
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
/* Avoid expanding mempcpy into memcpy when the call is determined
|
||||
to overflow the buffer. This also prevents the same overflow
|
||||
from being diagnosed again when expanding memcpy. */
|
||||
if (!check_memop_sizes (exp, dest, 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.
|
||||
|
|
@ -3255,6 +3549,33 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
|
|||
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
|
||||
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
|
||||
|
|
@ -3263,13 +3584,20 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
|
|||
static rtx
|
||||
expand_builtin_strcpy (tree exp, rtx target)
|
||||
{
|
||||
if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
return expand_builtin_strcpy_args (dest, src, target);
|
||||
}
|
||||
return NULL_RTX;
|
||||
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);
|
||||
|
||||
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
|
||||
|
|
@ -3377,6 +3705,131 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
|
|||
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
|
||||
NULL_RTX if we failed the caller should emit a normal call. */
|
||||
|
||||
|
|
@ -3390,9 +3843,33 @@ expand_builtin_strncpy (tree exp, rtx target)
|
|||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
/* The number of bytes to write (not the maximum). */
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
/* The length of the source sequence. */
|
||||
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. */
|
||||
if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
|
||||
return NULL_RTX;
|
||||
|
|
@ -3480,13 +3957,14 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
|
|||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
else
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree val = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
return expand_builtin_memset_args (dest, val, len, target, mode, exp);
|
||||
}
|
||||
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree val = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
|
||||
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.
|
||||
|
|
@ -3667,20 +4145,21 @@ expand_builtin_memset_args (tree dest, tree val, tree len,
|
|||
static rtx
|
||||
expand_builtin_bzero (tree exp)
|
||||
{
|
||||
tree dest, size;
|
||||
location_t loc = EXPR_LOCATION (exp);
|
||||
|
||||
if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
|
||||
dest = CALL_EXPR_ARG (exp, 0);
|
||||
size = CALL_EXPR_ARG (exp, 1);
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree size = CALL_EXPR_ARG (exp, 1);
|
||||
|
||||
check_memop_sizes (exp, dest, size);
|
||||
|
||||
/* New argument list transforming bzero(ptr x, int y) to
|
||||
memset(ptr x, int 0, size_t y). This is done this way
|
||||
so that if it isn't expanded inline, we fallback to
|
||||
calling bzero instead of memset. */
|
||||
|
||||
location_t loc = EXPR_LOCATION (exp);
|
||||
|
||||
return expand_builtin_memset_args (dest, integer_zero_node,
|
||||
fold_convert_loc (loc,
|
||||
size_type_node, size),
|
||||
|
|
@ -6205,12 +6684,24 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
|||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRCAT:
|
||||
target = expand_builtin_strcat (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRCPY:
|
||||
target = expand_builtin_strcpy (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNCAT:
|
||||
target = expand_builtin_strncat (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNCPY:
|
||||
target = expand_builtin_strncpy (exp, target);
|
||||
if (target)
|
||||
|
|
@ -9052,22 +9543,22 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
|
|||
len = CALL_EXPR_ARG (exp, 2);
|
||||
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;
|
||||
|
||||
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
|
||||
validated because the size is not constant. */
|
||||
if (!sizes_ok && !integer_all_onesp (size) && tree_int_cst_lt (size, len))
|
||||
return NULL_RTX;
|
||||
|
||||
if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
|
||||
{
|
||||
warning_at (tree_nonartificial_location (exp),
|
||||
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
|
||||
mem{cpy,pcpy,move,set} is available. */
|
||||
switch (fcode)
|
||||
|
|
@ -9153,68 +9644,68 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
|
|||
static void
|
||||
maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
|
||||
{
|
||||
int is_strlen = 0;
|
||||
tree len, size;
|
||||
location_t loc = tree_nonartificial_location (exp);
|
||||
/* The source string. */
|
||||
tree srcstr = NULL_TREE;
|
||||
/* 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)
|
||||
{
|
||||
case BUILT_IN_STRCPY_CHK:
|
||||
case BUILT_IN_STPCPY_CHK:
|
||||
/* For __strcat_chk the warning will be emitted only if overflowing
|
||||
by at least strlen (dest) + 1 bytes. */
|
||||
case BUILT_IN_STRCAT_CHK:
|
||||
len = CALL_EXPR_ARG (exp, 1);
|
||||
size = CALL_EXPR_ARG (exp, 2);
|
||||
is_strlen = 1;
|
||||
srcstr = CALL_EXPR_ARG (exp, 1);
|
||||
objsize = CALL_EXPR_ARG (exp, 2);
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRCAT_CHK:
|
||||
/* For __strcat_chk the warning will be emitted only if overflowing
|
||||
by at least strlen (dest) + 1 bytes. */
|
||||
catstr = CALL_EXPR_ARG (exp, 0);
|
||||
srcstr = CALL_EXPR_ARG (exp, 1);
|
||||
objsize = CALL_EXPR_ARG (exp, 2);
|
||||
break;
|
||||
|
||||
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_STPNCPY_CHK:
|
||||
len = CALL_EXPR_ARG (exp, 2);
|
||||
size = CALL_EXPR_ARG (exp, 3);
|
||||
srcstr = CALL_EXPR_ARG (exp, 1);
|
||||
maxlen = CALL_EXPR_ARG (exp, 2);
|
||||
objsize = CALL_EXPR_ARG (exp, 3);
|
||||
break;
|
||||
|
||||
case BUILT_IN_SNPRINTF_CHK:
|
||||
case BUILT_IN_VSNPRINTF_CHK:
|
||||
len = CALL_EXPR_ARG (exp, 1);
|
||||
size = CALL_EXPR_ARG (exp, 3);
|
||||
maxlen = CALL_EXPR_ARG (exp, 1);
|
||||
objsize = CALL_EXPR_ARG (exp, 3);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
if (!len || !size)
|
||||
return;
|
||||
|
||||
if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
|
||||
return;
|
||||
|
||||
if (is_strlen)
|
||||
if (catstr && maxlen)
|
||||
{
|
||||
len = c_strlen (len, 1);
|
||||
if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
|
||||
/* Check __strncat_chk. There is no way to determine the length
|
||||
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;
|
||||
}
|
||||
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",
|
||||
exp, get_callee_fndecl (exp));
|
||||
check_sizes (OPT_Wstringop_overflow_, exp,
|
||||
/*size=*/NULL_TREE, maxlen, srcstr, objsize);
|
||||
}
|
||||
|
||||
/* 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)
|
|||
else
|
||||
return;
|
||||
|
||||
if (! tree_int_cst_lt (len, size))
|
||||
warning_at (tree_nonartificial_location (exp),
|
||||
0, "%Kcall to %D will always overflow destination buffer",
|
||||
exp, get_callee_fndecl (exp));
|
||||
/* Add one for the terminating nul. */
|
||||
len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
|
||||
check_sizes (OPT_Wstringop_overflow_,
|
||||
exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
|
||||
}
|
||||
|
||||
/* Emit warning if a free is called with address of a variable. */
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
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>
|
||||
|
||||
* c-attribs.c (asan odr indicator): New attribute.
|
||||
|
|
|
|||
|
|
@ -684,6 +684,16 @@ Wsizeof-array-argument
|
|||
C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
|
||||
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
|
||||
C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
|
||||
Warn about functions which might be candidates for format attributes.
|
||||
|
|
|
|||
|
|
@ -304,6 +304,7 @@ Objective-C and Objective-C++ Dialects}.
|
|||
-Wsizeof-pointer-memaccess -Wsizeof-array-argument @gol
|
||||
-Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @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-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
|
||||
-Wmissing-format-attribute -Wsubobject-linkage @gol
|
||||
|
|
@ -4936,6 +4937,86 @@ comparisons, so this warning level gives a very large number of
|
|||
false positives.
|
||||
@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{]}
|
||||
@opindex Wsuggest-attribute=
|
||||
@opindex Wno-suggest-attribute=
|
||||
|
|
|
|||
|
|
@ -774,7 +774,23 @@ get_width_and_precision (const conversion_spec &spec,
|
|||
if (spec.star_width)
|
||||
{
|
||||
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
|
||||
width = HOST_WIDE_INT_MIN;
|
||||
}
|
||||
|
|
@ -1261,9 +1277,9 @@ format_floating (const conversion_spec &spec, int width, int prec)
|
|||
res.range.min = 2 + (prec < 0 ? 6 : prec);
|
||||
|
||||
/* Compute the maximum just once. */
|
||||
static const int f_max[] = {
|
||||
format_floating_max (double_type_node, 'f'),
|
||||
format_floating_max (long_double_type_node, 'f')
|
||||
const int f_max[] = {
|
||||
format_floating_max (double_type_node, 'f', prec),
|
||||
format_floating_max (long_double_type_node, 'f', prec)
|
||||
};
|
||||
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)
|
|||
res.range.min = 2 + (prec < 0 ? 6 : prec);
|
||||
|
||||
/* Compute the maximum just once. */
|
||||
static const int g_max[] = {
|
||||
format_floating_max (double_type_node, 'g'),
|
||||
format_floating_max (long_double_type_node, 'g')
|
||||
const int g_max[] = {
|
||||
format_floating_max (double_type_node, 'g', prec),
|
||||
format_floating_max (long_double_type_node, 'g', prec)
|
||||
};
|
||||
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)
|
|||
{
|
||||
dstsize = tree_to_uhwi (size);
|
||||
/* No object can be larger than SIZE_MAX bytes (half the address
|
||||
space) on the target. This imposes a limit that's one byte
|
||||
less than that.
|
||||
space) on the target.
|
||||
The functions are defined only for output of at most INT_MAX
|
||||
bytes. Specifying a bound in excess of that limit effectively
|
||||
defeats the bounds checking (and on some implementations such
|
||||
as Solaris cause the function to fail with EINVAL). */
|
||||
if (dstsize >= target_size_max () / 2)
|
||||
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
|
||||
"specified destination size %wu is too large",
|
||||
dstsize);
|
||||
if (dstsize > target_size_max () / 2)
|
||||
{
|
||||
/* Avoid warning if -Wstringop-overflow is specified since
|
||||
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 ())
|
||||
warning_at (gimple_location (info.callstmt), OPT_Wformat_length_,
|
||||
"specified destination size %wu exceeds %<INT_MAX %>",
|
||||
"specified bound %wu exceeds %<INT_MAX %>",
|
||||
dstsize);
|
||||
}
|
||||
else if (TREE_CODE (size) == SSA_NAME)
|
||||
|
|
@ -2800,10 +2824,15 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
info.objsize = dstsize < objsize ? dstsize : objsize;
|
||||
|
||||
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_,
|
||||
"specified size %wu exceeds the size %wu "
|
||||
"specified bound %wu exceeds the size %wu "
|
||||
"of the destination object", dstsize, objsize);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,32 @@
|
|||
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>
|
||||
|
||||
PR target/72717
|
||||
|
|
|
|||
|
|
@ -481,4 +481,4 @@ f4 (char *x, char **y, int z, char w[64])
|
|||
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 ()
|
|||
{
|
||||
int *p = new int;
|
||||
int *q = new int[4];
|
||||
MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1); // { dg-warning "will always overflow destination buffer" }
|
||||
MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * 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 "writing" }
|
||||
baz (p, q);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct B { char z[50]; };
|
|||
inline void
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,10 @@ namespace CS
|
|||
}
|
||||
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])
|
|||
return z;
|
||||
}
|
||||
|
||||
// { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" }
|
||||
// { dg-prune-output "\[\n\r\]*overflows\[\n\r\]*" }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// Test -Wsizeof-pointer-memaccess warnings.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-Wall -Wno-sizeof-array-argument" }
|
||||
// Test just twice, once with -O0 non-fortified, once with -O2 fortified.
|
||||
// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" }
|
||||
// Test just twice, once with -O0 non-fortified, once with -O2 fortified,
|
||||
// suppressing buffer overflow warnings.
|
||||
// { dg-skip-if "" { *-*-* } { "*" } { "-O0" "-O2" } }
|
||||
// { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* { dg-options "-ftree-vectorize" } */
|
||||
/* { dg-options "-Wno-stringop-overflow -ftree-vectorize" } */
|
||||
int *bar (void);
|
||||
|
||||
void
|
||||
|
|
@ -6,6 +6,10 @@ foo (void)
|
|||
{
|
||||
long x;
|
||||
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)
|
|||
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 "inlined from" "inlined" { target *-*-* } 0 } */
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@ test (void)
|
|||
strcpy (p, "Hello");
|
||||
p = malloc1 (6);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 @@
|
|||
extern void abort (void);
|
||||
|
||||
#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;
|
||||
char buf1[20];
|
||||
|
|
@ -22,60 +25,61 @@ test (int arg, ...)
|
|||
char *p = &buf1[10], *q;
|
||||
|
||||
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", 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], 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[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], "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], "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", 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", 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));
|
||||
strcat (&buf2[18], "a");
|
||||
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", "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], "ab"); /* { dg-warning "will always overflow" "sprintf" } */
|
||||
sprintf (&buf2[18], "ab"); /* { dg-warning "writing 3 " "sprintf" } */
|
||||
snprintf (&buf2[18], 2, "%d", x);
|
||||
/* N argument to snprintf is the size of the buffer.
|
||||
Although this particular call wouldn't overflow buf2,
|
||||
incorrect buffer size was passed to it and therefore
|
||||
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);
|
||||
vsprintf (&buf2[18], "a", ap);
|
||||
va_end (ap);
|
||||
|
||||
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_start (ap, arg);
|
||||
vsnprintf (&buf2[18], 2, "%s", ap);
|
||||
va_end (ap);
|
||||
va_start (ap, arg);
|
||||
/* 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);
|
||||
|
||||
p = p + 10;
|
||||
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. */
|
||||
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', 1); /* { dg-warning "will always overflow" "memset" } */
|
||||
memset (q, 'd', 1); /* { dg-warning "writing 1 " "memset" } */
|
||||
q = q - 10;
|
||||
memset (q, 'd', 10);
|
||||
}
|
||||
|
|
@ -90,26 +94,26 @@ void
|
|||
test2 (const H h)
|
||||
{
|
||||
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;
|
||||
sprintf (x.b, "%s", "ABCD"); /* { dg-warning "will always overflow" "sprintf" } */
|
||||
sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */
|
||||
|
||||
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];
|
||||
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;
|
||||
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;
|
||||
stpcpy (t.c,"Testing..."); /* { dg-warning "will always overflow" "stpcpy" } */
|
||||
stpcpy (t.c,"Testing..."); /* { dg-warning "writing" "stpcpy" } */
|
||||
|
||||
char b1[7];
|
||||
char b2[4];
|
||||
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 @@
|
|||
/* { dg-options "-O2 -ftrack-macro-expansion=0" } */
|
||||
|
||||
#include "../gcc.c-torture/execute/builtins/chk.h"
|
||||
|
||||
|
||||
void *bar (int);
|
||||
extern void *malloc (__SIZE_TYPE__);
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ baz (const struct A *x, const unsigned char *z)
|
|||
else
|
||||
do
|
||||
{
|
||||
memcpy (e, d, 513); /* { dg-warning "will always overflow" "memcpy" } */
|
||||
memcpy (e, d, 513); /* { dg-warning "writing" "memcpy" } */
|
||||
e += 4;
|
||||
}
|
||||
while (--h);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,525 @@
|
|||
/* 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" } */
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
/* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/* 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, ...)
|
|||
*p = 0;
|
||||
strncat (p, "abcdefghi", 10);
|
||||
*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;
|
||||
strncat (p, "abcdefgh", 11);
|
||||
*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;
|
||||
strncat (p, q, 9);
|
||||
*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;
|
||||
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
|
|||
foo8 ()
|
||||
{
|
||||
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]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ typedef __SIZE_TYPE__ size_t;
|
|||
extern inline __attribute__((gnu_inline, always_inline, artificial)) void *
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,5 +20,5 @@ main (void)
|
|||
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 } */
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ main (void)
|
|||
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 } */
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ main (void)
|
|||
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 } */
|
||||
|
|
|
|||
|
|
@ -710,4 +710,4 @@ f4 (char *x, char **y, int z, char w[64])
|
|||
return z;
|
||||
}
|
||||
|
||||
/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */
|
||||
/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
/* { 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;
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* { 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 } */
|
||||
|
||||
/* 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)
|
|||
|
||||
void test_snprintf_c_const (char *d)
|
||||
{
|
||||
T (-1, "%c", 0); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
|
||||
__builtin_snprintf (d, INT_MAX, "%c", 0); /* { dg-warning "specified destination size 2147483647 is too large" "ilp32" { target { ilp32 } } } */
|
||||
T (-1, "%c", 0); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
|
||||
|
||||
/* Verify the full text of the diagnostic for just the distinct messages
|
||||
and use abbreviations in subsequent test cases. */
|
||||
|
|
@ -1506,9 +1504,9 @@ void test_snprintf_chk_c_const (void)
|
|||
/* Verify that specifying a size of the destination buffer that's
|
||||
bigger than its actual size (normally determined and passed to
|
||||
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%c", 0, 0);
|
||||
|
|
@ -1619,7 +1617,7 @@ void test_vsprintf_int (__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 (1, "%s");
|
||||
|
|
@ -1642,9 +1640,9 @@ void test_vsnprintf_chk_s (__builtin_va_list va)
|
|||
/* Verify that specifying a size of the destination buffer that's
|
||||
bigger than its actual size (normally determined and passed to
|
||||
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 (1, "%s");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* { 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;
|
||||
|
||||
|
|
@ -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_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_p1, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
__builtin_snprintf (d, imax, "%c", x);
|
||||
__builtin_snprintf (d, imax_p1, "%c", x); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
|
||||
/* { 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_p1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
__builtin_vsnprintf (d, imax, "%c", va);
|
||||
__builtin_vsnprintf (d, imax_p1, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
|
||||
/* { 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_p1, 0, imax_p1, "%c", x); /* { dg-warning "specified destination size \[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 } */
|
||||
__builtin___snprintf_chk (d, imax, 0, imax, "%c", x);
|
||||
__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 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_p1, 0, imax_p1, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ exceeds .INT_MAX." "INT_MAX + 1" { target lp64 } } */
|
||||
/* { dg-warning "specified destination size \[0-9\]+ is too large" "" { target { ilp32 } } .-1 } */
|
||||
__builtin___vsnprintf_chk (d, imax, 0, imax, "%c", va);
|
||||
__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 bound \[0-9\]+ exceeds maximum object size" "INT_MAX + 1" { target { ilp32 } } .-1 } */
|
||||
|
||||
const size_t ptrmax = __PTRDIFF_MAX__;
|
||||
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, " %c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
__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 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, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
__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 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, 0, ptrmax, "%c", x); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
__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 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, 0, ptrmax, "%c", va); /* { dg-warning "specified destination size \[0-9\]+ is too large" } */
|
||||
__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 bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,3 +47,6 @@
|
|||
y(1) = 'hello world'
|
||||
end subroutine
|
||||
end
|
||||
|
||||
! Remove -Wstringop-overflow warnings.
|
||||
! { dg-prune-output "overflows the destination" }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
ANER(1)='A '
|
||||
ANER(2)=' '
|
||||
LINE=' '
|
||||
LINE(78:80)='xyz'
|
||||
LINE(78:80)='xyz' ! { dg-warning "writing 3 bytes into a region of size 2" }
|
||||
WRITE(*,'(A82)') "'"//LINE//"'"
|
||||
END
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue