PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk
gcc/ChangeLog:
2016-10-27 Martin Sebor <msebor@redhat.com>
PR c/53562
* builtins.c (check_sizes): New function.
(expand_builtin_memcpy): Call it.
(expand_builtin_mempcpy): Same.
(expand_builtin_strcpy): Same.
(expand_builtin_memset): Same.
(expand_builtin_bzero): Same.
(expand_builtin_memory_chk): Same.
(maybe_emit_sprintf_chk_warning): Same.
* doc/invoke.texi (Warning Options): Document -Wstringop-overflow.
gcc/c-family/ChangeLog:
2016-10-27 Martin Sebor <msebor@redhat.com>
PR c/53562
* c.opt (-Wstringop-overflow): New option.
gcc/testsuite/ChangeLog:
2016-10-27 Martin Sebor <msebor@redhat.com>
PR c/53562
* gcc.dg/builtin-stringop-chk-4.c: New test.
* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust.
* g++.dg/ext/builtin-object-size3.C: Same.
* g++.dg/ext/strncpy-chk1.C: Same.
* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
* gcc.c-torture/execute/builtins/lib/chk.c: Same.
* gcc.c-torture/execute/builtins/mempcpy-chk.c: Same.
* gcc.dg/Wobjsize-1.c: Same.
* gcc.dg/attr-alloc_size.c (test): Same.
* gcc.dg/builtin-object-size-1.c: Same.
* gcc.dg/builtin-object-size-5.c: Same.
* gcc.dg/builtin-stringop-chk-1.c: Same.
* gcc.dg/builtin-stringop-chk-2.c: Same.
* gcc.dg/builtin-strncat-chk-1.c: Same.
* gcc.dg/memcpy-2.c: Same.
* gcc.dg/pr40340-1.c: Same.
* gcc.dg/pr40340-2.c: Same.
* gcc.dg/pr40340-5.c: Same.
* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
@@ -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
@@ -2967,6 +2967,133 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
return dest_addr;
}
+/* 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).
+ N is the user-supplied length of the source sequence (such as in
+ strncpy(d, s, N).
+ LEN is the length of the source sequence (such as in strcpy(d, s),
+ LEN = strlen(s)).
+ 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 verfified as safe from buffer overflow
+ the function returns EXP. Otherwise, if the call is determined to
+ overflow the function returns error_mark_node. Otherwise, if
+ the call cannot be verified, the function returns NULL. */
+
+static tree
+check_sizes (tree exp, tree size, tree n, tree len, tree objsize)
+{
+ tree minlen = NULL_TREE;
+ tree maxlen = NULL_TREE;
+
+ if (!size)
+ size = objsize;
+
+ if (!len)
+ len = size_zero_node;
+
+ if (tree_fits_uhwi_p (len))
+ minlen = maxlen = len;
+ else if (TREE_CODE (len) == SSA_NAME)
+ {
+ wide_int min, max;
+ enum value_range_type range_type = get_range_info (len, &min, &max);
+
+ if (range_type == VR_RANGE)
+ {
+ /* Interpret the bounds in the variable's type. */
+ minlen = wide_int_to_tree (TREE_TYPE (len), min);
+ maxlen = wide_int_to_tree (TREE_TYPE (len), max);
+ }
+ else if (range_type == VR_ANTI_RANGE)
+ {
+ min = min - 1;
+ max = max + 1;
+ minlen = wide_int_to_tree (TREE_TYPE (len), min);
+ maxlen = wide_int_to_tree (TREE_TYPE (len), max);
+ }
+ }
+
+ /* The SIZE is known if it's non-null, constant, and in range
+ of unsigned HOST_WIDE_INT. */
+ bool knownsize = size && tree_fits_uhwi_p (size);
+ if (minlen || !knownsize || integer_all_onesp (size))
+ {
+ if ((!knownsize &&
+ minlen && tree_int_cst_lt (TYPE_MAX_VALUE (ssizetype), minlen))
+ || (knownsize
+ && (tree_int_cst_lt (size, minlen))))
+ {
+ unsigned HOST_WIDE_INT uwilen = tree_to_uhwi (minlen);
+
+ location_t loc = tree_nonartificial_location (exp);
+
+ if (!knownsize)
+ warning_at (loc, OPT_Wstringop_overflow,
+ (minlen == maxlen
+ ? G_("%K%D writing %wu bytes overflows "
+ "the destination")
+ : G_("%K%D writing between %wu and %wu bytes "
+ "overflows the destination")),
+ exp, get_callee_fndecl (exp), uwilen,
+ tree_to_uhwi (maxlen));
+ else if (minlen == maxlen)
+ warning_at (loc, OPT_Wstringop_overflow,
+ (uwilen == 1
+ ? G_("%K%D writing %wu byte into a region "
+ "of size %wu overflows the destination")
+ : G_("%K%D writing %wu bytes into a region "
+ "of size %wu overflows the destination")),
+ exp, get_callee_fndecl (exp), uwilen,
+ tree_to_uhwi (size));
+ else
+ warning_at (loc, OPT_Wstringop_overflow,
+ (ubvflow
+ ? G_("%K%D writing between %wu and %wu bytes "
+ "into a region of size %wu may overflow "
+ "the destination")
+ : G_("%K%D writing between %wu and %wu bytes "
+ "into a region of size %wu overflows "
+ "the destination")),
+ exp, get_callee_fndecl (exp), uwilen,
+ tree_to_uhwi (maxlen), tree_to_uhwi (size));
+
+ /* Return error when an overflow has been detected. */
+ return error_mark_node;
+ }
+
+ if (!knownsize)
+ return error_mark_node;
+ }
+
+ if (size == objsize && n)
+ size = n;
+
+ if (size && objsize && tree_fits_uhwi_p (size)
+ && tree_int_cst_lt (objsize, size))
+ {
+ location_t loc = tree_nonartificial_location (exp);
+
+ warning_at (loc, OPT_Wstringop_overflow,
+ "specified size %wu exceeds the size %wu "
+ "of the destination object",
+ tree_to_uhwi (size), tree_to_uhwi (objsize));
+ }
+
+ return NULL_TREE;
+}
+
/* 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
@@ -2983,6 +3110,14 @@ expand_builtin_memcpy (tree exp, rtx target)
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
tree len = CALL_EXPR_ARG (exp, 2);
+
+ tree objsize = NULL_TREE;
+ unsigned HOST_WIDE_INT uhwisize;
+ if (compute_builtin_object_size (dest, 1, &uhwisize))
+ objsize = build_int_cst (sizetype, uhwisize);
+
+ check_sizes (exp, NULL_TREE, NULL_TREE, len, objsize);
+
return expand_builtin_memcpy_args (dest, src, len, target, exp);
}
}
@@ -3037,10 +3172,22 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
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 destsize = NULL_TREE;
+ unsigned HOST_WIDE_INT objsize;
+ if (compute_builtin_object_size (dest, 1, &objsize))
+ destsize = build_int_cst (sizetype, objsize);
+
+ /* 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. */
+ tree ret = check_sizes (exp, NULL_TREE, NULL_TREE, len, destsize);
+ if (ret != error_mark_node)
+ return expand_builtin_mempcpy_args (dest, src, len,
+ target, mode, /*endp=*/ 1,
+ exp);
}
+ return NULL_RTX;
}
/* Expand an instrumented call EXP to the mempcpy builtin.
@@ -3224,6 +3371,30 @@ expand_builtin_strcpy (tree exp, rtx target)
{
tree dest = CALL_EXPR_ARG (exp, 0);
tree src = CALL_EXPR_ARG (exp, 1);
+
+ /* 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. At a (future) stricter warning setting the longest length
+ should be used instead. */
+ if (lenrange[0])
+ {
+ /* Try to determine the size of the destination object into
+ which the source is being copied. */
+ tree destsize = NULL_TREE;
+ unsigned HOST_WIDE_INT objsize;
+ if (compute_builtin_object_size (dest, 1, &objsize))
+ destsize = build_int_cst (sizetype, objsize);
+
+ /* Add one for the terminating nul. */
+ tree len = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+ size_one_node);
+ check_sizes (exp, NULL_TREE, NULL_TREE, len, destsize);
+ }
+
return expand_builtin_strcpy_args (dest, src, target);
}
return NULL_RTX;
@@ -3442,6 +3613,15 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
tree dest = CALL_EXPR_ARG (exp, 0);
tree val = CALL_EXPR_ARG (exp, 1);
tree len = CALL_EXPR_ARG (exp, 2);
+
+ /* Try to determine the size of the destination object. */
+ tree objsize = NULL_TREE;
+ unsigned HOST_WIDE_INT uhwisize;
+ if (compute_builtin_object_size (dest, 1, &uhwisize))
+ objsize = build_int_cst (sizetype, uhwisize);
+
+ check_sizes (exp, NULL_TREE, NULL_TREE, len, objsize);
+
return expand_builtin_memset_args (dest, val, len, target, mode, exp);
}
}
@@ -3633,6 +3813,14 @@ expand_builtin_bzero (tree exp)
dest = CALL_EXPR_ARG (exp, 0);
size = CALL_EXPR_ARG (exp, 1);
+ /* Try to determine the size of the destination object. */
+ tree objsize = NULL_TREE;
+ unsigned HOST_WIDE_INT uhwisize;
+ if (compute_builtin_object_size (dest, 1, &uhwisize))
+ objsize = build_int_cst (sizetype, uhwisize);
+
+ check_sizes (exp, NULL_TREE, NULL_TREE, size, objsize);
+
/* 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
@@ -9212,22 +9408,15 @@ 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))
- return NULL_RTX;
-
- if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
+ if (tree ret = check_sizes (exp, NULL_TREE, NULL_TREE, len, size))
{
- tree fn;
-
- 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;
- }
+ /* 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 (ret == error_mark_node)
+ 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)
@@ -9313,68 +9502,96 @@ 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 length of the source sequence of the memory operation, and
+ the size of the destination object. */
+ tree srclen = NULL_TREE;
+ tree objsize = NULL_TREE;
+ /* The length of the sequence that the source sequence is being
+ concatenated with (as with __strcat_chk) or null if it isn't. */
+ tree catlen = 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. */
+ srclen = CALL_EXPR_ARG (exp, 1);
+ objsize = CALL_EXPR_ARG (exp, 2);
+ break;
+
case BUILT_IN_STRCAT_CHK:
- len = CALL_EXPR_ARG (exp, 1);
- size = CALL_EXPR_ARG (exp, 2);
- is_strlen = 1;
+ /* For __strcat_chk the warning will be emitted only if overflowing
+ by at least strlen (dest) + 1 bytes. */
+ catlen = CALL_EXPR_ARG (exp, 0);
+ srclen = CALL_EXPR_ARG (exp, 1);
+ objsize = CALL_EXPR_ARG (exp, 2);
break;
+
case BUILT_IN_STRNCAT_CHK:
+ catlen = CALL_EXPR_ARG (exp, 0);
+ srclen = 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);
+ srclen = 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;
+ /* When the type of SRCLEN is a pointer compute the length of string
+ the pointer points to. Otherwise it is an integer that gives the
+ upper bound on the length of the operation (as in __strncpy_chk). */
+ if (srclen && TREE_CODE (TREE_TYPE (srclen)) == POINTER_TYPE)
+ srclen = c_strlen (srclen, 1);
- if (is_strlen)
- {
- len = c_strlen (len, 1);
- if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
- return;
- }
- else if (fcode == BUILT_IN_STRNCAT_CHK)
+ if (catlen)
{
- 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))
+ if ((!srclen || !tree_fits_uhwi_p (srclen))
+ && (!maxlen
+ || (tree_fits_uhwi_p (maxlen)
+ && tree_fits_uhwi_p (objsize)
+ && !tree_int_cst_lt (maxlen, objsize))))
{
+ location_t loc = tree_nonartificial_location (exp);
+
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))
+
+ if (!srclen)
return;
+
+ catlen = c_strlen (catlen, 1);
+ if (catlen)
+ srclen = fold_build2 (PLUS_EXPR, TREE_TYPE (srclen), srclen, catlen);
+ }
+
+ if (srclen)
+ {
+ srclen = fold_build2 (PLUS_EXPR, TREE_TYPE (srclen), srclen,
+ size_one_node);
+ if (maxlen && tree_fits_uhwi_p (maxlen)
+ && tree_int_cst_lt (maxlen, srclen))
+ srclen = maxlen;
}
- 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 (exp, NULL_TREE, maxlen, srclen, objsize);
}
/* Emit warning if a buffer overflow is detected at compile time
@@ -9428,10 +9645,9 @@ 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 (exp, NULL_TREE, NULL_TREE, len, size);
}
/* Emit warning if a free is called with address of a variable. */
@@ -647,6 +647,10 @@ 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++ Var(warn_stringop_overflow) Init(1) Warning
+Warn about buffer overflow in string manipulation functions.
+
Wsuggest-attribute=format
C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
Warn about functions which might be candidates for format attributes.
@@ -299,6 +299,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 @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
@@ -4831,6 +4832,27 @@ comparisons, so this warning level gives a very large number of
false positives.
@end table
+@item -Wstringop-overflow
+@opindex Wstringop-overflow
+@opindex Wno-stringop-overflow
+Warn for calls to string manipulation functions such as memcpy and strcpy
+hat are determined to overflow the destination buffer. The option works
+best with optimization enabled but it can detect a small subset of buffer
+overflows even without optimization. In any case, the option warns about
+just a subset of buffer overflows detected by the corresponding overflow
+checking built-ins. @xref{Object Size Checking}. For example,
+@option{-Wstringop-overflow} will warn on the following:
+
+@smallexample
+char buf[6];
+void f (int i)
+@{
+ strcpy (buf, i < 0 ? "yellow" : "orange");
+@};
+@end smallexample
+
+The @option{-Wstringop-overflow} option is enabled by default.
+
@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]}
@opindex Wsuggest-attribute=
@opindex Wno-suggest-attribute=
@@ -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 "overflows" }
}
void bar (const char *, int);
@@ -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\]*" }
@@ -5,6 +5,11 @@
extern void abort (void);
+#define abort() \
+ (__builtin_printf ("failure at %s:%i: %s\n", \
+ __FILE__, __LINE__, __FUNCTION__), \
+ __builtin_abort ())
+
extern int inside_main;
void *chk_fail_buf[256] __attribute__((aligned (16)));
volatile int chk_fail_allowed, chk_calls;
@@ -17,6 +17,8 @@ volatile char *s2 = "defg"; /* prevent constant propagation to happen when whole
volatile char *s3 = "FGH"; /* prevent constant propagation to happen when whole program assumptions are made. */
volatile size_t l1 = 1; /* prevent constant propagation to happen when whole program assumptions are made. */
+#define abort() (__builtin_printf ("failure on line %i\n", __LINE__), __builtin_abort ())
+
void
__attribute__((noinline))
test1 (void)
@@ -326,6 +328,8 @@ test4 (void)
char buf3[20];
chk_fail_allowed = 1;
+ mempcpy_disallowed = 0;
+
/* Runtime checks. */
if (__builtin_setjmp (chk_fail_buf) == 0)
{
@@ -343,7 +347,9 @@ test4 (void)
vx = mempcpy (&buf3[19], "ab", 2);
abort ();
}
+
chk_fail_allowed = 0;
+ mempcpy_disallowed = 1;
}
#ifndef MAX_OFFSET
@@ -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" } */
}
@@ -12,6 +12,8 @@ extern void *memcpy (void *, const void *, size_t);
extern void *memset (void *, int, size_t);
extern char *strcpy (char *, const char *);
+#define abort() (__builtin_printf ("failure in %s on line %i\n", __func__, __LINE__), __builtin_abort ())
+
struct A
{
char a[10];
@@ -2,7 +2,10 @@
/* { dg-options "-O2" } */
typedef __SIZE_TYPE__ size_t;
-extern void abort (void);
+extern void abort1 (void);
+extern void abort2 (void);
+extern void abort3 (void);
+extern void abort4 (void);
extern char buf[0x40000000];
void
@@ -14,7 +17,7 @@ test1 (size_t x)
for (i = 0; i < x; ++i)
p = p + 4;
if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
- abort ();
+ abort1 ();
}
void
@@ -26,7 +29,7 @@ test2 (size_t x)
for (i = 0; i < x; ++i)
p = p + 4;
if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
- abort ();
+ abort2 ();
}
void
@@ -38,7 +41,7 @@ test3 (size_t x)
for (i = 0; i < x; ++i)
p = p + 4;
if (__builtin_object_size (p, 2) != 0)
- abort ();
+ abort3 ();
}
void
@@ -50,7 +53,7 @@ test4 (size_t x)
for (i = 0; i < x; ++i)
p = p + 4;
if (__builtin_object_size (p, 3) != 0)
- abort ();
+ abort4 ();
}
/* { dg-final { scan-assembler-not "abort" } } */
@@ -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 size 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 "writing 3 " "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 size 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 size 3 exceeds the size 2 of the destination object" "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 size 3 exceeds the size 1 of the destination object" "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);
new file mode 100644
@@ -0,0 +1,145 @@
+/* Test exercising buffer overflow warnings emitted for __*_chk builtins
+ in cases where the destination involves a non-constant offset into
+ an object of known size. */
+/* { 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;
+
+#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 strcpy(d, s) (strcpy ((d), (s)), sink ((d)))
+extern char* (strcpy)(char*, const char*);
+
+void sink (void*);
+
+/* Function to "generate" a unique 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. */
+ptrdiff_t random_value (void);
+
+/* Return a random value between MIN and MAX. */
+static inline size_t
+value_range (size_t min, size_t max)
+{
+ const size_t val = random_value ();
+ return val < min || max < val ? min : val;
+}
+
+/* For brevity. */
+#define R(min, max) value_range (min, max)
+
+/* Test memcpy with a number of bytes bounded by a known range. */
+
+void test_memcpy_range (const void *p)
+{
+ char buf[5];
+
+ memcpy (buf, p, R (0, 5));
+ memcpy (buf, p, R (1, 5));
+ memcpy (buf, p, R (2, 5));
+ memcpy (buf, p, R (3, 5));
+ memcpy (buf, p, R (4, 5));
+
+ memcpy (buf, p, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+ const size_t max = SIZE_MAX;
+ memcpy (buf, p, R (max - 1, max)); /* { dg-warning "writing" } */
+
+ memcpy (buf, p, R (max / 2 + 1, max)); /* { dg-warning "writing" } */
+}
+
+/* Test mempcpy with a number of bytes bounded by a known range. */
+
+void test_mempcpy_range (const void *p)
+{
+ char buf[5];
+
+ mempcpy (buf, p, R (0, 5));
+ mempcpy (buf, p, R (1, 5));
+ mempcpy (buf, p, R (2, 5));
+ mempcpy (buf, p, R (3, 5));
+ mempcpy (buf, p, R (4, 5));
+
+ mempcpy (buf, p, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+ const size_t max = SIZE_MAX;
+ mempcpy (buf, p, R (max - 1, max)); /* { dg-warning "writing" } */
+
+ mempcpy (buf, p, R (max / 2 + 1, max)); /* { dg-warning "writing" } */
+}
+
+/* Test memset with a number of bytes bounded by a known range. */
+
+void test_memset_range (const void *p)
+{
+ char buf[5];
+
+ memset (buf, 0, R (0, 5));
+ memset (buf, 1, R (1, 5));
+ memset (buf, 2, R (2, 5));
+ memset (buf, 3, R (3, 5));
+ memset (buf, 4, R (4, 5));
+
+ memset (buf, 5, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */
+
+ const size_t max = SIZE_MAX;
+ memset (buf, 6, R (max - 1, max)); /* { dg-warning "writing" } */
+
+ memset (buf, 7, R (max / 2 + 1, max)); /* { dg-warning "writing" } */
+}
+
+/* Return 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 = value_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)
+
+void test_strcpy_range (const char *s)
+{
+ 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 " } */
+}
@@ -24,11 +24,11 @@ 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" } */
*p = 0;
strncat (p, "abcdefgh", 11);
*p = 0;
- strncat (p, "abcdefghijkl", 11); /* { dg-warning "will always overflow" } */
+ strncat (p, "abcdefghijkl", 11); /* { dg-warning "writing 11 bytes" } */
*p = 0;
strncat (p, q, 9);
*p = 0;
@@ -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\]*" } */