From patchwork Tue Jul 4 16:25:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Li, Pan2 via Gcc-patches" X-Patchwork-Id: 115850 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9f45:0:b0:3ea:f831:8777 with SMTP id v5csp1338436vqx; Tue, 4 Jul 2023 09:33:16 -0700 (PDT) X-Google-Smtp-Source: APBJJlFiiq5YLqX0DwLV+Q1SvTqlvR1rLZFQZbQioKe37KKg4ueu3lSIflnOvrBk6RqO9g5N3oMv X-Received: by 2002:a17:906:d90:b0:982:26c5:6525 with SMTP id m16-20020a1709060d9000b0098226c56525mr10206981eji.60.1688488396307; Tue, 04 Jul 2023 09:33:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1688488396; cv=none; d=google.com; s=arc-20160816; b=emAA6HXXtC5lo7GhVCeCxj1nDulD8VjM0pk0RZTyEa5EZDwPiiqXHdCkqO4S7YjiWz R1A3/Oxn9eY/N5AaO/ipwqrT6vOvKSMKCMmLl4zyoAjHO0af0V/OwN6UZCk2qynyVHjn 2s25MwoCGr5wnNhQ5AVd2ZWijGeRQrjLGolk6uf1kOWMREsMyo26qlck/pyB/d2yXhBV zEyWu/ZgkncmS4zNLcpns0s9zFTHUxvEW4RvRAjdesFAs/fx/Y6F+KaYwnygvZdb0i+l KRLnmsPFksEyr5tsuUyiz0RcjblVClyIcEWe+TA3ojLRw1ldMHjR34hY8MN7Dq0OUrx3 0HMA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:cc :to:dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=JodeWbZ87YP1qeF2Jovf7ur3W9PuCEyBnkpt7GJrBDQ=; fh=BmLHGtxUYkMrWYKI3KWmXxuB34ykwtOYtZM1ZxJWZDg=; b=H33MKd431qI9HoKbxXo9JDNS9LLotnY3bMuRtb06n2wS9djIO68OeexLTKfFMiEUDT yGVNKT16Mf7et4NwGiFrc17INvrJWZsVkXIJ4PLg7X5qGiioceOnoTD8Z3Qbaa+TC3W6 n44R/SeIMrTIvv9cK+XF8yJ299eYqFltcm1EPASVpebG4XK0i0CLwQiT2z0x+pmCyF1P LsQFnCfRNAO4EpTiswqAc1a0kc0P5Nycbnz0V5qD2rDs+1eMhONIk01aNtYfcDq1HGBN QBe93X7x2t2MKqdsHPouMf/+20y6tRv4jpHWHJLHvhylOxe9jqEAbFsOqF3wYLamQcf+ RpKw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=F3M8nPXI; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id h11-20020a170906110b00b00992ade22f04si6328251eja.108.2023.07.04.09.33.15 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jul 2023 09:33:16 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=F3M8nPXI; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 44FEB3858005 for ; Tue, 4 Jul 2023 16:33:10 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 44FEB3858005 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1688488390; bh=JodeWbZ87YP1qeF2Jovf7ur3W9PuCEyBnkpt7GJrBDQ=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=F3M8nPXI3/VNtM1tz7tN873cZmYf2H9w331Y2aq0TQNJ+TZFz0AzUtjyYvcbPHY7Q 03NAuqEhW8ThtmRFTNDYqJ2pLBvI2dCvp2BN7rGSaQJ8DSbXEGJZ1Q2tp+Hs625Uh3 ebPZysNVV6o3HocNwl/mvzubI9fN+PoHZD3BwisE= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com [IPv6:2a00:1450:4864:20::430]) by sourceware.org (Postfix) with ESMTPS id D8BEB3858D32 for ; Tue, 4 Jul 2023 16:32:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D8BEB3858D32 Received: by mail-wr1-x430.google.com with SMTP id ffacd0b85a97d-314172bac25so6336961f8f.3 for ; Tue, 04 Jul 2023 09:32:21 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1688488340; x=1691080340; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=JodeWbZ87YP1qeF2Jovf7ur3W9PuCEyBnkpt7GJrBDQ=; b=Td0ieWG8kTiAqBE+gJgrJIpHFabrotLaRMgTSVOQlZM8AIUff9X0KTaVU7GZhjv2lZ 2g3KI56dgfcZkG054aQgb0/4uSj67PtatgLt/1FMz0HbCk/n9rCjSjL2kqgeDC0vMMVu fVOgbY9Ta++RrysCxc7XDZ3JrRdInbyAShtzZE7FG5LujCIJGaKZGxzmL2EaL5bq3fsD VcGi5U9XM3X1Al3BMS4yf+yKqwjsFgFeGbLt548w20E954solPfZDxbwnM43V5TEnwAq S43aoCg1LIWlmptxIqXV2A55NLgJ0xto/qHMn1sO31cp/iHrfwBS4+on+EaXou3mnSmP dQHw== X-Gm-Message-State: ABy/qLamdqZ+FtW9VHa2Xxv6B+P6xc3Jy5gTrLJjtOWQwMTPBuYuYDAl q3vrI93BXnGIkqgRHtajp5y0SszprJic X-Received: by 2002:adf:ef12:0:b0:314:13e7:b8b with SMTP id e18-20020adfef12000000b0031413e70b8bmr10676248wro.18.1688488340301; Tue, 04 Jul 2023 09:32:20 -0700 (PDT) Received: from localhost ([2a01:e0a:2ec:f0d0:7193:d876:e5e1:89ff]) by smtp.gmail.com with UTF8SMTPSA id i3-20020adfdec3000000b0031411e46af3sm15506132wrn.97.2023.07.04.09.32.18 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 04 Jul 2023 09:32:19 -0700 (PDT) X-Google-Original-From: vultkayn@gcc.gnu.org To: gcc-patches@gcc.gnu.org Cc: benjamin priour , dmalcolm@redhat.com Subject: [PATCH] analyzer: Add support of placement new and improved operator new [PR105948] Date: Tue, 4 Jul 2023 18:25:35 +0200 Message-Id: <20230704162532.205035-1-vultkayn@gcc.gnu.org> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-Spam-Status: No, score=-10.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Benjamin Priour via Gcc-patches From: "Li, Pan2 via Gcc-patches" Reply-To: priour.be@gmail.com Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1770508408992758233?= X-GMAIL-MSGID: =?utf-8?q?1770508408992758233?= From: benjamin priour Script contrib/check_GNU_style.sh complains about there being a space before a left square bracket ("operator new []"). Though, it is actually within a literal string, and the space is required to correctly detect the function. Succesfully regstrapped on x86_64-linux-gnu against trunk 3c776fdf1a8. Is it OK for trunk ? Benjamin. --- Fixed spurious possibly-NULL warning always tagging along throwing operator new despite it never returning NULL. Now, operator new is correctly recognized as possibly returning NULL if and only if it is non-throwing or exceptions have been disabled. Different standard signatures of operator new are now properly recognized. Added support of placement new, so that is now properly recognized, and a 'heap_allocated' region is no longer created for it. Placement new size is also checked and a 'Wanalyzer-allocation-size' is emitted when relevant, as well as always a 'Wanalyzer-out-of-bounds'. gcc/analyzer/ChangeLog: PR analyzer/105948 * analyzer.h (is_placement_new_p): New declaration. * call-details.cc (call_details::maybe_get_arg_region): New function. Returns the region of the argument at given index if possible. * call-details.h: Declaration of above function. * kf-lang-cp.cc (is_placement_new_p): Returns true if the gcall is recognized as a placement new. * region-model.cc (region_model::eval_condition): Now recursively call itself if one the operand is wrapped in a cast. * sm-malloc.cc (malloc_state_machine::on_stmt): Added recognition of placement new. gcc/testsuite/ChangeLog: PR analyzer/105948 * g++.dg/analyzer/out-of-bounds-placement-new.C: New test. * g++.dg/analyzer/placement-new.C: Added tests. * g++.dg/analyzer/new-2.C: New test. * g++.dg/analyzer/noexcept-new.C: New test. * g++.dg/analyzer/placement-new-size.C: New test. Signed-off-by: benjamin priour --- gcc/analyzer/analyzer.h | 1 + gcc/analyzer/call-details.cc | 11 +++ gcc/analyzer/call-details.h | 1 + gcc/analyzer/kf-lang-cp.cc | 85 +++++++++++++++++-- gcc/analyzer/region-model.cc | 21 +++++ gcc/analyzer/sm-malloc.cc | 17 ++-- gcc/testsuite/g++.dg/analyzer/new-2.C | 50 +++++++++++ gcc/testsuite/g++.dg/analyzer/noexcept-new.C | 48 +++++++++++ .../analyzer/out-of-bounds-placement-new.C | 2 +- .../g++.dg/analyzer/placement-new-size.C | 27 ++++++ gcc/testsuite/g++.dg/analyzer/placement-new.C | 63 +++++++++++++- 11 files changed, 312 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/analyzer/new-2.C create mode 100644 gcc/testsuite/g++.dg/analyzer/noexcept-new.C create mode 100644 gcc/testsuite/g++.dg/analyzer/placement-new-size.C diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 579517c23e6..b86e5cac74d 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -391,6 +391,7 @@ extern bool is_std_named_call_p (const_tree fndecl, const char *funcname, const gcall *call, unsigned int num_args); extern bool is_setjmp_call_p (const gcall *call); extern bool is_longjmp_call_p (const gcall *call); +extern bool is_placement_new_p (const gcall *call); extern const char *get_user_facing_name (const gcall *call); diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc index 17edaf26276..01f061d774e 100644 --- a/gcc/analyzer/call-details.cc +++ b/gcc/analyzer/call-details.cc @@ -152,6 +152,17 @@ call_details::get_arg_svalue (unsigned idx) const return m_model->get_rvalue (arg, m_ctxt); } +/* If argument IDX's svalue at the callsite is a region_svalue, + return the region it points to. + Otherwise return NULL. */ + +const region * +call_details::maybe_get_arg_region (unsigned idx) const +{ + const svalue *sval = get_arg_svalue (idx); + return sval->maybe_get_region (); +} + /* Attempt to get the string literal for argument IDX, or return NULL otherwise. For use when implementing "__analyzer_*" functions that take diff --git a/gcc/analyzer/call-details.h b/gcc/analyzer/call-details.h index 14a206ff5d6..aac2b7d33d8 100644 --- a/gcc/analyzer/call-details.h +++ b/gcc/analyzer/call-details.h @@ -55,6 +55,7 @@ public: tree get_arg_tree (unsigned idx) const; tree get_arg_type (unsigned idx) const; const svalue *get_arg_svalue (unsigned idx) const; + const region *maybe_get_arg_region (unsigned idx) const; const char *get_arg_string_literal (unsigned idx) const; tree get_fndecl_for_call () const; diff --git a/gcc/analyzer/kf-lang-cp.cc b/gcc/analyzer/kf-lang-cp.cc index 393b4f25e79..258d92919d7 100644 --- a/gcc/analyzer/kf-lang-cp.cc +++ b/gcc/analyzer/kf-lang-cp.cc @@ -35,6 +35,34 @@ along with GCC; see the file COPYING3. If not see #if ENABLE_ANALYZER +/* Return TRUE if CALL is non-allocating operator new or operator new[]*/ + +bool is_placement_new_p (const gcall *call) +{ + gcc_assert (call); + + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) + return false; + + if (!is_named_call_p (fndecl, "operator new", call, 2) + && !is_named_call_p (fndecl, "operator new []", call, 2)) + return false; + tree arg1 = gimple_call_arg (call, 1); + + if (!POINTER_TYPE_P (TREE_TYPE (arg1))) + return false; + + /* Sadly, for non-throwing new, the second argument type + is not REFERENCE_TYPE but also POINTER_TYPE + so a simple check is out of the way. */ + tree identifier = TYPE_IDENTIFIER (TREE_TYPE (TREE_TYPE (arg1))); + if (!identifier) + return true; + const char *name = IDENTIFIER_POINTER (identifier); + return 0 != strcmp (name, "nothrow_t"); +} + namespace ana { /* Implementations of specific functions. */ @@ -46,7 +74,7 @@ class kf_operator_new : public known_function public: bool matches_call_types_p (const call_details &cd) const final override { - return cd.num_args () == 1; + return cd.num_args () == 1 || cd.num_args () == 2; } void impl_call_pre (const call_details &cd) const final override @@ -54,13 +82,60 @@ public: region_model *model = cd.get_model (); region_model_manager *mgr = cd.get_manager (); const svalue *size_sval = cd.get_arg_svalue (0); - const region *new_reg - = model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ()); - if (cd.get_lhs_type ()) + region_model_context *ctxt = cd.get_ctxt (); + const gcall *call = cd.get_call_stmt (); + + /* If the call is an allocating new, then create a heap allocated + region. */ + if (!is_placement_new_p (call)) + { + const region *new_reg + = model->get_or_create_region_for_heap_alloc (size_sval, ctxt); + if (cd.get_lhs_type ()) + { + const svalue *ptr_sval + = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + cd.maybe_set_lhs (ptr_sval); + } + } + /* If the call was actually a placement new, check that accessing + the buffer lhs is placed into does not result in out-of-bounds. */ + else { + const region *ptr_reg = cd.maybe_get_arg_region (1); + if (ptr_reg && cd.get_lhs_type ()) + { + const region *base_reg = ptr_reg->get_base_region (); + const svalue *num_bytes_sval = cd.get_arg_svalue (0); + const region *sized_new_reg = mgr->get_sized_region (base_reg, + cd.get_lhs_type (), + num_bytes_sval); + model->check_region_for_write (sized_new_reg, + nullptr, + ctxt); const svalue *ptr_sval - = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_new_reg); cd.maybe_set_lhs (ptr_sval); + } + } + } + + void impl_call_post (const call_details &cd) const final override + { + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + tree callee_fndecl = cd.get_fndecl_for_call (); + region_model_context *ctxt = cd.get_ctxt (); + + /* If the call is guaranteed to return nonnull + then add a nonnull constraint to the allocated region. */ + if (!TREE_NOTHROW (TREE_TYPE (callee_fndecl)) && flag_exceptions) + { + const svalue *nonnull + = mgr->get_or_create_null_ptr (cd.get_lhs_type ()); + const svalue *result + = model->get_store_value (cd.get_lhs_region (), ctxt); + model->add_constraint (result, NE_EXPR, nonnull, ctxt); } } }; diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 187013a37cc..599178e3d86 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -3479,6 +3479,27 @@ region_model::eval_condition (const svalue *lhs, } } + /* Attempt to unwrap cast if there is one, and the types match. */ + tree lhs_type = lhs->get_type (); + tree rhs_type = rhs->get_type (); + if (lhs_type && rhs_type) + { + const unaryop_svalue *lhs_un_op = dyn_cast (lhs); + const unaryop_svalue *rhs_un_op = dyn_cast (rhs); + if (lhs_un_op && CONVERT_EXPR_CODE_P (lhs_un_op->get_op ()) + && rhs_un_op && CONVERT_EXPR_CODE_P (rhs_un_op->get_op ()) + && lhs_type == rhs_type) + return eval_condition (lhs_un_op->get_arg (), op, rhs_un_op->get_arg ()); + + else if (lhs_un_op && CONVERT_EXPR_CODE_P (lhs_un_op->get_op ()) + && lhs_type == rhs_type) + return eval_condition (lhs_un_op->get_arg (), op, rhs); + + else if (rhs_un_op && CONVERT_EXPR_CODE_P (rhs_un_op->get_op ()) + && lhs_type == rhs_type) + return eval_condition (lhs, op, rhs_un_op->get_arg ()); + } + /* Otherwise, try constraints. Cast to const to ensure we don't change the constraint_manager as we do this (e.g. by creating equivalence classes). */ diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index a8c63eb1ce8..41c313c07dd 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -754,7 +754,7 @@ public: override { if (change.m_old_state == m_sm.get_start_state () - && unchecked_p (change.m_new_state)) + && (unchecked_p (change.m_new_state) || nonnull_p (change.m_new_state))) // TODO: verify that it's the allocation stmt, not a copy return label_text::borrow ("allocated here"); if (unchecked_p (change.m_old_state) @@ -1910,11 +1910,16 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt, return true; } - if (is_named_call_p (callee_fndecl, "operator new", call, 1)) - on_allocator_call (sm_ctxt, call, &m_scalar_delete); - else if (is_named_call_p (callee_fndecl, "operator new []", call, 1)) - on_allocator_call (sm_ctxt, call, &m_vector_delete); - else if (is_named_call_p (callee_fndecl, "operator delete", call, 1) + if (!is_placement_new_p (call)) + { + bool returns_nonnull = !TREE_NOTHROW (callee_fndecl) && flag_exceptions; + if (is_named_call_p (callee_fndecl, "operator new")) + on_allocator_call (sm_ctxt, call, &m_scalar_delete, returns_nonnull); + else if (is_named_call_p (callee_fndecl, "operator new []")) + on_allocator_call (sm_ctxt, call, &m_vector_delete, returns_nonnull); + } + + if (is_named_call_p (callee_fndecl, "operator delete", call, 1) || is_named_call_p (callee_fndecl, "operator delete", call, 2)) { on_deallocator_call (sm_ctxt, node, call, diff --git a/gcc/testsuite/g++.dg/analyzer/new-2.C b/gcc/testsuite/g++.dg/analyzer/new-2.C new file mode 100644 index 00000000000..4e696040a54 --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/new-2.C @@ -0,0 +1,50 @@ +// { dg-additional-options "-O0" } + +struct A +{ + int x; + int y; +}; + +void test_spurious_null_warning_throwing () +{ + int *x = new int; /* { dg-bogus "dereference of possibly-NULL" } */ + int *y = new int (); /* { dg-bogus "dereference of possibly-NULL" "non-throwing" } */ + int *arr = new int[3]; /* { dg-bogus "dereference of possibly-NULL" } */ + A *a = new A (); /* { dg-bogus "dereference of possibly-NULL" "throwing new cannot be null" } */ + + int z = *y + 2; + z = *x + 4; /* { dg-bogus "dereference of possibly-NULL 'x'" } */ + /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-* } .-1 } */ + z = arr[0] + 4; /* { dg-bogus "dereference of possibly-NULL" } */ + + delete a; + delete y; + delete x; + delete[] arr; +} + +void test_default_initialization () +{ + int *y = ::new int; + int *x = ::new int (); /* { dg-bogus "dereference of possibly-NULL 'operator new" } */ + + int b = *x + 3; /* { dg-bogus "dereference of possibly-NULL" } */ + /* { dg-bogus "use of uninitialized ‘*x’" "" { target *-*-* } .-1 } */ + int a = *y + 2; /* { dg-bogus "dereference of possibly-NULL 'y'" } */ + /* { dg-warning "use of uninitialized value '\\*y'" "no default init" { target *-*-* } .-1 } */ + + delete x; + delete y; +} + +/* From clang core.uninitialized.NewArraySize +new[] should not be called with an undefined size argument */ + +void test_garbage_new_array () +{ + int n; + int *arr = ::new int[n]; /* { dg-warning "use of uninitialized value 'n'" } */ + arr[0] = 7; + ::delete[] arr; /* no warnings emitted here either */ +} diff --git a/gcc/testsuite/g++.dg/analyzer/noexcept-new.C b/gcc/testsuite/g++.dg/analyzer/noexcept-new.C new file mode 100644 index 00000000000..7699cd99cff --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/noexcept-new.C @@ -0,0 +1,48 @@ +/* { dg-additional-options "-O0 -fno-exceptions -fno-analyzer-suppress-followups" } */ +#include + +/* Test non-throwing variants of operator new */ + +struct A +{ + int x; + int y; +}; + +void test_throwing () +{ + int* x = new int; + int* y = new int(); /* { dg-warning "dereference of possibly-NULL" } */ + int* arr = new int[10]; + A *a = new A(); /* { dg-warning "dereference of possibly-NULL" } */ + + int z = *y + 2; + z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */ + /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-* } .-1 } */ + z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'" } */ + /* { dg-warning "use of uninitialized value '\\*arr'" "" { target *-*-* } .-1 } */ + a->y = a->x + 3; + + delete a; + delete y; + delete x; + delete[] arr; +} + +void test_nonthrowing () +{ + int* x = new(std::nothrow) int; + int* y = new(std::nothrow) int(); + int* arr = new(std::nothrow) int[10]; + + int z = *y + 2; /* { dg-warning "dereference of NULL 'y'" } */ + /* { dg-warning "use of uninitialized value '\\*y'" "" { target *-*-* } .-1 } */ + z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */ + /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-* } .-1 } */ + z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'" } */ + /* { dg-warning "use of uninitialized value '\\*arr'" "" { target *-*-* } .-1 } */ + + delete y; + delete x; + delete[] arr; +} diff --git a/gcc/testsuite/g++.dg/analyzer/out-of-bounds-placement-new.C b/gcc/testsuite/g++.dg/analyzer/out-of-bounds-placement-new.C index d3076c3cf25..33872e6ddab 100644 --- a/gcc/testsuite/g++.dg/analyzer/out-of-bounds-placement-new.C +++ b/gcc/testsuite/g++.dg/analyzer/out-of-bounds-placement-new.C @@ -14,6 +14,6 @@ struct int_and_addr { int test (int_container ic) { - int_and_addr *iaddr = new (ic.addr ()) int_and_addr; + int_and_addr *iaddr = new (ic.addr ()) int_and_addr; /* { dg-warning "stack-based buffer overflow" } */ return iaddr->i; } diff --git a/gcc/testsuite/g++.dg/analyzer/placement-new-size.C b/gcc/testsuite/g++.dg/analyzer/placement-new-size.C new file mode 100644 index 00000000000..810944cd137 --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/placement-new-size.C @@ -0,0 +1,27 @@ +/* { dg-additional-options "-Wno-placement-new -Wno-analyzer-use-of-uninitialized-value" } */ + +#include +#include "../../gcc.dg/analyzer/analyzer-decls.h" + +extern int get_buf_size (); + +void var_too_short () +{ + short s; + long *lp = new (&s) long; /* { dg-warning "stack-based buffer overflow" } */ + /* { dg-warning "allocated buffer size is not a multiple of the pointee's size" "" { target *-*-* } .-1 } */ +} + +void static_buffer_too_short () +{ + int n = 16; + int buf[n]; + int *p = new (buf) int[n + 1]; /* { dg-warning "stack-based buffer overflow" } */ +} + +void symbolic_buffer_too_short () +{ + int n = get_buf_size (); + char buf[n]; + char *p = new (buf) char[n + 10]; /* { dg-warning "stack-based buffer overflow" } */ +} diff --git a/gcc/testsuite/g++.dg/analyzer/placement-new.C b/gcc/testsuite/g++.dg/analyzer/placement-new.C index 89055491a48..c0cf7ec4c48 100644 --- a/gcc/testsuite/g++.dg/analyzer/placement-new.C +++ b/gcc/testsuite/g++.dg/analyzer/placement-new.C @@ -14,15 +14,74 @@ void test_2 (void) { char buf[sizeof(int) * 10]; int *p = new(buf) int[10]; -} +} // { dg-prune-output "-Wfree-nonheap-object" } /* Delete of placement new. */ void test_3 (void) { char buf[sizeof(int)]; // { dg-message "region created on stack here" } - int *p = new(buf) int (42); + int *p = new (buf) int (42); delete p; // { dg-warning "memory on the stack" } } // { dg-prune-output "-Wfree-nonheap-object" } + +void test_4 (void) +{ + int buf[5]; // { dg-message "region created on stack here" } + int *p = new (&buf[2]) int (42); + delete p; // { dg-warning "memory on the stack" } +} + + +// { dg-prune-output "-Wfree-nonheap-object" } + +void test_write_placement_after_delete (void) +{ + short *s = ::new short; + short *lp = ::new (s) short; + ::delete s; + *lp = 12; /* { dg-warning "use after 'delete' of 'lp'" "write placement new after buffer deletion" } */ +} + +void test_read_placement_after_delete (void) +{ + short *s = ::new short; + short *lp = ::new (s) short; + ::delete s; + short m = *lp; // { dg-warning "use after 'delete' of 'lp'" "read placement new after buffer deletion" } +} + +struct A +{ + int x; + int y; +}; + +void test_use_placement_after_destruction (void) +{ + A a; + int *lp = ::new (&a.y) int; + *lp = 2; /* { dg-bogus "-Wanalyzer-use-of-uninitialized-value" } */ + a.~A(); + int m = *lp; /* { dg-warning "use of uninitialized value '\\*lp'" "use of placement after the underlying buffer was destructed." } */ +} + +void test_initialization_through_placement (void) +{ + int x; + int *p = ::new (&x) int; + *p = 10; + int z = x + 2; /* { dg-bogus "use of uninitialized value 'x'" "x has been initialized through placement pointer" } */ +} + +void test_partial_initialization_through_placement (void) +{ + char buf[4]; + char *p = ::new (&buf[2]) char; + *p = 10; + char *y = ::new (&buf[0]) char; + char z = buf[2] + 2; /* { dg-bogus "use of uninitialized value" } */ + z = *y + 2; /* { dg-warning "use of uninitialized value '\\*y'" "y has only been partially initialized" } */ +} \ No newline at end of file