From patchwork Wed Jul 28 10:19:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 1510789 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=gotplt.org header.i=@gotplt.org header.a=rsa-sha1 header.s=gotplt.org header.b=j91M/Us4; dkim-atps=neutral Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GZVDY0Dhnz9s5R for ; Wed, 28 Jul 2021 20:25:48 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9EC4A39A542C for ; Wed, 28 Jul 2021 10:25:45 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from olivedrab.birch.relay.mailchannels.net (olivedrab.birch.relay.mailchannels.net [23.83.209.135]) by sourceware.org (Postfix) with ESMTPS id B9E703994834 for ; Wed, 28 Jul 2021 10:19:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org B9E703994834 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 1DF6B5428C6; Wed, 28 Jul 2021 10:19:52 +0000 (UTC) Received: from pdx1-sub0-mail-a75.g.dreamhost.com (100-96-17-89.trex.outbound.svc.cluster.local [100.96.17.89]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 8C7F654231F; Wed, 28 Jul 2021 10:19:51 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a75.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.17.89 (trex/6.3.3); Wed, 28 Jul 2021 10:19:52 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Language-Society: 64d9ac8b52974bdc_1627467591891_1989574467 X-MC-Loop-Signature: 1627467591891:446409628 X-MC-Ingress-Time: 1627467591891 Received: from pdx1-sub0-mail-a75.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a75.g.dreamhost.com (Postfix) with ESMTP id 353098C384; Wed, 28 Jul 2021 03:19:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:mime-version:content-transfer-encoding; s=gotplt.org; bh=vOj8su+IVjU1iMOWYzHZ/VbPUzI=; b=j91M/Us4Q0J19Y +pWZFvjjHk/tJgU1mpX/QoLYQiAlTEHfyerT3wplWZrZSnFYEMVMDt4d/3H8sPOq NWlFVTWmQk0qh0mraUHdENJYNm/E8TzXxS0UD8UtVnLOIHwnXLJp9CSgl2Qj4yIH 9fdjCu2hsmREX66sw5j8nTI7lrPdg= Received: from rhbox.redhat.com (unknown [1.186.101.110]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a75.g.dreamhost.com (Postfix) with ESMTPSA id 5A5698C382; Wed, 28 Jul 2021 03:19:48 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a75 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH] analyzer: Handle strdup builtins Date: Wed, 28 Jul 2021 15:49:20 +0530 Message-Id: <20210728101920.3988489-1-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 X-Spam-Status: No, score=-3037.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_BARRACUDACENTRAL, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Consolidate allocator builtin handling and add support for __builtin_strdup and __builtin_strndup. gcc/analyzer/ChangeLog: * analyzer.cc (is_named_call_p, is_std_named_call_p): Make first argument a const_tree. * analyzer.h (is_named_call_p, -s_std_named_call_p): Likewise. * sm-malloc.cc (known_allocator_p): New function. (malloc_state_machine::on_stmt): Use it. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/strdup-1.c (test_4, test_5, test_6): New tests. --- gcc/analyzer/analyzer.cc | 8 ++--- gcc/analyzer/analyzer.h | 8 ++--- gcc/analyzer/sm-malloc.cc | 41 +++++++++++++++++++----- gcc/testsuite/gcc.dg/analyzer/strdup-1.c | 19 +++++++++++ 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index ddace9a0c32..b845b86cfe1 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -240,7 +240,7 @@ is_special_named_call_p (const gcall *call, const char *funcname, Compare with special_function_p in calls.c. */ bool -is_named_call_p (tree fndecl, const char *funcname) +is_named_call_p (const_tree fndecl, const char *funcname) { gcc_assert (fndecl); gcc_assert (funcname); @@ -292,7 +292,7 @@ is_std_function_p (const_tree fndecl) /* Like is_named_call_p, but look for std::FUNCNAME. */ bool -is_std_named_call_p (tree fndecl, const char *funcname) +is_std_named_call_p (const_tree fndecl, const char *funcname) { gcc_assert (fndecl); gcc_assert (funcname); @@ -314,7 +314,7 @@ is_std_named_call_p (tree fndecl, const char *funcname) arguments? */ bool -is_named_call_p (tree fndecl, const char *funcname, +is_named_call_p (const_tree fndecl, const char *funcname, const gcall *call, unsigned int num_args) { gcc_assert (fndecl); @@ -332,7 +332,7 @@ is_named_call_p (tree fndecl, const char *funcname, /* Like is_named_call_p, but check for std::FUNCNAME. */ bool -is_std_named_call_p (tree fndecl, const char *funcname, +is_std_named_call_p (const_tree fndecl, const char *funcname, const gcall *call, unsigned int num_args) { gcc_assert (fndecl); diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 90143d9aba2..8de5d60821f 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -220,11 +220,11 @@ enum access_direction extern bool is_special_named_call_p (const gcall *call, const char *funcname, unsigned int num_args); -extern bool is_named_call_p (tree fndecl, const char *funcname); -extern bool is_named_call_p (tree fndecl, const char *funcname, +extern bool is_named_call_p (const_tree fndecl, const char *funcname); +extern bool is_named_call_p (const_tree fndecl, const char *funcname, const gcall *call, unsigned int num_args); -extern bool is_std_named_call_p (tree fndecl, const char *funcname); -extern bool is_std_named_call_p (tree fndecl, const char *funcname, +extern bool is_std_named_call_p (const_tree fndecl, const char *funcname); +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); diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 1d69d57df0e..4f07d1f9257 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -1526,6 +1526,38 @@ malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl) return d; } +/* Try to identify the function declaration either by name or as a known malloc + builtin. */ + +static bool +known_allocator_p (const_tree fndecl, const gcall *call) +{ + /* Either it is a function we know by name and number of arguments... */ + if (is_named_call_p (fndecl, "malloc", call, 1) + || is_named_call_p (fndecl, "calloc", call, 2) + || is_std_named_call_p (fndecl, "malloc", call, 1) + || is_std_named_call_p (fndecl, "calloc", call, 2) + || is_named_call_p (fndecl, "strdup", call, 1) + || is_named_call_p (fndecl, "strndup", call, 2)) + return true; + + /* ... or it is a builtin allocator that allocates objects freed with + __builtin_free. */ + if (fndecl_built_in_p (fndecl)) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_MALLOC: + case BUILT_IN_CALLOC: + case BUILT_IN_STRDUP: + case BUILT_IN_STRNDUP: + return true; + default: + break; + } + + return false; +} + /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */ bool @@ -1536,14 +1568,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt, if (const gcall *call = dyn_cast (stmt)) if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) { - if (is_named_call_p (callee_fndecl, "malloc", call, 1) - || is_named_call_p (callee_fndecl, "calloc", call, 2) - || is_std_named_call_p (callee_fndecl, "malloc", call, 1) - || is_std_named_call_p (callee_fndecl, "calloc", call, 2) - || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1) - || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2) - || is_named_call_p (callee_fndecl, "strdup", call, 1) - || is_named_call_p (callee_fndecl, "strndup", call, 2)) + if (known_allocator_p (callee_fndecl, call)) { on_allocator_call (sm_ctxt, call, &m_free); return true; diff --git a/gcc/testsuite/gcc.dg/analyzer/strdup-1.c b/gcc/testsuite/gcc.dg/analyzer/strdup-1.c index 6b950ca23a9..9ac3921af21 100644 --- a/gcc/testsuite/gcc.dg/analyzer/strdup-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/strdup-1.c @@ -14,8 +14,27 @@ void test_2 (const char *s) char *p = strdup (s); free (p); } + void test_3 (const char *s) { char *p = strdup (s); /* { dg-message "this call could return NULL" } */ requires_nonnull (p); /* { dg-warning "use of possibly-NULL 'p'" } */ } + +/* Repeat tests for __builtin_strdup. */ +void test_4 (const char *s) +{ + char *p = __builtin_strdup (s); /* { dg-message "allocated here" } */ +} /* { dg-warning "leak of 'p'" } */ + +void test_5 (const char *s) +{ + char *p = __builtin_strdup (s); + free (p); +} + +void test_6 (const char *s) +{ + char *p = __builtin_strdup (s); /* { dg-message "this call could return NULL" } */ + requires_nonnull (p); /* { dg-warning "use of possibly-NULL 'p'" } */ +}