From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1930) id 919A23855029; Mon, 14 Jun 2021 22:51:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 919A23855029 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Martin Sebor To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-1441] Teach compute_objsize about placement new [PR100876]. X-Act-Checkin: gcc X-Git-Author: Martin Sebor X-Git-Refname: refs/heads/master X-Git-Oldrev: f9598d89a9f5a327ecdfa6f6978a0cfbe4447111 X-Git-Newrev: d9f1466f88abef7c814d02ba39a6ea5ef420aaec Message-Id: <20210614225144.919A23855029@sourceware.org> Date: Mon, 14 Jun 2021 22:51:44 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 14 Jun 2021 22:51:44 -0000 https://gcc.gnu.org/g:d9f1466f88abef7c814d02ba39a6ea5ef420aaec commit r12-1441-gd9f1466f88abef7c814d02ba39a6ea5ef420aaec Author: Martin Sebor Date: Mon Jun 14 16:34:48 2021 -0600 Teach compute_objsize about placement new [PR100876]. Resolves: PR c++/100876 - -Wmismatched-new-delete should understand placement new when it's not inlined gcc/ChangeLog: PR c++/100876 * builtins.c (gimple_call_return_array): Check for attribute fn spec. Handle calls to placement new. (ndecl_dealloc_argno): Avoid placement delete. gcc/testsuite/ChangeLog: PR c++/100876 * g++.dg/warn/Wmismatched-new-delete-4.C: New test. * g++.dg/warn/Wmismatched-new-delete-5.C: New test. * g++.dg/warn/Wstringop-overflow-7.C: New test. * g++.dg/warn/Wfree-nonheap-object-6.C: New test. * g++.dg/analyzer/placement-new.C: Prune out expected warning. Diff: --- gcc/builtins.c | 47 ++++++++++++++++++++-- gcc/testsuite/g++.dg/analyzer/placement-new.C | 2 + gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C | 45 +++++++++++++++++++++ .../g++.dg/warn/Wmismatched-new-delete-4.C | 37 +++++++++++++++++ .../g++.dg/warn/Wmismatched-new-delete-5.C | 37 +++++++++++++++++ gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C | 42 +++++++++++++++++++ 6 files changed, 207 insertions(+), 3 deletions(-) diff --git a/gcc/builtins.c b/gcc/builtins.c index af1fe49bb48..75419cc4edc 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -5159,11 +5159,42 @@ static tree gimple_call_return_array (gimple *stmt, offset_int offrng[2], range_query *rvals) { - if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL) - || gimple_call_num_args (stmt) < 1) + { + /* Check for attribute fn spec to see if the function returns one + of its arguments. */ + attr_fnspec fnspec = gimple_call_fnspec (as_a (stmt)); + unsigned int argno; + if (fnspec.returns_arg (&argno)) + { + offrng[0] = offrng[1] = 0; + return gimple_call_arg (stmt, argno); + } + } + + if (gimple_call_num_args (stmt) < 1) return NULL_TREE; tree fn = gimple_call_fndecl (stmt); + if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) + { + /* See if this is a call to placement new. */ + if (!fn + || !DECL_IS_OPERATOR_NEW_P (fn) + || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn)) + return NULL_TREE; + + tree fname = DECL_ASSEMBLER_NAME (fn); + if (!id_equal (fname, "_ZnwmPv") // ordinary form + && !id_equal (fname, "_ZnamPv")) // array form + return NULL_TREE; + + if (gimple_call_num_args (stmt) != 2) + return NULL_TREE; + + offrng[0] = offrng[1] = 0; + return gimple_call_arg (stmt, 1); + } + switch (DECL_FUNCTION_CODE (fn)) { case BUILT_IN_MEMCPY: @@ -13285,7 +13316,17 @@ fndecl_dealloc_argno (tree fndecl) { /* A call to operator delete isn't recognized as one to a built-in. */ if (DECL_IS_OPERATOR_DELETE_P (fndecl)) - return 0; + { + if (DECL_IS_REPLACEABLE_OPERATOR (fndecl)) + return 0; + + /* Avoid placement delete that's not been inlined. */ + tree fname = DECL_ASSEMBLER_NAME (fndecl); + if (id_equal (fname, "_ZdlPvS_") // ordinary form + || id_equal (fname, "_ZdaPvS_")) // array form + return UINT_MAX; + return 0; + } /* TODO: Handle user-defined functions with attribute malloc? Handle known non-built-ins like fopen? */ diff --git a/gcc/testsuite/g++.dg/analyzer/placement-new.C b/gcc/testsuite/g++.dg/analyzer/placement-new.C index 8250f45b9d9..b648a428247 100644 --- a/gcc/testsuite/g++.dg/analyzer/placement-new.C +++ b/gcc/testsuite/g++.dg/analyzer/placement-new.C @@ -24,3 +24,5 @@ void test_3 (void) int *p = new(buf) int (42); delete p; // { dg-warning "memory not on the heap" } } + +// { dg-prune-output "-Wfree-nonheap-object" } diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C new file mode 100644 index 00000000000..83b6ff9157c --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C @@ -0,0 +1,45 @@ +/* { dg-do compile } + { dg-options "-O0 -Wall" } */ + +#if __cplusplus < 201103L +# define noexcept throw () +#endif + +void* operator new (__SIZE_TYPE__, void* __p) noexcept; +void operator delete (void*, void*); + +void* operator new[] (__SIZE_TYPE__, void* __p) noexcept; +void operator delete[] (void*, void*) noexcept; + +struct A { A (); ~A (); int i; }; + +extern void *p; + +void nowarn_placement_new () +{ + char a[sizeof (A)]; + p = new (a) A (); // { dg-bogus "-Wfree-nonheap-object" } +} + + +void warn_placement_new () +{ + char a[sizeof (A)]; + p = new (a + 1) A (); // { dg-warning "\\\[-Wplacement-new" } + // { dg-bogus "-Wfree-nonheap-object" "bogus" { target *-*-* } .-1 } +} + + +void nowarn_placement_new_array () +{ + char a[sizeof (A)]; + p = new (a) A[1]; // { dg-bogus "-Wfree-nonheap-object" } +} + + +void warn_placement_new_array () +{ + char a[sizeof (A)]; + p = new (a + 1) A[1]; // { dg-warning "\\\[-Wplacement-new" } + // { dg-bogus "-Wfree-nonheap-object" "bogus" { target *-*-* } .-1 } +} diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C new file mode 100644 index 00000000000..4320181e4d7 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C @@ -0,0 +1,37 @@ +/* PR c++/100876 - -Wmismatched-new-delete should either look through + or ignore placement new + { dg-do compile } + { dg-options "-O0 -Wall" } */ + +extern "C" { + void* malloc (__SIZE_TYPE__); + void free (void*); +} + +void* operator new (__SIZE_TYPE__, void*); +void* operator new[] (__SIZE_TYPE__, void*); + +void nowarn_placement_new () +{ + free (new (malloc (sizeof (int))) int ()); // { dg-bogus "-Wmismatched-new-delete" } +} + +void nowarn_placement_array_new () +{ + free (new (malloc (sizeof (int) * 2)) int[2]); // { dg-bogus "-Wmismatched-new-delete" } +} + + +void warn_placement_new () +{ + void *p = malloc (sizeof (int)); + int *q = new (p) int (); + delete q; // { dg-warning "-Wmismatched-new-delete" } +} + +void warn_placement_array_new () +{ + void *p = malloc (sizeof (int)); + int *q = new (p) int[2]; + delete q; // { dg-warning "-Wmismatched-new-delete" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-5.C new file mode 100644 index 00000000000..92c75df40d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-5.C @@ -0,0 +1,37 @@ +/* PR c++/100876 - -Wmismatched-new-delete should either look through + or ignore placement new + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +extern "C" { + void* malloc (__SIZE_TYPE__); + void free (void*); +} + +inline void* operator new (__SIZE_TYPE__, void *p) { return p; } +inline void* operator new[] (__SIZE_TYPE__, void *p) { return p; } + +void nowarn_placement_new () +{ + free (new (malloc (sizeof (int))) int ()); // { dg-bogus "-Wmismatched-new-delete" } +} + +void nowarn_placement_array_new () +{ + free (new (malloc (sizeof (int) * 2)) int[2]); // { dg-bogus "-Wmismatched-new-delete" } +} + + +void warn_placement_new () +{ + void *p = malloc (sizeof (int)); + int *q = new (p) int (); + delete q; // { dg-warning "-Wmismatched-new-delete" } +} + +void warn_placement_array_new () +{ + void *p = malloc (sizeof (int)); + int *q = new (p) int[2]; + delete q; // { dg-warning "-Wmismatched-new-delete" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C new file mode 100644 index 00000000000..d3d28f43d0a --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C @@ -0,0 +1,42 @@ +/* PR c++/100876 - -Wmismatched-new-delete should either look through + or ignore placement new + { dg-do compile } + { dg-options "-O0 -Wall -Wno-array-bounds" } */ + +inline void* operator new (__SIZE_TYPE__, void *p) { return p; } +inline void* operator new[] (__SIZE_TYPE__, void *p) { return p; } + +void* nowarn_placement_new_memset () +{ + struct S { int i; }; + void *p = __builtin_malloc (sizeof (S)); + S *q = new (p) S; + __builtin_memset (q, 0, sizeof (S)); + return q; +} + +void* warn_placement_new_memset () +{ + struct S { int i; }; + void *p = __builtin_malloc (sizeof (S)); + S *q = new (p) S; + __builtin_memset (q, 0, sizeof (S) + 1); // { dg-warning "\\\[-Wstringop-overflow" } + return q; +} + +void* nowarn_placement_new_array_strncpy (const char *s) +{ + void *p = __builtin_malloc (5); + char *q = new (p) char[5]; + __builtin_strncpy (q, s, 5); + return q; + +} + +void* warn_placement_new_array_strncpy (const char *s) +{ + void *p = __builtin_malloc (4); + char *q = new (p) char[5]; + __builtin_strncpy (q, s, 5); // { dg-warning "\\\[-Wstringop-overflow" } + return q; +}