diff options
author | Martin Sebor <msebor@redhat.com> | 2020-12-03 15:41:25 -0700 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2020-12-03 15:43:32 -0700 |
commit | dce6c58db87ebf7f4477bd3126228e73e4eeee97 (patch) | |
tree | f89f18c53c2f16c2d39a1951c21f99cdd4773c99 /gcc/testsuite | |
parent | a3f7a6957a674caf95c4aefa618be51092022e87 (diff) | |
download | gcc-dce6c58db87ebf7f4477bd3126228e73e4eeee97.zip gcc-dce6c58db87ebf7f4477bd3126228e73e4eeee97.tar.gz gcc-dce6c58db87ebf7f4477bd3126228e73e4eeee97.tar.bz2 |
Add support for detecting mismatched allocation/deallocation calls.
PR c++/90629 - Support for -Wmismatched-new-delete
PR middle-end/94527 - Add an __attribute__ that marks a function as freeing an object
gcc/ChangeLog:
PR c++/90629
PR middle-end/94527
* builtins.c (access_ref::access_ref): Initialize new member.
(compute_objsize): Use access_ref::deref. Handle simple pointer
assignment.
(expand_builtin): Remove handling of the free built-in.
(call_dealloc_argno): Same.
(find_assignment_location): New function.
(fndecl_alloc_p): Same.
(gimple_call_alloc_p): Same.
(call_dealloc_p): Same.
(matching_alloc_calls_p): Same.
(warn_dealloc_offset): Same.
(maybe_emit_free_warning): Same.
* builtins.h (struct access_ref): Declare new member.
(maybe_emit_free_warning): Make extern. Make use of access_ref.
Handle -Wmismatched-new-delete.
* calls.c (initialize_argument_information): Call
maybe_emit_free_warning.
* doc/extend.texi (attribute malloc): Update.
* doc/invoke.texi (-Wfree-nonheap-object): Expand documentation.
(-Wmismatched-new-delete): Document new option.
(-Wmismatched-dealloc): Document new option.
gcc/c-family/ChangeLog:
PR c++/90629
PR middle-end/94527
* c-attribs.c (handle_dealloc_attribute): New function.
(handle_malloc_attribute): Handle argument forms of attribute.
* c.opt (-Wmismatched-dealloc): New option.
(-Wmismatched-new-delete): New option.
gcc/testsuite/ChangeLog:
PR c++/90629
PR middle-end/94527
* g++.dg/asan/asan_test.cc: Fix a bug.
* g++.dg/warn/delete-array-1.C: Add expected warning.
* g++.old-deja/g++.other/delete2.C: Add expected warning.
* g++.dg/warn/Wfree-nonheap-object-2.C: New test.
* g++.dg/warn/Wfree-nonheap-object.C: New test.
* g++.dg/warn/Wmismatched-new-delete.C: New test.
* g++.dg/warn/Wmismatched-dealloc-2.C: New test.
* g++.dg/warn/Wmismatched-dealloc.C: New test.
* gcc.dg/Wmismatched-dealloc.c: New test.
* gcc.dg/analyzer/malloc-1.c: Prune out expected warning.
* gcc.dg/attr-malloc.c: New test.
* gcc.dg/free-1.c: Adjust text of expected warning.
* gcc.dg/free-2.c: Same.
* gcc.dg/torture/pr71816.c: Prune out expected warning.
* gcc.dg/tree-ssa/pr19831-2.c: Add an expected warning.
* gcc.dg/Wfree-nonheap-object-2.c: New test.
* gcc.dg/Wfree-nonheap-object-3.c: New test.
* gcc.dg/Wfree-nonheap-object.c: New test.
libstdc++-v3/ChangeLog:
* testsuite/ext/vstring/modifiers/clear/56166.cc: Suppress a false
positive warning.
Diffstat (limited to 'gcc/testsuite')
19 files changed, 1789 insertions, 23 deletions
diff --git a/gcc/testsuite/g++.dg/asan/asan_test.cc b/gcc/testsuite/g++.dg/asan/asan_test.cc index 5f2e2c2..dbf1a6a 100644 --- a/gcc/testsuite/g++.dg/asan/asan_test.cc +++ b/gcc/testsuite/g++.dg/asan/asan_test.cc @@ -829,7 +829,7 @@ NOINLINE static int LargeFunction(bool do_bad_access) { x[18]++; x[19]++; - delete x; + delete[] x; return res; } diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C new file mode 100644 index 0000000..9d4d2a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C @@ -0,0 +1,274 @@ +/* PR ????? - No warning on attempts to access free object + Verify that freeing unallocated objects referenced either directly + or through pointers is diagnosed. + { dg-do compile } + { dg-options "-O2 -Wall -Wfree-nonheap-object" } */ + +typedef __INTPTR_TYPE__ intptr_t; +typedef __SIZE_TYPE__ size_t; + +extern "C" +{ + void free (void*); + extern void* malloc (size_t); + extern void* realloc (void *p, size_t); +} + +void sink (void*, ...); +#define sink(...) sink (0, __VA_ARGS__) + +extern char ecarr[]; +extern void* eparr[]; + +extern char *eptr; + +void* source (void); + +void nowarn_free (void *p, void **pp, size_t n, intptr_t iptr) +{ + free (p); + + p = 0; + free (p); + + p = malloc (n); + sink (p); + free (p); + + p = malloc (n); + sink (p); + + p = realloc (p, n * 2); + sink (p); + free (p); + + free ((void*)iptr); + + p = source (); + free (p); + + p = source (); + p = (char*)p - 1; + free (p); + + free (*pp); +} + +void warn_free_extern_arr (void) +{ + free (ecarr); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_extern_arr_offset (int i) +{ + char *p = ecarr + i; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_cstint (void) +{ + void *p = (void*)1; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_func (void) +{ + void *p = (void*)warn_free_func; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_string (int i) +{ + { + char *p = (char*)"123"; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char *p = (char*)"234" + 1; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char *p = (char*)"345" + i; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + + if (i >= 0) + { + char *p = (char*)"456" + i; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } +} + +void warn_free_local_arr (int i) +{ + { + char a[4]; + sink (a); + free (a); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char b[5]; + sink (b); + + char *p = b + 1; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char c[6]; + sink (c); + + char *p = c + i; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } +} + + +void warn_free_vla (int n, int i) +{ + { + int vla[n], *p = vla; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + + { + int vla[n + 1], *p = vla + 1; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + int vla[n + 2], *p = vla + i; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } +} + + +void nowarn_free_extern_ptrarr (void) +{ + free (*eparr); +} + +void nowarn_free_extern_ptrarr_offset (int i) +{ + void *p = eparr[i]; + free (p); +} + + +void warn_free_extern_ptrarr (void) +{ + free (eparr); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_extern_ptrarr_offset (int i) +{ + void *p = &eparr[i]; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void nowarn_free_local_ptrarr (int i) +{ + void* a[4]; + sink (a); + free (a[0]); + free (a[1]); + free (a[i]); +} + + +void nowarn_free_extern_ptr (void) +{ + free (eptr); +} + +void nowarn_free_extern_ptr_offset (int i) +{ + char *p = eptr + i; + free (p); +} + +void warn_free_extern_ptr_pos_offset (int i) +{ + if (i <= 0) + i = 1; + + char *q = eptr + i; + free (q); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void nowarn_free_parm_offset (char *p, int i) +{ + char *q = p + i; + free (q); +} + +void nowarn_free_parm_neg_offset (char *p, int i) +{ + if (i >= 0) + i = -1; + + char *q = p + i; + free (q); +} + +void warn_free_parm_pos_offset (char *p, int i) +{ + if (i <= 0) + i = 1; + + char *q = p + i; + free (q); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +struct Members +{ + char a[4], *p, *q; +}; + +extern struct Members em; + +void nowarn_free_member_ptr (struct Members *pm, int i) +{ + char *p = em.p; + free (p); + p = em.q + i; + free (p); + + free (pm->q); + p = pm->p; + free (pm); + free (p); +} + +void nowarn_free_struct_cast (intptr_t *p) +{ + struct Members *q = (struct Members*)*p; + if (q->p == 0) + free (q); // { dg-bogus "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_member_array (void) +{ + char *p = em.a; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_member_array_off (int i) +{ + char *p = em.a + i; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C new file mode 100644 index 0000000..82b081a --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C @@ -0,0 +1,124 @@ +/* PR ????? - No warning on attempts to access free object + Verify that attempts to deallocate objects by pointers with nonzero + offsets is diagnosed. + { dg-do compile } + { dg-options "-O2 -Wall -Wfree-nonheap-object" } */ + +typedef __INTPTR_TYPE__ intptr_t; +typedef __SIZE_TYPE__ size_t; + +void sink (void*, ...); + +extern char ecarr[]; +extern void* eparr[]; + +extern char *eptr; + +char* source (void); + +void nowarn_op_delete (void *p, void ***ppp, size_t n, intptr_t iptr) +{ + operator delete (p); + + p = 0; + operator delete (p); + + p = operator new (n); + sink (p); + operator delete (p); + + p = operator new (n); + sink (p); + + operator delete ((void*)iptr); + + p = source (); + operator delete (p); + + p = source (); + p = (char*)p - 1; + operator delete (p); + + operator delete (**ppp); + operator delete (*ppp); + operator delete (ppp); +} + +void warn_op_delete_cstaddr (void *p) +{ + operator delete (p); + p = (void*)~0; + operator delete (p); // { dg-warning "called on a pointer to an unallocated object" } */ +} + +void warn_op_delete_funcaddr () +{ + void *p = (void*)&warn_op_delete_funcaddr; + operator delete (p); // { dg-warning "called on unallocated object 'void warn_op_delete_funcaddr()" } */ +} + +void warn_op_delete_string (void *p) +{ + operator delete (p); + p = (void*)""; + operator delete (p); // { dg-warning "called on a pointer to an unallocated object" } */ +} + +void warn_op_delete_ptr_to_self (void *p) +{ + operator delete (p); + p = &p; + operator delete (p); // { dg-warning "called on unallocated object 'p'" } */ +} + +void nowarn_op_new_delete (size_t n) +{ + void *p = operator new (n); + sink (p); + operator delete (p); +} + +void nowarn_op_new_delete_ptr_plus (size_t n) +{ + void *p0_1 = operator new (n); + void *p1 = (char*)p0_1 + 1; + sink (p0_1, p1); + void *p0_2 = (char*)p1 - 1; + sink (p0_1, p1, p0_2); + operator delete (p0_2); +} + +void warn_op_new_delete_cstoff (size_t n) +{ + void *p = operator new (n); + void *q = (char*)p + 1; + sink (p, q); + operator delete (q); // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer '\[^'\]+' with nonzero offset 1" } +} + +void warn_op_new_delete_ptr_plus (size_t n) +{ + char *p = (char*)operator new (n); + sink (++p); + operator delete (p); // { dg-warning "called on pointer '\[^']+' with nonzero offset 1" } +} + +void warn_op_delete_funcret_plus (size_t n) +{ + char *p = source (); + sink (++p); + operator delete (p); // { dg-warning "called on pointer '\[^']+' with nonzero offset 1" } +} + +void warn_op_delete_eptr_plus (int i) +{ + extern char *ecp; + + if (i < 1) + i = 1; + + char *p = ecp + i; + sink (p); + + operator delete (p); // { dg-warning "called on pointer '\[^']+' with nonzero offset \\\[1, \\d+]" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C new file mode 100644 index 0000000..7ecc99a --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C @@ -0,0 +1,185 @@ +/* PR middle-end/94527 - Add an attribute that marks a function as freeing + an object + The detection doesn't require optimization. + { dg-do compile } + { dg-options "-Wall" } */ + +#define A(...) __attribute__ ((malloc (__VA_ARGS__))) + +typedef __SIZE_TYPE__ size_t; + +extern "C" { + void free (void *); + void* realloc (void *, size_t); +} + +void sink (void *); + +void mydealloc (int, void*); +void* A (mydealloc, 2) myalloc (void*); + + +void my_delete (const char*, void*); +void my_array_delete (const char*, void*); + +typedef void OpDelete1 (void*); +typedef void OpDelete2 (void*, size_t); + +A ((OpDelete1*)operator delete, 1) +#if __cplusplus >= 201402L +A ((OpDelete2*)operator delete, 1) +#endif +A (my_delete, 2) +int* my_new (size_t); + +A ((OpDelete1*)operator delete[], 1) +#if __cplusplus >= 201402L +A ((OpDelete2*)operator delete[], 1) +#endif +A (my_array_delete, 2) +int* my_array_new (size_t); + + +void test_my_new () +{ + { + void *p = my_new (1); + operator delete (p); + } + { + void *p = my_new (1); + sink (p); + operator delete (p); + } + { + int *p = my_new (1); + sink (p); + delete p; + } + + { + void *p = my_new (1); + // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + operator delete[] (p); + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 } + } + { + void *p = my_new (1); + // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + operator delete[] (p); + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 } + } + { + int *p = my_new (1); + sink (p); + delete[] p; + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 } + } + + { + void *p = my_new (1); + my_delete ("1", p); + } + { + void *p = my_new (1); + sink (p); + my_delete ("2", p); + } + + { + void *p = my_new (1); + // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + my_array_delete ("3", p); + // { dg-warning "'void my_array_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + void *p = my_new (1); + // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + free (p); + // { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + void *p = my_new (1); + // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + p = realloc (p, 123); + // { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} + + +void test_my_array_new () +{ + { + void *p = my_array_new (1); + operator delete[] (p); + } + { + void *p = my_array_new (1); + sink (p); + operator delete[] (p); + } + { + int *p = my_array_new (1); + sink (p); + delete[] p; + } + + { + void *p = my_array_new (1); + // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + operator delete (p); + // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 } + } + { + void *p = my_array_new (1); + // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + operator delete (p); + // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 } + } + { + int *p = my_array_new (1); + sink (p); + delete p; + // { dg-warning "'void operator delete\\\(void\\\*\[^\)\]*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 } + } + + { + void *p = my_array_new (1); + my_array_delete ("1", p); + } + { + void *p = my_array_new (1); + sink (p); + my_array_delete ("2", p); + } + { + void *p = my_array_new (1); + // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + my_delete ("3", p); + // { dg-warning "'void my_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + void *p = my_array_new (1); + // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + free (p); + // { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + void *p = my_array_new (1); + // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 } + sink (p); + p = realloc (p, 123); + // { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C new file mode 100644 index 0000000..682db6f0 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C @@ -0,0 +1,27 @@ +/* PR middle-end/94527 - Add an attribute that marks a function as freeing + an object + { dg-do compile { target c++11 } } + { dg-options "-Wall" } */ + +#define A(...) __attribute__ ((malloc (__VA_ARGS__))) + +typedef __SIZE_TYPE__ size_t; + +void mydealloc (int, void*); +void* A (mydealloc, 2) myalloc (void*); + + +void* A (operator delete, 1) + bad_new (size_t); // { dg-error "attribute argument 1 is ambiguous" } +void* A (operator delete[], 1) + bad_array_new (size_t); // { dg-error "attribute argument 1 is ambiguous" } + +void my_delete (const char*, void*); +void my_array_delete (const char*, void*); + +typedef void OpDelete (void*); + +int* A ((OpDelete*)operator delete, 1) A (my_delete, 2) + my_new (size_t); +int* A ((OpDelete*)operator delete[], 1) A (my_array_delete, 2) + my_array_new (size_t); diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C new file mode 100644 index 0000000..ed1090b --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C @@ -0,0 +1,212 @@ +/* PR c++/90629 - Support for -Wmismatched-new-delete + The detection doesn't require optimization. + { dg-do compile } + { dg-options "-Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +extern "C" { + void free (void *); + void* malloc (size_t); + void* realloc (void *, size_t); + char* strdup (const char *); + char* strndup (const char *, size_t); +} + +void sink (void *); + +void nowarn_op_new_delete (int n) +{ + void *p = operator new (n); + sink (p); + operator delete (p); +} + +void nowarn_new_delete (int n) +{ + { + char *p = new char; + sink (p); + delete p; + } + + { + char *p = new char[n]; + sink (p); + delete[] p; + } +} + +/* Verify a warning for calls to free() with a pointer returned from + a call to operator new() or the new expressopm. */ + +void warn_new_free (int n) +{ + { + void *p = operator new (n); + // { dg-message "returned from a call to 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 } + sink (p); + free (p); + // { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + { + char *p = new char[n]; + // { dg-message "returned from a call to 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 } + sink (p); + free (p); + // { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} + + +/* Verify a warning for calls to realloc() with a pointer returned from + a call to operator new() or the new expressopm. */ + +void warn_new_realloc (int n) +{ + { + void *p = operator new (n); + // { dg-message "returned from a call to 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 } + sink (p); + p = realloc (p, n * 2); + // { dg-warning "'void\\\* realloc\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + sink (p); + } + { + void *p = new char[n]; + // { dg-message "returned from a call to 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 } + sink (p); + p = realloc (p, n * 2); + // { dg-warning "'void\\\* realloc\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + sink (p); + } +} + + +/* Verify a warning for a call to operator_delete() with a pointer returned + from a call to malloc(). */ + +void warn_malloc_op_delete (int n) +{ + char *p = (char *)malloc (n); + // { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 } + sink (p); + operator delete (p); + // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } +} + + +/* Verify a warning for an invocation of either form of the delete + expression with a pointer returned from a call to malloc(). */ + +void warn_malloc_delete (int n) +{ + { + char *p = (char *)malloc (n); + // { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 } + sink (p); + /* C++98 calls operator delete (void*) but later versions call + operator delete (void*, size_t). The difference doesn't matter + here so verify just that some operator delete is called. */ + delete p; + // { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + char *p = (char *)malloc (n); + // { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 } + sink (p); + delete[] p; + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} + + +/* Verify a warning for an invocation of either form of the delete + expression with a pointer returned from a call to realloc(). */ + +void warn_realloc_delete (void *p1, void *p2, int n) +{ + { + char *q = (char *)realloc (p1, n); + // { dg-message "returned from a call to 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 } + sink (q); + /* C++98 calls operator delete (void*) but later versions call + operator delete (void*, size_t). The difference doesn't matter + here so verify just that some operator delete is called. */ + delete q; + // { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + char *q = (char *)realloc (p2, n); + // { dg-message "returned from a call to 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 } + sink (q); + delete[] q; + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} + + +/* Verify a warning for an invocation of either form of the delete + expression with a pointer returned from a call to strdup(). */ + +void warn_strdup_delete (const char *s1, const char *s2) +{ + { + char *q = strdup (s1); + // { dg-message "returned from a call to 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 } + sink (q); + /* C++98 calls operator delete (void*) but later versions call + operator delete (void*, size_t). The difference doesn't matter + here so verify just that some operator delete is called. */ + delete q; + // { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + char *q = strdup (s2); + // { dg-message "returned from a call to 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 } + sink (q); + delete[] q; + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} + + + +/* Verify a warning for an invocation of either form of the delete + expression with a pointer returned from a call to strndup(). */ + +void warn_strdup_delete (const char *s1, const char *s2, size_t n) +{ + { + char *q = strndup (s1, n); + // { dg-message "returned from a call to 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 } + sink (q); + /* C++98 calls operator delete (void*) but later versions call + operator delete (void*, size_t). The difference doesn't matter + here so verify just that some operator delete is called. */ + delete q; + // { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } + + { + char *q = strndup (s2, n); + // { dg-message "returned from a call to 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 } + sink (q); + delete[] q; + // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 } + } +} + + +struct Base { virtual ~Base (); }; +struct Derived: Base { }; + +void warn_new_free_base_derived () +{ + Base *p = new Derived (); + sink (p); + free (p); // { dg-warning "\\\[-Wmismatched-new-delete" } +} diff --git a/gcc/testsuite/g++.dg/warn/delete-array-1.C b/gcc/testsuite/g++.dg/warn/delete-array-1.C index c3af713..95fa7d4 100644 --- a/gcc/testsuite/g++.dg/warn/delete-array-1.C +++ b/gcc/testsuite/g++.dg/warn/delete-array-1.C @@ -5,7 +5,7 @@ struct S { int a [1]; } s; void foo (S *p) { - delete a; // { dg-warning "deleting array" } - delete s.a; // { dg-warning "deleting array" } - delete p->a; // { dg-warning "deleting array" } + delete a; // { dg-warning "deleting array|-Wfree-nonheap-object" } + delete s.a; // { dg-warning "deleting array|-Wfree-nonheap-object" } + delete p->a; // { dg-warning "deleting array|-Wfree-nonheap-object" } } diff --git a/gcc/testsuite/g++.old-deja/g++.other/delete2.C b/gcc/testsuite/g++.old-deja/g++.other/delete2.C index 1d0554f..76ae355 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/delete2.C +++ b/gcc/testsuite/g++.old-deja/g++.other/delete2.C @@ -9,5 +9,7 @@ void bar(foo a) { delete[] a; // should be accepted char b[1]; delete b; // { dg-warning "deleting array" } expecting pointer type + // { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 } delete[] b; // { dg-warning "deleting array" } expecting pointer type + // { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 } } diff --git a/gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c new file mode 100644 index 0000000..2b00d77 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c @@ -0,0 +1,279 @@ +/* PR ????? - No warning on attempts to access free object + Verify that attempting to reallocate unallocated objects referenced + either directly or through pointers is diagnosed. + { dg-do compile } + { dg-options "-O2 -Wall -Wfree-nonheap-object" } */ + +typedef __SIZE_TYPE__ size_t; + +extern void free (void*); +extern void* alloca (size_t); +extern void* realloc (void*, size_t); + +void sink (void*, ...); + +extern void* eparr[]; +extern char *eptr; + +extern size_t n; + + +void nowarn_realloc (void *p, size_t n) +{ + char *q = realloc (p, n); + sink (q); + + q = realloc (0, n); + sink (q); + + q = realloc (q, n * 2); + sink (q); +} + +/* Verify that calling realloc on a pointer to an unknown object minus + some nonzero offset isn't diagnosed, but a pointer plus a positive + offset is (a positive offset cannot point at the beginning). */ + +void test_realloc_offset (char *p1, char *p2, char *p3, size_t n, int i) +{ + char *q; + q = realloc (p1 - 1, n); + sink (q); + + q = realloc (p2 + 1, n); // { dg-warning "'realloc' called on pointer 'p2' with nonzero offset 1" } + sink (q); + + q = realloc (p3 + i, n); + sink (q); +} + +void warn_realloc_extern_arr (void) +{ + extern char ecarr[]; // { gg-message "declared here" } + char *p = ecarr; + char *q = realloc (p, n); // { dg-warning "'realloc' called on unallocated object 'ecarr'" } + sink (q); +} + +void warn_realloc_extern_arr_offset (int i) +{ + extern char ecarr[]; + char *p = ecarr + i; + char *q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); +} + + +void warn_realloc_string (int i) +{ + char *p, *q; + { + p = "123"; + sink (p); + q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + { + p = "234" + 1; + sink (p); + q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + { + p = "123" + i; + sink (p); + q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } +} + + +void warn_realloc_alloca (int n, int i) +{ + char *p, *q; + { + p = alloca (n); + sink (p); + q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + { + p = (char*)alloca (n + 1); + sink (p); + q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + { + p = (char*)alloca (n + 2) + i; + sink (p); + q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } +} + + +void warn_realloc_local_arr (int i) +{ + char *q; + { + char a[4]; + sink (a); + q = realloc (a, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + + { + char b[5]; + sink (b); + q = realloc (b + 1, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + + { + char c[6]; + sink (c); + q = realloc (&c[2], n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + + { + char d[7]; + sink (d); + q = realloc (&d[i], n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } +} + +void warn_realloc_vla (int n1, int n2, int i) +{ + char *q; + { + char vla[n1]; + sink (vla); + q = realloc (vla, n2); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + + { + char vlb[n1 + 1]; + sink (vlb); + q = realloc (vlb + 1, n2);// { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + + { + char vlc[n1 + 2]; + sink (vlc); + q = realloc (&vlc[2], n2);// { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } + + { + char vld[7]; + sink (vld); + q = realloc (&vld[i], n2);// { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); + } +} + +void nowarn_realloc_extern_ptrarr (void) +{ + char *q = realloc (*eparr, n); + sink (q); +} + +void nowarn_realloc_extern_ptrarr_offset (int i) +{ + char *p = eparr[i]; + char *q = realloc (p, n); + sink (q); +} + + +void warn_realloc_extern_ptrarr (void) +{ + char *q = realloc (eparr, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); +} + +void warn_realloc_extern_ptrarr_offset (int i) +{ + void *p = eparr + i; + void *q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); +} + + +void nowarn_realloc_extern_ptr (void) +{ + char *q = realloc (eptr, n); + sink (q); +} + +void nowarn_realloc_extern_ptr_offset (int i) +{ + char *p = eptr + i; + char *q = realloc (p, n); + sink (q); +} + + +void warn_realloc_extern_ptr_pos_offset (int i) +{ + if (i <= 0) + i = 1; + + char *p = eptr + i; + char *q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); +} + + +void nowarn_realloc_parm_offset (char *p, int i) +{ + char *q = p + i; + q = realloc (q, n); + sink (q); +} + +void nowarn_realloc_parm_neg_offset (char *p, int i) +{ + if (i >= 0) + i = -1; + + char *q = p + i; + q = realloc (q, n); + sink (q); +} + +void warn_realloc_parm_pos_offset (char *p, int i) +{ + if (i <= 0) + i = 1; + + char *q = p + i; + q = realloc (q, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); +} + +void nowarn_realloc_deref_parm_pos_offset (void **p, int i) +{ + if (i <= 0) + i = 1; + + // The offset is from p, not *p. + void *q = *(p + i); + q = realloc (q, n); + sink (q); +} + +void warn_realloc_deref_parm_pos_offset (void **p, int i) +{ + if (i <= 0) + i = 1; + + // Unlike in the function above the offset is from *p. + void *q = *p + i; + q = realloc (q, n); // { dg-warning "\\\[-Wfree-nonheap-object" } + sink (q); +} diff --git a/gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c new file mode 100644 index 0000000..a472b93 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c @@ -0,0 +1,57 @@ +/* PR ????? - No warning on attempts to access free object + Verify that freeing unallocated objects referenced indirectly through + pointers obtained from function calls is diagnosed. + { dg-do compile } + { dg-options "-O2 -Wall -Wfree-nonheap-object" } */ + +typedef __SIZE_TYPE__ size_t; + +extern void free (void*); +extern char* memchr (const void*, int, size_t); +extern char* strchr (const char*, int); + +void sink (void*, ...); + +extern char ecarr[]; +extern void* eparr[]; + +extern char *eptr; + + +void warn_free_memchr_ecarr (int x, size_t n) +{ + char *p = memchr (ecarr, x, n); + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_memchr_ecarr_offset (int i, int j, int x, size_t n) +{ + char *p = memchr (ecarr + i, x, n); + char *q = p + j; + sink (p, q); + free (q); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_memchr_local_arr (int x, size_t n) +{ + char a[8]; + sink (a); + + char *p = memchr (a, x, n); + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_memchr_local_arr_offset (int i, int j, int x, size_t n) +{ + char a[8]; + sink (a); + + char *p = memchr (a + i, x, n); + char *q = p + j; + sink (p, q); + free (q); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + diff --git a/gcc/testsuite/gcc.dg/Wfree-nonheap-object.c b/gcc/testsuite/gcc.dg/Wfree-nonheap-object.c new file mode 100644 index 0000000..bb222cc --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wfree-nonheap-object.c @@ -0,0 +1,273 @@ +/* PR ????? - No warning on attempts to access free object + Verify that freeing unallocated objects referenced either directly + or through pointers is diagnosed. In most cases this doesn't require + optimization. + { dg-do compile } + { dg-options "-Wall -Wfree-nonheap-object" } */ + +typedef __INTPTR_TYPE__ intptr_t; +typedef __SIZE_TYPE__ size_t; + +extern void free (void*); +extern void* malloc (size_t); +extern void* realloc (void *p, size_t); + +void sink (void*, ...); + +extern char ecarr[]; +extern void* eparr[]; + +extern char *eptr; + +void* source (void); + +void nowarn_free (void *p, void **pp, size_t n, intptr_t iptr) +{ + free (p); + + p = 0; + free (p); + + p = malloc (n); + sink (p); + free (p); + + p = malloc (n); + sink (p); + + p = realloc (p, n * 2); + sink (p); + free (p); + + free ((void*)iptr); + + p = source (); + free (p); + + p = source (); + p = (char*)p - 1; + free (p); + + free (*pp); +} + +void warn_free_extern_arr (void) +{ + free (ecarr); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_extern_arr_offset (int i) +{ + char *p = ecarr + i; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_cstint (void) +{ + void *p = (void*)1; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_func (void) +{ + void *p = warn_free_func; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_string (int i) +{ + { + char *p = "123"; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char *p = "234" + 1; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char *p = "345" + i; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + + if (i >= 0) + { + char *p = "456" + i; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } +} + +void warn_free_local_arr (int i) +{ + { + char a[4]; + sink (a); + free (a); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char b[5]; + sink (b); + + char *p = b + 1; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + char c[6]; + sink (c); + + char *p = c + i; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } +} + + +void warn_free_vla (int n, int i) +{ + { + int vla[n], *p = vla; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + + { + int vla[n + 1], *p = vla + 1; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } + { + int vla[n + 2], *p = vla + i; + sink (p); + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } + } +} + + +void nowarn_free_extern_ptrarr (void) +{ + free (*eparr); +} + +void nowarn_free_extern_ptrarr_offset (int i) +{ + char *p = eparr[i]; + free (p); +} + + +void warn_free_extern_ptrarr (void) +{ + free (eparr); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_extern_ptrarr_offset (int i) +{ + void *p = &eparr[i]; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +void nowarn_free_local_ptrarr (int i) +{ + void* a[4]; + sink (a); + free (a[0]); + free (a[1]); + free (a[i]); +} + + +void nowarn_free_extern_ptr (void) +{ + free (eptr); +} + +void nowarn_free_extern_ptr_offset (int i) +{ + char *p = eptr + i; + free (p); +} + +void nowarn_free_parm_offset (char *p, int i) +{ + char *q = p + i; + free (q); +} + +void nowarn_free_parm_neg_offset (char *p, int i) +{ + if (i >= 0) + i = -1; + + char *q = p + i; + free (q); +} + +struct Members +{ + char a[4], *p, *q; +}; + +extern struct Members em; + +void nowarn_free_member_ptr (struct Members *pm, int i) +{ + char *p = em.p; + free (p); + p = em.q + i; + free (p); + + free (pm->q); + p = pm->p; + free (pm); + free (p); +} + +void nowarn_free_struct_cast (intptr_t *p) +{ + struct Members *q = (struct Members*)*p; + if (q->p == 0) + free (q); // { dg-bogus "\\\[-Wfree-nonheap-object" } +} + + +void warn_free_member_array (void) +{ + char *p = em.a; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_member_array_off (int i) +{ + char *p = em.a + i; + free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + + +// Range information requires optimization. +#pragma GCC optimize "1" + +void warn_free_extern_ptr_pos_offset (int i) +{ + if (i <= 0) + i = 1; + + char *q = eptr + i; + free (q); // { dg-warning "\\\[-Wfree-nonheap-object" } +} + +void warn_free_parm_pos_offset (char *p, int i) +{ + if (i <= 0) + i = 1; + + char *q = p + i; + free (q); // { dg-warning "\\\[-Wfree-nonheap-object" } +} diff --git a/gcc/testsuite/gcc.dg/Wmismatched-dealloc.c b/gcc/testsuite/gcc.dg/Wmismatched-dealloc.c new file mode 100644 index 0000000..7c5d6ac --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wmismatched-dealloc.c @@ -0,0 +1,252 @@ +/* PR middle-end/94527 - Add an attribute that marks a function as freeing + an object + Verify that attribute malloc with one or two arguments has the expected + effect on diagnostics. + { dg-options "-Wall -ftrack-macro-expansion=0" } */ + +#define A(...) __attribute__ ((malloc (__VA_ARGS__))) + +typedef struct FILE FILE; +typedef __SIZE_TYPE__ size_t; + +void free (void*); +void* malloc (size_t); +void* realloc (void*, size_t); + +int fclose (FILE*); +FILE* freopen (const char*, const char*, FILE*); +int pclose (FILE*); + +A (fclose) A (freopen, 3) + FILE* fdopen (int); +A (fclose) A (freopen, 3) + FILE* fopen (const char*, const char*); +A (fclose) A (freopen, 3) + FILE* fmemopen(void *, size_t, const char *); +A (fclose) A (freopen, 3) + FILE* freopen (const char*, const char*, FILE*); +A (pclose) A (freopen, 3) + FILE* popen (const char*, const char*); +A (fclose) A (freopen, 3) + FILE* tmpfile (void); + +void sink (FILE*); + + + void release (void*); +A (release) FILE* acquire (void); + +void nowarn_fdopen (void) +{ + { + FILE *q = fdopen (0); + if (!q) + return; + + fclose (q); + } + + { + FILE *q = fdopen (0); + if (!q) + return; + + q = freopen ("1", "r", q); + fclose (q); + } + + { + FILE *q = fdopen (0); + if (!q) + return; + + sink (q); + } +} + + +void warn_fdopen (void) +{ + { + FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" } + sink (q); + release (q); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" } + } + { + FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" } + sink (q); + free (q); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" } + sink (q); + q = realloc (q, 7); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" } + sink (q); + } +} + + +void nowarn_fopen (void) +{ + { + FILE *q = fopen ("1", "r"); + sink (q); + fclose (q); + } + + { + FILE *q = fopen ("2", "r"); + sink (q); + q = freopen ("3", "r", q); + sink (q); + fclose (q); + } + + { + FILE *q = fopen ("4", "r"); + sink (q); + } +} + + +void warn_fopen (void) +{ + { + FILE *q = fopen ("1", "r"); + sink (q); + release (q); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" } + } + { + FILE *q = fdopen (0); + sink (q); + free (q); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *q = fdopen (0); + sink (q); + q = realloc (q, 7); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" } + sink (q); + } +} + + +void test_popen (void) +{ + { + FILE *p = popen ("1", "r"); + sink (p); + pclose (p); + } + + { + FILE *p; + p = popen ("2", "r"); // { dg-message "returned from a call to 'popen'" "note" } + sink (p); + fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" } + } + + { + /* freopen() can close a stream open by popen() but pclose() can't + close the stream returned from freopen(). */ + FILE *p = popen ("2", "r"); + sink (p); + p = freopen ("3", "r", p); // { dg-message "returned from a call to 'freopen'" "note" } + sink (p); + pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" } + } +} + + +void test_tmpfile (void) +{ + { + FILE *p = tmpfile (); + sink (p); + fclose (p); + } + + { + FILE *p = tmpfile (); + sink (p); + p = freopen ("1", "r", p); + sink (p); + fclose (p); + } + + { + FILE *p = tmpfile (); // { dg-message "returned from a call to 'tmpfile'" "note" } + sink (p); + pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" } + } +} + + +void warn_malloc (void) +{ + { + FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" } + sink (p); + fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" } + sink (p); + p = freopen ("1", "r", p);// { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" } + sink (p); + pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" } + } +} + + +void test_acquire (void) +{ + { + FILE *p = acquire (); + release (p); + } + + { + FILE *p = acquire (); + sink (p); + release (p); + } + + { + FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" } + sink (p); + fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" } + sink (p); + pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" } + sink (p); + p = freopen ("1", "r", p); // { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" } + sink (p); + } + + { + FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" } + sink (p); + free (p); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" } + } + + { + FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" } + sink (p); + p = realloc (p, 123); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" } + sink (p); + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c index c5bf1227c..26d8288 100644 --- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c @@ -609,3 +609,5 @@ int test_49 (int i) *p = 1; /* { dg-warning "dereference of NULL 'p' \\\[CWE-476\\\]" } */ return x; } + +/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */ diff --git a/gcc/testsuite/gcc.dg/attr-malloc.c b/gcc/testsuite/gcc.dg/attr-malloc.c new file mode 100644 index 0000000..14f1980 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-malloc.c @@ -0,0 +1,75 @@ +/* PR middle-end/94527 - Add an attribute that marks a function as freeing + an object + Verify that attribute malloc with one or two arguments is accepted where + intended and rejected where it's invalid. + { dg-options "-Wall -ftrack-macro-expansion=0" } */ + +#define A(...) __attribute__ ((malloc (__VA_ARGS__))) + +A (0) void* alloc_zero (int); // { dg-error "'malloc' attribute argument 1 does not name a function" } + +A ("") void* alloc_string (int); // { dg-error "'malloc' attribute argument 1 does not name a function" } + +int var; +A (var) void* alloc_var (int); // { dg-error "'malloc' attribute argument 1 does not name a function" } + +typedef struct Type { int i; } Type; +A (Type) void* alloc_type (int); // { dg-error "expected expression|identifier" } + +A (unknown) void* alloc_unknown (int); // { dg-error "'unknown' undeclared" } + +void fv_ (); // { dg-message "declared here" } +A (fv_) void* alloc_fv_ (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument" } + +void fvi (int); // { dg-message "declared here" } +A (fvi) void* alloc_fvi (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'int'" } + +void fvv (void); // { dg-message "declared here" } +A (fvv) void* alloc_fvv (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'void'" } + +void fvi_ (int, ...); // { dg-message "declared here" } +A (fvi_) void* alloc_fvi_ (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'int'" } + +void fvi_vp (Type, void*); // { dg-message "declared here" } +A (fvi_vp) void* alloc_fvi_vp (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'Type'" } + + +void fpv (void*); +A (fpv) void* alloc_fpv (int); + +void fpv_i (void*, int); +A (fpv_i) void* alloc_fpv_i (int); + +void fpv_pv (void*, void*); +A (fpv_i) void* alloc_fpv_pv (int); + + +void gpc (char*); +void hpi (int*); +A (fpv) A (gpc) A (hpi) Type* alloc_fpv_gpv (int); + + +/* Verify that the attribute can be applied to <stdio.h> functions. */ +typedef struct FILE FILE; +typedef __SIZE_TYPE__ size_t; + +int fclose (FILE*); +FILE* fdopen (int); +FILE* fopen (const char*, const char*); +FILE* freopen (const char*, const char*, FILE*); +int pclose (FILE*); +FILE* popen (const char*, const char*); +FILE* tmpfile (void); + +A (fclose) A (freopen, 3) A (pclose) + FILE* fdopen (int); +A (fclose) A (freopen, 3) A (pclose) + FILE* fopen (const char*, const char*); +A (fclose) A (freopen, 3) A (pclose) + FILE* fmemopen(void *, size_t, const char *); +A (fclose) A (freopen, 3) A (pclose) + FILE* freopen (const char*, const char*, FILE*); +A (fclose) A (freopen, 3) A (pclose) + FILE* popen (const char*, const char*); +A (fclose) A (freopen, 3) A (pclose) + FILE* tmpfile (void); diff --git a/gcc/testsuite/gcc.dg/free-1.c b/gcc/testsuite/gcc.dg/free-1.c index 5496c84..ad49d78 100644 --- a/gcc/testsuite/gcc.dg/free-1.c +++ b/gcc/testsuite/gcc.dg/free-1.c @@ -13,14 +13,14 @@ void foo (void) static char buf4[10], e; char *q = buf; free (p); - free (q); /* { dg-warning "attempt to free a non-heap object" } */ - free (buf2); /* { dg-warning "attempt to free a non-heap object" } */ - free (&c); /* { dg-warning "attempt to free a non-heap object" } */ - free (buf3); /* { dg-warning "attempt to free a non-heap object" } */ - free (&d); /* { dg-warning "attempt to free a non-heap object" } */ - free (buf4); /* { dg-warning "attempt to free a non-heap object" } */ - free (&e); /* { dg-warning "attempt to free a non-heap object" } */ + free (q); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (buf2); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (&c); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (buf3); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (&d); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (buf4); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (&e); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ free (&r->a); - free ("abcd"); /* { dg-warning "attempt to free a non-heap object" } */ - free (L"abcd"); /* { dg-warning "attempt to free a non-heap object" } */ + free ("abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (L"abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ } diff --git a/gcc/testsuite/gcc.dg/free-2.c b/gcc/testsuite/gcc.dg/free-2.c index eb94651..edbcdc7 100644 --- a/gcc/testsuite/gcc.dg/free-2.c +++ b/gcc/testsuite/gcc.dg/free-2.c @@ -13,14 +13,14 @@ void foo (void) static char buf4[10], e; char *q = buf; free (p); - free (q); /* At -O0 no warning is reported here. */ - free (buf2); /* { dg-warning "attempt to free a non-heap object" } */ - free (&c); /* { dg-warning "attempt to free a non-heap object" } */ - free (buf3); /* { dg-warning "attempt to free a non-heap object" } */ - free (&d); /* { dg-warning "attempt to free a non-heap object" } */ - free (buf4); /* { dg-warning "attempt to free a non-heap object" } */ - free (&e); /* { dg-warning "attempt to free a non-heap object" } */ + free (q); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (buf2); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (&c); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (buf3); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (&d); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (buf4); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (&e); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ free (&r->a); - free ("abcd"); /* { dg-warning "attempt to free a non-heap object" } */ - free (L"abcd"); /* { dg-warning "attempt to free a non-heap object" } */ + free ("abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ + free (L"abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */ } diff --git a/gcc/testsuite/gcc.dg/torture/pr71816.c b/gcc/testsuite/gcc.dg/torture/pr71816.c index be37ad9..cc143fa 100644 --- a/gcc/testsuite/gcc.dg/torture/pr71816.c +++ b/gcc/testsuite/gcc.dg/torture/pr71816.c @@ -20,3 +20,7 @@ struct ext2_icount_el *insert_icount_el() { ext2fs_resize_mem(&insert_icount_el_icount_1); return 0; } + +/* Passing the address of a declared object to realloc triggers + -Wfree-nonheap-object unless -flto is used. + { dg-prune-output "\\\[-Wfree-nonheap-object" } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c index 55b4fd0..df4120d 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c @@ -5,7 +5,7 @@ void test1(void) { int *p = __builtin_malloc (sizeof (int) * 4); *p++ = 4; - __builtin_free (p); + __builtin_free (p); // { dg-warning "\\\[-Wfree-nonheap-object" } } /* Undefined. We can't do anything here. */ |