Commit ef29b12c by Martin Sebor Committed by Martin Sebor

PR middle-end/91582 - missing heap overflow detection for strcpy

PR middle-end/91582 - missing heap overflow detection for strcpy
PR middle-end/92868 - ICE: tree check: expected integer_cst, have ssa_name

gcc/ChangeLog:

	PR middle-end/91582
	PR middle-end/92868
	* builtins.c (addr_decl_size): New function.
	(gimple_call_alloc_size): Add arguments.
	(compute_objsize): Add an argument.  Set *PDECL even for allocated
	objects.
	Correct checking for negative wide_int.
	Correct handling of negative outer offsets into unknown regions
	or with unknown inner offsets.
	Extend offsets to at most sizetype precision.
	Only handle constant subobject sizes.
	* builtins.h (gimple_call_alloc_size): Add arguments.
	* tree.c (component_ref_size): Always return sizetype.
	* tree-ssa-strlen.c (strinfo::alloc): New member.
	(get_addr_stridx): Add argument.
	(get_stridx): Use ptrdiff_t.  Add argument.
	(new_strinfo): Set new member.
	(get_string_length): Handle alloca and VLA.
	(dump_strlen_info): Dump more state.
	(maybe_invalidate): Print more info.  Decrease indentation.
	(unshare_strinfo): Set new member.
	(valid_builtin_call): Handle alloca and VLA.
	(maybe_warn_overflow): Check and set no-warning bit.  Improve
	handling of offsets.  Print allocated objects.
	(handle_builtin_strlen): Handle strinfo records with null lengths.
	(handle_builtin_strcpy): Add argument.  Call maybe_warn_overflow.
	(is_strlen_related_p): Handle dynamically allocated objects.
	(get_range): Add argument.
	(handle_builtin_malloc): Rename...
	(handle_alloc): ...to this and handle all allocation functions.
	(handle_builtin_memset): Call maybe_warn_overflow.
	(count_nonzero_bytes): Handle more MEM_REF forms.
	(strlen_check_and_optimize_call): Call handle_alloc_call.  Pass
	arguments to more callees.
	(handle_integral_assign): Add argument.  Create strinfo entries
	for MEM_REF assignments.
	(check_and_optimize_stmt): Handle more MEM_REF forms.

gcc/testsuite/ChangeLog:

	PR middle-end/91582
	* c-c++-common/Wrestrict.c: Adjust expected warnings.
	* gcc/testsuite/c-c++-common/Wstringop-truncation-4.c: Enable more
	warnings.
	* gcc/testsuite/c-c++-common/Wstringop-truncation.c: Remove an xfail.
	* gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
	* gcc.dg/Warray-bounds-47.c: Same.
	* gcc.dg/Warray-bounds-52.c: New test.
	* gcc.dg/Wstringop-overflow-27.c: New test.
	* gcc.dg/Wstringop-overflow-28.c: New test.
	* gcc.dg/Wstringop-overflow-29.c: New test.
	* gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
	* gcc.dg/attr-copy-2.c: Adjust expected warnings.
	* gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
	* gcc.dg/strlenopt-86.c: Relax test.
	* gcc.target/i386/pr82002-1.c: Prune expected warnings.

From-SVN: r279392
parent e78b9a6f
2019-12-13 Martin Sebor <msebor@redhat.com>
PR middle-end/91582
PR middle-end/92868
* builtins.c (addr_decl_size): New function.
(gimple_call_alloc_size): Add arguments.
(compute_objsize): Add an argument. Set *PDECL even for allocated
objects.
Correct checking for negative wide_int.
Correct handling of negative outer offsets into unknown regions
or with unknown inner offsets.
Extend offsets to at most sizetype precision.
Only handle constant subobject sizes.
* builtins.h (gimple_call_alloc_size): Add arguments.
* tree.c (component_ref_size): Always return sizetype.
* tree-ssa-strlen.c (strinfo::alloc): New member.
(get_addr_stridx): Add argument.
(get_stridx): Use ptrdiff_t. Add argument.
(new_strinfo): Set new member.
(get_string_length): Handle alloca and VLA.
(dump_strlen_info): Dump more state.
(maybe_invalidate): Print more info. Decrease indentation.
(unshare_strinfo): Set new member.
(valid_builtin_call): Handle alloca and VLA.
(maybe_warn_overflow): Check and set no-warning bit. Improve
handling of offsets. Print allocated objects.
(handle_builtin_strlen): Handle strinfo records with null lengths.
(handle_builtin_strcpy): Add argument. Call maybe_warn_overflow.
(is_strlen_related_p): Handle dynamically allocated objects.
(get_range): Add argument.
(handle_builtin_malloc): Rename...
(handle_alloc): ...to this and handle all allocation functions.
(handle_builtin_memset): Call maybe_warn_overflow.
(count_nonzero_bytes): Handle more MEM_REF forms.
(strlen_check_and_optimize_call): Call handle_alloc_call. Pass
arguments to more callees.
(handle_integral_assign): Add argument. Create strinfo entries
for MEM_REF assignments.
(check_and_optimize_stmt): Handle more MEM_REF forms.
2019-12-13 Iain Sandoe <iain@sandoe.co.uk>
* config/rs6000/darwin.h (DARWIN_DYLIB1_SPEC): New.
......@@ -133,8 +133,12 @@ extern tree fold_call_stmt (gcall *, bool);
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
extern bool is_simple_builtin (tree);
extern bool is_inexpensive_builtin (tree);
extern tree gimple_call_alloc_size (gimple *);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL);
class vr_values;
tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
const vr_values * = NULL);
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
const vr_values * = NULL);
extern bool readonly_data_expr (tree exp);
extern bool init_target_chars (void);
......
2019-12-13 Martin Sebor <msebor@redhat.com>
PR middle-end/91582
* c-c++-common/Wrestrict.c: Adjust expected warnings.
* c-c++-common/Wstringop-truncation-4.c: Enable more
warnings.
* c-c++-common/Wstringop-truncation.c: Remove an xfail.
* gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
* gcc.dg/Warray-bounds-47.c: Same.
* gcc.dg/Warray-bounds-52.c: New test.
* gcc.dg/Wstringop-overflow-27.c: New test.
* gcc.dg/Wstringop-overflow-28.c: New test.
* gcc.dg/Wstringop-overflow-29.c: New test.
* gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
* gcc.dg/attr-copy-2.c: Adjust expected warnings.
* gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
* gcc.dg/strlenopt-86.c: Relax test.
* gcc.target/i386/pr82002-1.c: Prune expected warnings.
2019-12-13 Roman Zhuykov <zhroma@ispras.ru>
PR rtl-optimization/92591
......
......@@ -731,10 +731,16 @@ void test_strcpy_range (void)
r = SR (3, DIFF_MAX - 3);
T (8, "01", a + r, a);
T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
/* The accesses below might trigger either
-Wrestrict: accessing 4 bytes at offsets [3, \[0-9\]+] and 0 may overlap 1 byte at offset 3
or
-Wstringop-overflow: writing 4 bytes into a region of size 0
Either of the two is appropriate. */
T (8, "012", a + r, a); /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
T (8, "012", a + r, a); /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
T (8, "012", a + r, a); /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
/* Exercise the full range of ptrdiff_t. */
r = signed_value ();
......
......@@ -21,9 +21,13 @@ struct Arrays
void test_arrays (struct Arrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. The size argument is a good
enough indication that it is not being used as a "legacy"
flexible array member. */
strncpy (p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct Pointers
......@@ -49,9 +53,11 @@ struct ConstArrays
void test_const_arrays (struct ConstArrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct ConstPointers
......@@ -77,9 +83,11 @@ struct VolatileArrays
void test_volatile_arrays (struct VolatileArrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct VolatilePointers
......@@ -105,9 +113,11 @@ struct ConstVolatileArrays
void test_const_volatile_arrays (struct ConstVolatileArrays *p, const char *s)
{
/* Expect accesses to all three arrays to trigger the warning,
including the trailing one. */
strncpy ((char*)p->a, s, sizeof p->a); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->b, s, sizeof p->b); /* { dg-warning "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-bogus "\\\[-Wstringop-truncation" } */
strncpy ((char*)p->c, s, sizeof p->c); /* { dg-warning "\\\[-Wstringop-truncation" } */
}
struct ConstVolatilePointers
......
......@@ -300,8 +300,7 @@ void test_strncpy_array (Dest *pd, int i, const char* s)
CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */
CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */
/* The following is not yet handled. */
CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" { xfail *-*-* } } */
CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" } */
/* Verify that a copy that nul-terminates is not diagnosed. */
CPY (pd->a5, "1234", sizeof pd->a5);
......
......@@ -12,7 +12,7 @@ void sink (void*);
struct Ax
{
char n;
char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" }
char a[]; // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" "note: flexarray" }
};
// Verify warning for a definition with no initializer.
......@@ -93,7 +93,7 @@ NOIPA void gaxx ()
struct A0
{
char n;
char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" }
char a[0]; // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" "note: trailing zero-length array" }
};
// Verify warning for a definition with no initializer.
......@@ -160,7 +160,7 @@ NOIPA void ga0x ()
struct A1
{
char n;
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" }
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" "note: trailing one-element array" }
};
// Verify warning for a definition with no initializer.
......@@ -234,7 +234,7 @@ NOIPA void ga1x ()
struct A1i
{
char n;
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" }
char a[1]; // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" "note: interior one-element array" }
char x;
};
......@@ -307,7 +307,7 @@ NOIPA void ga1ix ()
struct Bx
{
char n;
char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" }
char a[]; // { dg-message "at offset 0 to object 'Bx::a' declared here" "note: flexarray class member" }
// Verify the warning for a constant.
Bx () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
......@@ -332,7 +332,7 @@ NOIPA void gbxi (int i)
struct B0
{
char n;
char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" }
char a[0]; // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" "note: zero-length trailing array class member" }
B0 () { a[0] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
......@@ -348,7 +348,7 @@ NOIPA void gb0 (void)
struct B1
{
char n;
char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" }
char a[1]; // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" "note: one-element trailing array class member" }
B1 () { a[1] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
......@@ -362,7 +362,7 @@ NOIPA void gb1 (void)
struct B123
{
char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" }
char a[123]; // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" "note: large trailing array class member" }
B123 () { a[123] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
......@@ -376,7 +376,7 @@ NOIPA void gb123 (void)
struct B234
{
char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" }
char a[234]; // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" "note: large trailing array class member" }
B234 (int i) { a[i] = 0; } // { dg-warning "\\\[-Wstringop-overflow" }
};
......
......@@ -3,7 +3,7 @@
Test to verify that past-the-end accesses by string functions to member
arrays by-reference objects are diagnosed.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" } */
{ dg-options "-O2 -Wall -Wno-unused-local-typedefs -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
#define SA(expr) typedef int StaticAssert [2 * !!(expr) - 1]
......
/* PR middle-end/91830 - Bogus -Warray-bounds on strcpy into a member
of a subobject compiling binutils
{ dg-do compile }
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
{ dg-options "-O2 -Wall -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
extern char* strcpy (char*, const char*);
extern void sink (void*);
......
/* PR middle-end/92341 - missing -Warray-bounds indexing past the end
of a compound literal
{ dg-do compile }
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
#include "range.h"
#define INT_MAX __INT_MAX__
#define INT_MIN (-__INT_MAX__ - 1)
void sink (int, ...);
#define T(...) sink (__LINE__, (__VA_ARGS__))
void direct_idx_cst (void)
{
T ((int[]){ }[-1]); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
T ((int[]){ }[0]); // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
T ((int[]){ }[1]); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
T ((int[]){ 1 }[-1]); // { dg-warning "array subscript -1 is below array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[0]);
T ((int[]){ 1 }[1]); // { dg-warning "array subscript 1 is above array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[INT_MIN]); // { dg-warning "array subscript -\[0-9\]+ is below array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[INT_MAX]); // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
T ((int[]){ 1 }[SIZE_MAX]); // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
}
void direct_idx_var (int i)
{
T ((char[]){ }[i]); // { dg-warning "array subscript i is outside array bounds of 'char\\\[0]'" }
T ((int[]){ }[i]); // { dg-warning "array subscript i is outside array bounds of 'int\\\[0]'" }
}
void direct_idx_range (void)
{
ptrdiff_t i = SR (-2, -1);
T ((int[]){ 1 }[i]); // { dg-warning "array subscript \[ \n\r]+ is outside array bounds of 'int\\\[0]'" "pr?????" { xfail *-*-* } }
}
#undef T
#define T(idx, ...) do { \
int *p = (__VA_ARGS__); \
sink (p[idx]); \
} while (0)
void ptr_idx_cst (void)
{
T (-1, (int[]){ }); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
T ( 0, (int[]){ }); // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
T (+1, (int[]){ }); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
T (-1, (int[]){ 1 }); // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" }
T ( 0, (int[]){ 1 });
T (+1, (int[]){ 1 }); // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[1]'" }
T (INT_MIN, (int[]){ 1 }); // { dg-warning "array subscript -\[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { xfail ilp32 } }
T (INT_MAX, (int[]){ 1 }); // { dg-warning "array subscript \[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { target lp64 } }
// { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" "ilp32" { target ilp32 } .-1 }
T (SIZE_MAX, (int[]){ 1 }); // { dg-warning "array subscript -?\[0-9\]+ is outside array bounds of 'int\\\[1]'" }
}
void ptr_idx_var (int i)
{
T (i, (int[]){ }); // { dg-warning "array subscript \[^\n\r\]+ is outside array bounds of 'int\\\[0]'" }
T (i, (int[]){ 1 });
T (i, (int[]){ i, 1 });
}
void ptr_idx_range (void)
{
ptrdiff_t i = SR (-2, -1);
T (i, (int[]){ }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[0]'" }
T (i, (int[]){ 1 }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
T (i, (int[]){ i }); // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
i = SR (0, 1);
T (i, (int[]){ }); // { dg-warning "array subscript \\\[0, 1] is outside array bounds of 'int\\\[0]'" }
T (i, (int[]){ 1 });
i = SR (1, 2);
T (i, (int[]){ 1 }); // { dg-warning "array subscript \\\[1, 2] is outside array bounds of 'int\\\[1]'" }
i = SR (2, 3);
T (i, (int[]){ 1, 2, 3 });
i = SR (3, 4);
T (i, (int[]){ 2, 3, 4 }); // { dg-warning "array subscript \\\[3, 4] is outside array bounds of 'int\\\[3]'" }
}
/* PR middle-end/91582 - missing heap overflow detection for strcpy
PR middle-end/85484 - missing -Wstringop-overflow for strcpy with
a string of non-const length
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds" } */
typedef __SIZE_TYPE__ size_t;
extern void* calloc (size_t, size_t);
extern void* malloc (size_t);
extern void* memcpy (void*, const void*, size_t);
extern void* memset (void*, int, size_t);
extern char* strcpy (char*, const char*);
extern size_t strlen (const char*);
void sink (void*);
void test_memcpy_nowarn (const void *s, int i, size_t n)
{
sink (memcpy (calloc (1, 1), s, 1));
sink (memcpy (calloc (1, 2), s, 1));
sink (memcpy (calloc (2, 1), s, 1));
sink (memcpy (calloc (3, 1), s, 2));
sink (memcpy (calloc (3, 1), "12", 2));
sink (memcpy (calloc (3, 1), s, 3));
sink (memcpy (calloc (3, 1), "12", 3));
sink (memcpy (calloc (i, 1), s, 1));
sink (memcpy (calloc (n, 1), s, 1));
sink (memcpy (calloc (1, n), "", 1));
sink (memcpy (calloc (1, i), "", 1));
sink (memcpy (calloc (i, 1), "123", 3));
sink (memcpy (calloc (n, 1), "123", 3));
sink (memcpy (calloc (1, i), "123456", 7));
sink (memcpy (calloc (1, n), "123456", 7));
sink (memcpy (calloc (n, 1), s, 12345));
sink (memcpy (calloc (1, n), s, n - 1));
sink (memcpy (calloc (n, 1), s, n));
sink (memcpy ((char*)calloc (1, 1) + i, "123", 1));
sink (memcpy ((char*)calloc (n, 1) + i, "123", n));
sink (memcpy ((char*)calloc (1, 1) + i, s, 1));
sink (memcpy ((char*)calloc (n, 1) + i, s, n));
sink (memcpy (malloc (1), s, 1));
sink (memcpy (malloc (2), s, 1));
sink (memcpy (malloc (3), s, 2));
sink (memcpy (malloc (3), "12", 2));
sink (memcpy (malloc (3), s, 3));
sink (memcpy (malloc (3), "12", 3));
sink (memcpy (malloc (n), s, 1));
sink (memcpy (malloc (n), "", 1));
sink (memcpy (malloc (n), "123", 3));
sink (memcpy (malloc (n), "123456", 7));
sink (memcpy (malloc (n), s, 12345));
sink (memcpy (malloc (n), s, n - 1));
sink (memcpy (malloc (n), s, n));
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof a);
memcpy (p, a, sizeof a);
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
int vla[nelts];
memcpy (vla, a, nelts * sizeof *vla);
sink (vla);
}
}
void test_memcpy_warn (const int *s, size_t n)
{
{
void *p = (char*)malloc (0);
memcpy (p, s, 1); // { dg-warning "writing 1 byte into a region of size 0" }
sink (p);
}
{
void *p = (char*)malloc (1);
memcpy (p, s, 2); // { dg-warning "writing 2 bytes into a region of size 1" }
sink (p);
}
{
void *p = (char*)malloc (2);
memcpy (p, s, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
sink (p);
}
{
void *p = (char*)malloc (3);
memcpy (p, s, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof *a);
memcpy (p, a, sizeof a); // { dg-warning "" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
char vla[nelts];
memcpy (vla, a, nelts * sizeof *a); // { dg-warning "" }
sink (vla);
}
{
void *p = malloc (n);
memcpy (p, s, n * sizeof *s); // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
sink (p);
}
}
void test_memset_nowarn (int x, size_t n)
{
sink (memset (calloc (1, 1), x, 1));
sink (memset (calloc (1, 2), x, 1));
sink (memset (calloc (2, 1), x, 1));
sink (memset (calloc (3, 1), x, 2));
sink (memset (calloc (3, 1), x, 3));
sink (memset (calloc (n, 1), x, 1));
sink (memset (calloc (n, 1), x, 12345));
sink (memset (calloc (1, n), x, n - 1));
sink (memset (calloc (n, 1), x, n));
sink (memset (malloc (1), x, 1));
sink (memset (malloc (2), x, 1));
sink (memset (malloc (3), x, 2));
sink (memset (malloc (3), x, 3));
sink (memset (malloc (n), x, 1));
sink (memset (malloc (n), x, 12345));
sink (memset (malloc (n), x, n - 1));
sink (memset (malloc (n), x, n));
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof a);
memset (p, x, sizeof a);
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
int vla[nelts];
memset (vla, x, nelts * sizeof *vla);
sink (vla);
}
}
void test_memset_warn (int x, size_t n)
{
{
void *p = (char*)malloc (0);
memset (p, x, 1); // { dg-warning "writing 1 byte into a region of size 0" }
sink (p);
}
{
void *p = (char*)malloc (1);
memset (p, x, 2); // { dg-warning "writing 2 bytes into a region of size 1" }
sink (p);
}
{
void *p = (char*)malloc (2);
memset (p, x, 3); // { dg-warning "writing 3 bytes into a region of size 2" }
sink (p);
}
{
void *p = (char*)malloc (3);
memset (p, x, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4 };
void *p = (char*)malloc (sizeof *a);
memset (p, 0, sizeof a); // { dg-warning "" }
sink (p);
}
{
const int a[] = { 1, 2, 3, 4, 5 };
size_t nelts = sizeof a / sizeof *a;
char vla[nelts];
memset (vla, 0, nelts * sizeof *a); // { dg-warning "" }
sink (vla);
}
{
void *p = malloc (n);
memset (p, x, n * sizeof (int)); // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
sink (p);
}
}
void test_strcpy_nowarn (const char *s)
{
{
const char a[] = "12";
int n = strlen (a);
char *t = (char*)calloc (2, n);
strcpy (t, a);
sink (t);
}
{
const char a[] = "123";
unsigned n = strlen (a) + 1;
char *t = (char*)calloc (n, 1);
strcpy (t, a);
sink (t);
}
{
const char a[] = "1234";
size_t n = strlen (a) * 2;
char *t = (char*)malloc (n);
strcpy (t, a);
sink (t);
}
{
const char a[] = "1234";
size_t len = strlen (a) + 1;
char vla[len];
strcpy (vla, a);
sink (vla);
}
{
size_t n = strlen (s) + 1;
char *t = (char*)malloc (n);
strcpy (t, s);
sink (t);
}
}
void test_strcpy_warn (const char *s)
{
{
const char a[] = "123";
/* Verify that using signed int for the strlen result works (i.e.,
that the conversion from signed int to size_t doesn't prevent
the detection. */
int n = strlen (a);
char *t = (char*)calloc (n, 1); // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note" { xfail *-*-* } }
// { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note" { target *-*-* } .-1 }
strcpy (t, a); // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
sink (t);
}
{
const char a[] = "1234";
size_t n = strlen (a);
char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note" { xfail *-*-* } }
// { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note" { target *-*-* } .-1 }
strcpy (t, a); // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
sink (t);
}
// Exercise PR middle-end/85484.
{
size_t len = strlen (s);
char vla[len]; // { dg-message "at offset 0 to an object declared here" "vla note" }
strcpy (vla, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
sink (vla);
}
{
size_t n = strlen (s);
char *t = (char*)malloc (n); // { dg-message "at offset 0 to an object allocated by 'malloc' here" "malloc note" }
strcpy (t, s); // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
sink (t);
}
}
/* PR middle-end/91582 - missing heap overflow detection for strcpy
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
#include "range.h"
#define INT_MAX __INT_MAX__
#define INT_MIN (-INT_MAX - 1)
#define ATTR(...) __attribute__ ((__VA_ARGS__))
#define NOIPA ATTR (noipa)
extern void* alloca (size_t);
extern void* calloc (size_t, size_t);
extern void* malloc (size_t);
extern ATTR (alloc_size (1), malloc) char* alloc1 (size_t);
extern ATTR (alloc_size (1, 2), malloc) char* alloc2 (size_t, size_t);
extern char* strcpy (char*, const char*);
void sink (void*, ...);
/* Verify warning in stores to an object of variable size N in a known
range, at an offset (N + I) with a constant I. */
void same_size_and_offset_idx_cst (void)
{
#define T(size, off, idx) do { \
size_t n_ = size; \
ptrdiff_t i_ = idx; \
char *p_ = alloc1 (n_); \
p_ += off; \
p_[i_] = 0; \
sink (p_); \
} while (0)
{
const size_t n = UR (2, 3);
T (n, n, -4); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, n, -3);
T (n, n, -2);
T (n, n, -1);
T (n, n, 0);
T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (3, 4);
T (n, n, -5); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, n, -4);
T (n, n, -3);
T (n, n, -2);
T (n, n, -1);
T (n, n, 0);
T (n, n, 1); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (5, SIZE_MAX - 2);
T (n, n, -1);
T (n, n, -1);
T (n, n, -1);
T (n, n, -1);
}
}
/* Verify warning in stores to an object of variable size N in a known
range, at an offset (M + I) with a variable M in some range and
constant I. */
void different_size_and_offset_idx_cst (void)
{
{
const size_t n = UR (2, 3);
const size_t i = UR (1, 2);
T (n, i, -4); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, i, -3); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
T (n, i, -2);
T (n, i, -1);
T (n, i, 0);
T (n, i, 1);
T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
{
const size_t n = UR (3, 4);
const size_t i = UR (2, 5);
T (n, i, -6); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
/* The offsets -5 and -4 are both necessarily invalid even if the sum
(i - 5) and (i - 4) are (or could be) in bounds because they imply
that the intermediate offset (p + i) is out of bounds. */
T (n, i, -5); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
T (n, i, -4); // { dg-warning "" "intermediate offset" { xfail *-*-* } }
T (n, i, -3);
T (n, i, -2);
T (n, i, -1);
T (n, i, 0);
T (n, i, 1);
T (n, i, 2); // { dg-warning "writing 1 byte into a region of size 0" }
// { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
}
}
/* Verify warning in stores to an object of variable size N in a known
range, at an offset (M + I) with a variable M in some range and
constant I. */
void different_size_and_offset_idx_var (void)
{
{
const size_t n = UR (3, 4);
const size_t i = UR (1, 2);
T (n, i, SR (DIFF_MIN, 0));
T (n, i, SR ( -3, 0));
T (n, i, SR ( -1, 0));
T (n, i, SR ( 0, 1));
T (n, i, SR ( 1, 2));
T (n, i, SR ( 2, 3));
/* The warning is issued below but the offset and the size in
the note are wrong. See the FIXME in compute_objsize(). */
T (n, i, SR ( 3, 4)); // { dg-warning "\\\[-Wstringop-overflow" }
// { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
// { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
}
}
void ptr_add_2 (int n, int i0, int i1)
{
if (n < 1 || 2 < n) n = 2;
if (i0 < 0 || 1 < i0) i0 = 0;
if (i1 < 1 || 2 < i1) i1 = 1;
char *p = (char*)__builtin_malloc (n);
char *q = p;
q += i0;
q[0] = 0; // p[0]
q += i1;
q[0] = 1; // p[1]
q[1] = 2; // p[2] // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
void ptr_add_3 (int n, int i0, int i1, int i2)
{
if (n < 3 || 4 < n) n = 3;
if (i0 < 0 || 1 < i0) i0 = 0;
if (i1 < 1 || 2 < i1) i1 = 1;
if (i2 < 2 || 3 < i2) i2 = 2;
char *p = (char*)__builtin_malloc (n);
char *q = p;
q += i0;
q[0] = 0; // p[0]
q += i1;
q[0] = 1; // p[1]
q[1] = 2; // p[2]
q += i2;
q[0] = 3; // p[3]
q[1] = 4; // p[4] // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
void ptr_add_4 (int n, int i0, int i1, int i2, int i3)
{
if (n < 7 || 8 < n) n = 7;
if (i0 < 0 || 1 < i0) i0 = 0;
if (i1 < 1 || 2 < i1) i1 = 1;
if (i2 < 2 || 3 < i2) i2 = 2;
if (i3 < 3 || 4 < i3) i3 = 3;
char *p = (char*)__builtin_malloc (n);
char *q = p;
q += i0;
q[0] = 0; // p[0]
q += i1;
q[0] = 1; // p[1]
q[1] = 2; // p[2]
q += i2;
q[0] = 3; // p[3]
q[1] = 4; // p[4]
q[2] = 5; // p[5]
q += i3;
q[0] = 6; // p[6]
q[1] = 7; // p[7]
q[2] = 8; // p[8] // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
{
if (n < 1 || 2 < n) n = 2;
char *p = (char*)__builtin_malloc (n);
char *q = p;
// The following isn't diagnosed due to a bug/limitation.
q += n; // N=1 N=2
q[-1] = 0; // p[0] p[1]
q[-2] = 1; // p[-1] p[0]
q[-3] = 2; // p[-2] p[-1] // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
/* The following isn't diagnosed because the warning doesn't recognize
the index below as necessarily having the same value as the size
argument to malloc. All it considers is the range. */
q[0] = 2; // { dg-warning "\\\[-Wstringop-overflow" "pr92937: store just past the end" { xfail *-*-* } }
q[1] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
sink (p, q);
}
/* PR middle-end/91582 - missing heap overflow detection for strcpy
Verify calls via function pointers.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
typedef __attribute__ ((alloc_size (1))) char* allocfn_t (unsigned);
extern allocfn_t allocfn;
void sink (void*);
void direct_call (void)
{
char *q = allocfn (0); // { dg-message "at offset 0 to an object with size 0 allocated by 'allocfn'" }
q[0] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
void local_ptr_call (void)
{
allocfn_t *ptr = allocfn;
char *q = ptr (1); // { dg-message "at offset -1 to an object with size 1 allocated by 'allocfn'" }
q[0] = 0;
q[-1] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
void global_ptr_call (void)
{
extern allocfn_t *ptralloc;
allocfn_t *ptr = ptralloc;
char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptralloc'" }
q[0] = 0;
q[1] = 1;
q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
void global_ptr_array_call (void)
{
extern allocfn_t * (arralloc[]);
allocfn_t *ptr = arralloc[0];
char *q = ptr (2); // { dg-message "at offset 3 to an object with size 2 allocated by 'ptr'" }
q[0] = 1;
q[1] = 2;
q[3] = 3; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
struct S { allocfn_t *ptralloc; };
void member_ptr_call (struct S *p)
{
char *q = p->ptralloc (3); // { dg-message "at offset 5 to an object with size 3 allocated by 'ptralloc' here" }
q[0] = 0;
q[1] = 1;
q[2] = 2;
q[5] = 0; // { dg-warning "\\\[-Wstringop-overflow" }
sink (q);
}
......@@ -22,15 +22,15 @@ test (void)
strcpy (p, "Hello");
p = malloc1 (6);
strcpy (p, "Hello");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = calloc1 (2, 5);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
}
......@@ -99,7 +99,7 @@ void* xref12 (int);
void* call_xref12 (void)
{
void *p = xref12 (3);
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
return p;
}
......@@ -197,7 +197,7 @@ void* falias_malloc (void);
void* call_falias_malloc (void)
{
char *p = falias_malloc ();
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
return p;
}
......
......@@ -110,7 +110,7 @@ void test_memop_warn_alloc (const void *src)
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" } */
memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
escape (a, src);
/* At -Wstringop-overflow=1 the destination is considered to be
......@@ -127,7 +127,7 @@ void test_memop_warn_alloc (const void *src)
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" } */
memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
escape (b);
/* The following idiom of clearing multiple members of a struct is
......
......@@ -9,11 +9,11 @@
unsigned n0, n1;
void*
keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
keep_strlen_calloc_store_cst_memset (int i, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = 'x';
p[i] = 'x';
__builtin_memset (p, 0, b);
......@@ -23,11 +23,11 @@ keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
}
void*
keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
keep_strlen_calloc_store_var_memset (int i, int x, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = x;
p[i] = x;
__builtin_memset (p, 0, b);
......@@ -37,11 +37,11 @@ keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
}
void*
keep_strlen_calloc_store_memset_2 (int x, unsigned a, unsigned b, unsigned c)
keep_strlen_calloc_store_memset_2 (int i, int x, unsigned a, unsigned b, unsigned c)
{
char *p = __builtin_calloc (a, 1);
p[1] = x;
p[i] = x;
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
......
......@@ -10,3 +10,5 @@ b ()
a (c);
a (c);
}
// { dg-prune-output "\\\[-Wstringop-overflow" }
......@@ -25,8 +25,10 @@ extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
struct c_strlen_data;
class vr_values;
extern tree get_range (tree, wide_int[2], const vr_values * = NULL);
struct c_strlen_data;
extern void get_range_strlen_dynamic (tree , c_strlen_data *, const vr_values *);
/* APIs internal to strlen pass. Defined in in gimple-ssa-sprintf.c. */
......
......@@ -13583,8 +13583,8 @@ get_initializer_for (tree init, tree decl)
determine the size of an initialized flexible array member.
If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
an interior zero-length array.
Returns the size (which might be zero for an object with
an uninitialized flexible array member) or null if the size
Returns the size as sizetype (which might be zero for an object
with an uninitialized flexible array member) or null if the size
cannot be determined. */
tree
......@@ -13733,7 +13733,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
memsz64 -= baseoff;
return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
}
return integer_zero_node;
return size_zero_node;
}
/* Return "don't know" for an external non-array object since its
......@@ -13744,7 +13744,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
&& DECL_EXTERNAL (base)
&& (!typematch
|| TREE_CODE (basetype) != ARRAY_TYPE)
? NULL_TREE : integer_zero_node);
? NULL_TREE : size_zero_node);
}
/* Return the machine mode of T. For vectors, returns the mode of the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment