From patchwork Wed Jul 20 17:59:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Li, Pan2 via Gcc-patches" X-Patchwork-Id: 81 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:e252:0:0:0:0:0 with SMTP id bl18csp1071423wrb; Wed, 20 Jul 2022 11:01:10 -0700 (PDT) X-Google-Smtp-Source: AGRyM1tL1hV2ah/I0wZfG2muBbiAILPXvPEBFEtPpiQELPgX7mQxtN9Ws+NSyy5nTKk7qedOX8LS X-Received: by 2002:a05:6402:248f:b0:43a:b89c:20e3 with SMTP id q15-20020a056402248f00b0043ab89c20e3mr52684745eda.335.1658340070767; Wed, 20 Jul 2022 11:01:10 -0700 (PDT) Received: from sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id b18-20020a056402351200b0043a837b7216si2298296edd.28.2022.07.20.11.01.10 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Jul 2022 11:01:10 -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=yaPiijX7; arc=fail (signature failed); 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 3DA7F3858285 for ; Wed, 20 Jul 2022 18:01:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3DA7F3858285 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1658340069; bh=U5Vksq6PN7+/3jXYeVxHUzx+2T4cbtoUT4lU73t1+Uk=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=yaPiijX7D/3b9kY/R2pBJE9YDX7A3NMzyR7CvEoDuGvn26/x8q1vHurutmoxl/V8A AFXcm2JsUJJPJdqmREq+P2nDdBzf7PQvOql98BOOIywztCEnCC8BFSwT1lCM9+amiA MyblnUu5ohp5oPgbB2FStryv7Jx9d/W6SvQzmXDM= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from NAM10-DM6-obe.outbound.protection.outlook.com (mail-dm6nam10olkn2098.outbound.protection.outlook.com [40.92.41.98]) by sourceware.org (Postfix) with ESMTPS id CB9103858C83 for ; Wed, 20 Jul 2022 18:00:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org CB9103858C83 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=eGQFiRrEQF/oZM16WTUaZbELCAH207N+IyuBpOaLa9o5zWLKwcOgSZ22/sNdC+cFY3EVGwv0uWfX4b2VakfrhK6f/qgjiioMYx5js1AlhA8jCbwLv3K9+EO7vp7u3Y26ZksZDhn9AtTKE6qViYnRF8BYKFVD0eKwOj7On31G87hKjxzpuEmv8Z/vdWV7MU3NOUNlLm0UtrWLUH/99i3ae723v/Fh9PvKts0u6BtZ6SI+ALAk93nLfWm/u+/tBstsNpbzYV7s9+uLBb5HEMbugYECCydAiw+jKUxHtM3hRclS6e48gSd/t8f6YfjOS4GsxqzFceSlUgDW23HTIs/T4A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=U5Vksq6PN7+/3jXYeVxHUzx+2T4cbtoUT4lU73t1+Uk=; b=iO2bpg+iuppBwXDIKEuEprdRsGruIjsSMRPZ2WFeIYDhqx+fTd+3ZIQJotJBCidNW0edz4tFmw0PJAhyVH32oeOuBAT5XufXnYtaaR0Twae09en/pizQdvOx1dh/5OEMsRWY5a22ja+X3EvWZeZ6/Vme7eojrtFpK/WQyqacyy/LN/ZBew0l3uBTVMwzMWKxlw4TfQIRizB2KAFmrZTRPIv7Dc/HsmzYWauNiiRswanQQs612SgEmuqfs3P1YbvZD3fZWkLPN27wNQ2TVorwnGrFa2i1OX5qaH5t2eR0NCcspH3Pd4+3+b7dcVVxR4ZQL+bgyS+NqFqTlpK7yHeJfQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none Received: from CY4PR1801MB1910.namprd18.prod.outlook.com (2603:10b6:910:79::14) by MWHPR18MB1280.namprd18.prod.outlook.com (2603:10b6:320:2b::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5458.18; Wed, 20 Jul 2022 18:00:17 +0000 Received: from CY4PR1801MB1910.namprd18.prod.outlook.com ([fe80::4d74:63e5:d5ee:3647]) by CY4PR1801MB1910.namprd18.prod.outlook.com ([fe80::4d74:63e5:d5ee:3647%2]) with mapi id 15.20.5438.021; Wed, 20 Jul 2022 18:00:17 +0000 To: gcc-patches@gcc.gnu.org Subject: [PATCH] Adding three new function attributes for static analysis of file descriptors Date: Wed, 20 Jul 2022 23:29:51 +0530 Message-ID: X-Mailer: git-send-email 2.25.1 X-TMN: [7pxa7FhOj/3E1rBKL0QX/dduqdkr2t3aJQZ8d6HPTghoHSXvp2o1cdBGdl6bbkpV] X-ClientProxiedBy: PN0PR01CA0047.INDPRD01.PROD.OUTLOOK.COM (2603:1096:c01:49::11) To CY4PR1801MB1910.namprd18.prod.outlook.com (2603:10b6:910:79::14) X-Microsoft-Original-Message-ID: <20220720175951.8413-1-mirimmad@outlook.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 7828bcaa-9a74-494a-8ad5-08da6a79b3d3 X-MS-Exchange-SLBlob-MailProps: LzDHi3doVoLy55UZr329xWl5UP+TODzJ1UUsvuMtinhzPpn2g/4plcVFBmBUsFoZwnLRfz3b5d6TlaeOgF9wZG5QSABBcA1rOmxGk7YP5h8R14+Mn6cGRs0Ewb5+kQz0khVePeEUEcBIGFuvKpxNDIbARdpsHEvmYI/G2STQozGtZqujK5AkuZNaNc4hDHPp6BcVRHXY3tjg/ZLXUagRx9uLYQTL4uqseYm5l1gS/EgRK48rUXLKMSGrM9FzrMJu8U4Lib+Fda9fwIcZ/MUBqMkZXoHbmgzY5j2UnrT5IfZcZT6zkMAJcvec5LaYRmA/qsMtw1i5tXR0iWH9pSoI/ArD1unWvc7BohBk/yuvDCAw+biDoiK5SpEYv+w5O9DZQzUIIAr6XLLQX9ZKRCHi+SSzS2yPtt538QiHt3GixIdHtrQ4yH2oLgz8mIB0UJVwbq08y7YfX9FyPf/Ab6oZEM5Xg+fFW2etPfbuV+dRqLvKDqTN9w6+t7afJlgidRdKiHC1Wzs8BJ84LesG9NFxkLZyJ7HEQiwhmsxdAMEGEYKmS2IRmD/fslsWgozJm4mSeLyVO9TicOGBJBPJQWMuHw3grtZaxQYRpLtbGjhQN1oIB2nfAexQAGbXstta17TAxUvtwS8pUrJuiZRc6wX6chuXdqoy+Ku6jkooUTRn4sf+Pdxk3ZQDwLzBn4ZS2awYGDMhDu9dUafuVRQN+rQzbzlI//VdxdCohmzUqjAEX8s= X-MS-TrafficTypeDiagnostic: MWHPR18MB1280:EE_ X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: dJ6uAC5enir8Ujr2qyD9fvgY1wvSFmCFdApwMBkcC8ZaAaVRUOCBUAfCB4cUWgvyiMClI08GQJWq2ou366NFo6vnbxzBD9ABHFh2zG1gW+U38kJ8+54RindYy/55kkYlxYPsJV+1a10+I/qZDYX3fTBlixVLoSaH3qEI+eC280Trp77AFS7F+/BA5fQDa1kIsXXyQrVnB9PYamnvkrLziY00nw530paaTukfa3jgaqsqAFaPnpn9Fab+mJRvHbY8QBhThEHC7Mj4I01yeMrdxWJ3wJq13/yaLlFjOhwCRB9NAwR4ljMTW1Yv3amEsL1ijdEBXCdD//D73nhVtlfhyIWdfxLmmIbwUviIep/BSIduq2T1N5rbaluJHI3zTc/iqe14yzJ7EQOgRo5zbA5PKaSrdsiBCC6KKJjGjk257G1SlchpxmdM/YK4Vya5NIBKWabdUwByKL4GZ6ImZVYZsJB+sGJlx6wPpcOt1/eg1SVrJ2WdDIuEHq8Km1OhIl/IyJn0wAudsJgLBITDTIzlBfm6F6lbHKxVOkBTVe5CK3A/Bib4umdseuPafFGHiSYqKBSUbYOfvKF2pYCSHVEQwhXi7SlNCyX5RJsPkNFKjvGzYhBLTxy5BMSEzaVeEawt X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: uJTbw+RHuAAeDM9bTg2jYNmPAx/7AYN6xI9QOq6UNFyfqbttCxcFJtPaIDyyBO5j+siw9FiUAGbNRNAR6ESWBjeKDrdz4mTsUgLStATR8Ad3LDBzSFyXLclBz+rzawW5Rljtd7r+UUcigyHd5u+Gah6ahFOjSdoTSfpqgAqJnolGlUvOpiNUCy6h+vO3bP7yw4z73o68n7k1Fk8LHWxFNBLxUGfcPp6Ms2mp2k8Hx3lCi+rUluGToumpL9HCUR4posKaSs043pRKPW4yxdAgsfMEM2p3Nz90Jho9MJPjdSV1sI3rd9928pejjK4jb3+8Q9hXjbagH/3TUao0pXd+ojEkNgdyz8Hk0xfqoXHweCxaX5atPc6smB2broNEWgLiSVw68fzR2AyKUOMeRHnq8UQPU8UJIQJCeBCSmUT+1EtDj+4Hd8nfe6d1rGqSrOvbt/Pb3v2dgYP1T1aEf2F+fwsjs6FyltqbDABzZ4KuXudHo3oN1+TJBfqLhmbkzjEWPfS3RYWFrfx+8gswvYkXeDdAHxUDM5YzZFpWJk+cfEbBmuiK068vNgp/PMM+Aq05IOAmZ8vQ+Swrdio6X3OaULY74XsewkGlVOv8yCo656B430psZhHRof71f+jebLdyzNBX47wCS10sRmNg2nmHt3zSbUkEKVz5S6CibsbLBYELNLgradP6gYmMW9m4U0DI/X3JscNVicP4DPa6RcmWcOq3ukVSOc1sZ6qHcZQkYuPsLxuvSXslt95Y5SGv5N7Egf4eDoi8dTdZYCQ+NYCXMtK6vYSTCBjEQ8fmN6mgEo/7Yw/pNE125d5SS5aMmpqmYwtcG301tFIUMSFDCzmf7USKB9Nkmjyf++pTIOYytBztOkfFX8zlsUwgUN4hUE7NJFao9lgAqEcmSVpiZC6IZ24pxJGUyiznfbAGKBSa065ggH4KW0kNaXHgTWhbxRoVqqGMmF57LeH7jgxb3rNcdPjT9eoWL9RRA2vmIwStcwBTcYCfVmT6Mz5ZV1lLDePb0jQtZD0JTGAkiBPHn/QMABdzWNSXe/Pec1pCeCwJtih24d4CqYQ9eExCF/GktVJUl+DL+UdspM8bvQ2zTeDdzVZlV6KoQoWqMy8w2T0ionWp4sHNCUw67OfeUQ+1HadryQxT5l6czNB1JkC9wQuWNVTgLNpFlL1l+KkGWecNTjwH3F74hktA5KLSzDZsu/wYB8x9w56JEwwGnsRF2TN45/4gjuoObDGjEM+kOgrCIKEGDqoTGNXe38/Ixq8S0Y+geFYUthQIg2NaHV9HNx2s1BTwcXjF67Ppyzdc94avP+aQuRHichjerz6ElXb0gNx1hNMdN6AGuEmOi62HsifMfg== X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7828bcaa-9a74-494a-8ad5-08da6a79b3d3 X-MS-Exchange-CrossTenant-AuthSource: CY4PR1801MB1910.namprd18.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jul 2022 18:00:17.2855 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MWHPR18MB1280 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, FREEMAIL_REPLYTO, FREEMAIL_REPLYTO_END_DIGIT, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_PASS, TXREP, T_FILL_THIS_FORM_SHORT 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: Immad Mir via Gcc-patches From: "Li, Pan2 via Gcc-patches" Reply-To: mirimnan017@gmail.com Cc: Immad Mir , mirimnan017@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?1738895597770420802?= X-GMAIL-MSGID: =?utf-8?q?1738895597770420802?= This patch adds three new function attributes to GCC that are used for static analysis of usage of file descriptors: 1) __attribute__ ((fd_arg(N))): The attributes may be applied to a function that takes on open file descriptor at refrenced argument N. It indicates that the passed filedescriptor must not have been closed. Therefore, when the analyzer is enabled with -fanalyzer, the analyzer may emit a -Wanalyzer-fd-use-after-close diagnostic if it detects a code path in which a function with this attribute is called with a closed file descriptor. The attribute also indicates that the file descriptor must have been checked for validity before usage. Therefore, analyzer may emit -Wanalyzer-fd-use-without-check diagnostic if it detects a code path in which a function with this attribute is called with a file descriptor that has not been checked for validity. 2) __attribute__((fd_arg_read(N))): The attribute is identical to fd_arg, but with the additional requirement that it might read from the file descriptor, and thus, the file descriptor must not have been opened as write-only. The analyzer may emit a -Wanalyzer-access-mode-mismatch diagnostic if it detects a code path in which a function with this attribute is called on a file descriptor opened with O_WRONLY. 3) __attribute__((fd_arg_write(N))): The attribute is identical to fd_arg_read except that the analyzer may emit a -Wanalyzer-access-mode-mismatch diagnostic if it detects a code path in which a function with this attribute is called on a file descriptor opened with O_RDONLY. gcc/analyzer/ChangeLog: * sm-fd.cc (fd_param_diagnostic): New diagnostic class. (fd_access_mode_mismatch): Change inheritance from fd_diagnostic to fd_param_diagnostic. Add new overloaded constructor. (fd_use_after_close): Likewise. (unchecked_use_of_fd): Likewise and also change name to fd_use_without_check. (double_close): Change name to fd_double_close. (enum access_directions): New. (fd_state_machine::on_stmt): Handle calls to function with the new three function attributes. (fd_state_machine::check_for_fd_attrs): New. (fd_state_machine::on_open): Use the new overloaded constructors of diagnostic classes. gcc/c-family/ChangeLog: * c-attribs.cc: (c_common_attribute_table): add three new attributes namely: fd_arg, fd_arg_read and fd_arg_write. (handle_fd_arg_attribute): New. gcc/ChangeLog: * doc/extend.texi: Add fd_arg, fd_arg_read and fd_arg_write under "Common Function Attributes" section. * doc/invoke.texi: Add docs to -Wanalyzer-fd-access-mode-mismatch, -Wanalyzer-use-after-close, -Wanalyzer-fd-use-without-check that these warnings may be emitted through usage of three function attributes used for static analysis of file descriptors namely fd_arg, fd_arg_read and fd_arg_write. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/fd-5.c: New test. * c-c++-common/attr-fd.c: New test. Signed-off-by: Immad Mir --- gcc/analyzer/sm-fd.cc | 335 +++++++++++++++++++++------ gcc/c-family/c-attribs.cc | 32 +++ gcc/doc/extend.texi | 37 +++ gcc/doc/invoke.texi | 17 +- gcc/testsuite/c-c++-common/attr-fd.c | 18 ++ gcc/testsuite/gcc.dg/analyzer/fd-5.c | 53 +++++ 6 files changed, 422 insertions(+), 70 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/attr-fd.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-5.c diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index 8e4300b06e2..bb89c471f7e 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -39,10 +39,13 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/analyzer-selftests.h" #include "tristate.h" #include "selftest.h" +#include "stringpool.h" +#include "attribs.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" +#include "bitmap.h" #if ENABLE_ANALYZER @@ -59,6 +62,13 @@ enum access_mode WRITE_ONLY }; +enum access_directions +{ + DIRS_READ_WRITE, + DIRS_READ, + DIRS_WRITE +}; + class fd_state_machine : public state_machine { public: @@ -146,7 +156,7 @@ private: void check_for_open_fd (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call, const tree callee_fndecl, - enum access_direction access_fn) const; + enum access_directions access_fn) const; void make_valid_transitions_on_condition (sm_context *sm_ctxt, const supernode *node, @@ -156,6 +166,10 @@ private: const supernode *node, const gimple *stmt, const svalue *lhs) const; + void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const tree callee_fndecl, + const char *attr_name, + access_directions fd_attr_access_dir) const; }; /* Base diagnostic class relative to fd_state_machine. */ @@ -220,6 +234,68 @@ protected: tree m_arg; }; +class fd_param_diagnostic : public fd_diagnostic +{ +public: + fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl, + const char *attr_name, int arg_idx) + : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), + m_attr_name (attr_name), m_arg_idx (arg_idx) + { + } + + fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl) + : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), + m_attr_name (NULL), m_arg_idx (-1) + { + } + + bool + subclass_equal_p (const pending_diagnostic &base_other) const override + { + const fd_param_diagnostic &sub_other + = (const fd_param_diagnostic &)base_other; + return (same_tree_p (m_arg, sub_other.m_arg) + && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl) + && m_arg_idx == sub_other.m_arg_idx + && ((m_attr_name) ? (m_attr_name == sub_other.m_attr_name) : true)); + } + + void + inform_filedescriptor_attribute (access_directions fd_dir) + { + + if (m_attr_name) + switch (fd_dir) + { + case DIRS_READ_WRITE: + inform (DECL_SOURCE_LOCATION (m_callee_fndecl), + "argument %d of %qD must be an open file descriptor, due to " + "%<__attribute__((%s(%d)))%>", + m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); + break; + case DIRS_WRITE: + inform (DECL_SOURCE_LOCATION (m_callee_fndecl), + "argument %d of %qD must be a readable file descriptor, due " + "to %<__attribute__((%s(%d)))%>", + m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); + break; + case DIRS_READ: + inform (DECL_SOURCE_LOCATION (m_callee_fndecl), + "argument %d of %qD must be a writable file descriptor, due " + "to %<__attribute__((%s(%d)))%>", + m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); + break; + } + } + +protected: + tree m_callee_fndecl; + const char *m_attr_name; + /* ARG_IDX is 0-based. */ + int m_arg_idx; +}; + class fd_leak : public fd_diagnostic { public: @@ -290,18 +366,26 @@ private: diagnostic_event_id_t m_open_event; }; -class fd_access_mode_mismatch : public fd_diagnostic +class fd_access_mode_mismatch : public fd_param_diagnostic { public: fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, - enum access_direction fd_dir, - const tree callee_fndecl) - : fd_diagnostic (sm, arg), m_fd_dir (fd_dir), - m_callee_fndecl (callee_fndecl) + enum access_directions fd_dir, + const tree callee_fndecl, const char *attr_name, + int arg_idx) + : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx), + m_fd_dir (fd_dir) { } + fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, + enum access_directions fd_dir, + const tree callee_fndecl) + : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir) + { + } + const char * get_kind () const final override { @@ -317,29 +401,25 @@ public: bool emit (rich_location *rich_loc) final override { + bool warned; switch (m_fd_dir) { - case DIR_READ: - return warning_at (rich_loc, get_controlling_option (), - "%qE on % file descriptor %qE", + case DIRS_READ: + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on read-only file descriptor %qE", m_callee_fndecl, m_arg); - case DIR_WRITE: - return warning_at (rich_loc, get_controlling_option (), - "%qE on % file descriptor %qE", + break; + case DIRS_WRITE: + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on write-only file descriptor %qE", m_callee_fndecl, m_arg); + break; default: gcc_unreachable (); } - } - - bool - subclass_equal_p (const pending_diagnostic &base_other) const override - { - const fd_access_mode_mismatch &sub_other - = (const fd_access_mode_mismatch &)base_other; - return (same_tree_p (m_arg, sub_other.m_arg) - && m_callee_fndecl == sub_other.m_callee_fndecl - && m_fd_dir == sub_other.m_fd_dir); + if (warned) + inform_filedescriptor_attribute (m_fd_dir); + return warned; } label_text @@ -347,11 +427,11 @@ public: { switch (m_fd_dir) { - case DIR_READ: - return ev.formatted_print ("%qE on % file descriptor %qE", + case DIRS_READ: + return ev.formatted_print ("%qE on read-only file descriptor %qE", m_callee_fndecl, m_arg); - case DIR_WRITE: - return ev.formatted_print ("%qE on % file descriptor %qE", + case DIRS_WRITE: + return ev.formatted_print ("%qE on write-only file descriptor %qE", m_callee_fndecl, m_arg); default: gcc_unreachable (); @@ -359,21 +439,20 @@ public: } private: - enum access_direction m_fd_dir; - const tree m_callee_fndecl; + enum access_directions m_fd_dir; }; -class double_close : public fd_diagnostic +class fd_double_close : public fd_diagnostic { public: - double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) + fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) { } const char * get_kind () const final override { - return "double_close"; + return "fd_double_close"; } int @@ -418,12 +497,19 @@ private: diagnostic_event_id_t m_first_close_event; }; -class fd_use_after_close : public fd_diagnostic +class fd_use_after_close : public fd_param_diagnostic { public: + fd_use_after_close (const fd_state_machine &sm, tree arg, + const tree callee_fndecl, const char *attr_name, + int arg_idx) + : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx) + { + } + fd_use_after_close (const fd_state_machine &sm, tree arg, const tree callee_fndecl) - : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl) + : fd_param_diagnostic (sm, arg, callee_fndecl) { } @@ -442,9 +528,13 @@ public: bool emit (rich_location *rich_loc) final override { - return warning_at (rich_loc, get_controlling_option (), + bool warned; + warned = warning_at (rich_loc, get_controlling_option (), "%qE on closed file descriptor %qE", m_callee_fndecl, m_arg); + if (warned) + inform_filedescriptor_attribute (DIRS_READ_WRITE); + return warned; } label_text @@ -466,32 +556,38 @@ public: describe_final_event (const evdesc::final_event &ev) final override { if (m_first_close_event.known_p ()) - return ev.formatted_print ( - "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl, - m_arg, "close", &m_first_close_event); - else - return ev.formatted_print ("%qE on closed file descriptor %qE", - m_callee_fndecl, m_arg); + return ev.formatted_print ( + "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl, + m_arg, "close", &m_first_close_event); + else + return ev.formatted_print ("%qE on closed file descriptor %qE", + m_callee_fndecl, m_arg); } private: diagnostic_event_id_t m_first_close_event; - const tree m_callee_fndecl; }; -class unchecked_use_of_fd : public fd_diagnostic +class fd_use_without_check : public fd_param_diagnostic { public: - unchecked_use_of_fd (const fd_state_machine &sm, tree arg, - const tree callee_fndecl) - : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl) + fd_use_without_check (const fd_state_machine &sm, tree arg, + const tree callee_fndecl, const char *attr_name, + int arg_idx) + : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx) + { + } + + fd_use_without_check (const fd_state_machine &sm, tree arg, + const tree callee_fndecl) + : fd_param_diagnostic (sm, arg, callee_fndecl) { } const char * get_kind () const final override { - return "unchecked_use_of_fd"; + return "fd_use_without_check"; } int @@ -503,18 +599,12 @@ public: bool emit (rich_location *rich_loc) final override { - return warning_at (rich_loc, get_controlling_option (), - "%qE on possibly invalid file descriptor %qE", - m_callee_fndecl, m_arg); - } - - bool - subclass_equal_p (const pending_diagnostic &base_other) const override - { - const unchecked_use_of_fd &sub_other - = (const unchecked_use_of_fd &)base_other; - return (same_tree_p (m_arg, sub_other.m_arg) - && m_callee_fndecl == sub_other.m_callee_fndecl); + bool warned; + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on possibly invalid file descriptor %qE", + m_callee_fndecl, m_arg); + if (warned) + inform_filedescriptor_attribute (DIRS_READ_WRITE); } label_text @@ -541,8 +631,7 @@ public: } private: - diagnostic_event_id_t m_first_open_event; - const tree m_callee_fndecl; + diagnostic_event_id_t m_first_open_event; }; fd_state_machine::fd_state_machine (logger *logger) @@ -647,11 +736,117 @@ fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, on_read (sm_ctxt, node, stmt, call, callee_fndecl); return true; } // "read" + + + { + // Handle __attribute__((fd_arg)) + + check_for_fd_attrs (sm_ctxt, node, stmt, callee_fndecl, "fd_arg", DIRS_READ_WRITE); + + // Handle __attribute__((fd_arg_read)) + + check_for_fd_attrs (sm_ctxt, node, stmt, callee_fndecl, "fd_arg_read", DIRS_READ); + + // Handle __attribute__((fd_arg_write)) + + check_for_fd_attrs (sm_ctxt, node, stmt, callee_fndecl, "fd_arg_write", DIRS_WRITE); + + return true; + } + } return false; } +void +fd_state_machine::check_for_fd_attrs ( + sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const tree callee_fndecl, const char *attr_name, + access_directions fd_attr_access_dir) const +{ + + tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); + attrs = lookup_attribute (attr_name, attrs); + if (!attrs) + return; + + if (!TREE_VALUE (attrs)) + return; + + auto_bitmap argmap; + + for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx)) + { + unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1; + bitmap_set_bit (argmap, val); + } + if (bitmap_empty_p (argmap)) + return; + + for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (stmt); arg_idx++) + { + tree arg = gimple_call_arg (stmt, arg_idx); + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + state_t state = sm_ctxt->get_state (stmt, arg); + bool bit_set = bitmap_bit_p (argmap, arg_idx); + if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE) + continue; + if (bit_set) // Check if arg_idx is marked by any of the file descriptor + // attributes + { + + if (is_closed_fd_p (state)) + { + + sm_ctxt->warn (node, stmt, arg, + new fd_use_after_close (*this, diag_arg, + callee_fndecl, attr_name, + arg_idx)); + continue; + } + + if (!(is_valid_fd_p (state) || (state == m_stop))) + { + if (!is_constant_fd_p (state)) + sm_ctxt->warn (node, stmt, arg, + new fd_use_without_check (*this, diag_arg, + callee_fndecl, attr_name, + arg_idx)); + } + + switch (fd_attr_access_dir) + { + case DIRS_READ_WRITE: + break; + case DIRS_READ: + + if (is_writeonly_fd_p (state)) + { + sm_ctxt->warn ( + node, stmt, arg, + new fd_access_mode_mismatch (*this, diag_arg, DIRS_WRITE, + callee_fndecl, attr_name, arg_idx)); + } + + break; + case DIRS_WRITE: + + if (is_readonly_fd_p (state)) + { + sm_ctxt->warn ( + node, stmt, arg, + new fd_access_mode_mismatch (*this, diag_arg, DIRS_READ, + callee_fndecl, attr_name, arg_idx)); + } + + break; + } + } + } +} + + void fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call) const @@ -706,7 +901,7 @@ fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node, if (is_closed_fd_p (state)) { - sm_ctxt->warn (node, stmt, arg, new double_close (*this, diag_arg)); + sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg)); sm_ctxt->set_next_state (stmt, arg, m_stop); } } @@ -715,21 +910,21 @@ fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call, const tree callee_fndecl) const { - check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIR_READ); + check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ); } void fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call, const tree callee_fndecl) const { - check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIR_WRITE); + check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE); } void fd_state_machine::check_for_open_fd ( sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call, const tree callee_fndecl, - enum access_direction callee_fndecl_dir) const + enum access_directions callee_fndecl_dir) const { tree arg = gimple_call_arg (call, 0); tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); @@ -748,30 +943,32 @@ fd_state_machine::check_for_open_fd ( if (!is_constant_fd_p (state)) sm_ctxt->warn ( node, stmt, arg, - new unchecked_use_of_fd (*this, diag_arg, callee_fndecl)); + new fd_use_without_check (*this, diag_arg, callee_fndecl)); } switch (callee_fndecl_dir) { - case DIR_READ: + case DIRS_READ: if (is_writeonly_fd_p (state)) { tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); sm_ctxt->warn (node, stmt, arg, new fd_access_mode_mismatch ( - *this, diag_arg, DIR_WRITE, callee_fndecl)); + *this, diag_arg, DIRS_WRITE, callee_fndecl)); } break; - case DIR_WRITE: + case DIRS_WRITE: if (is_readonly_fd_p (state)) { tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); sm_ctxt->warn (node, stmt, arg, new fd_access_mode_mismatch ( - *this, diag_arg, DIR_READ, callee_fndecl)); + *this, diag_arg, DIRS_READ, callee_fndecl)); } break; + default: + gcc_unreachable (); } } } diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index c8d96723f4c..0ad3a1a7040 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -173,6 +173,7 @@ static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *); static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int, bool *); static tree handle_retain_attribute (tree *, tree, tree, int, bool *); +static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *); /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ @@ -555,6 +556,12 @@ const struct attribute_spec c_common_attribute_table[] = handle_dealloc_attribute, NULL }, { "tainted_args", 0, 0, true, false, false, false, handle_tainted_args_attribute, NULL }, + { "fd_arg", 1, 1, false, true, true, false, + handle_fd_arg_attribute, NULL}, + { "fd_arg_read", 1, 1, false, true, true, false, + handle_fd_arg_attribute, NULL}, + { "fd_arg_write", 1, 1, false, true, true, false, + handle_fd_arg_attribute, NULL}, { NULL, 0, 0, false, false, false, false, NULL, NULL } }; @@ -4521,6 +4528,31 @@ handle_nonnull_attribute (tree *node, tree name, return NULL_TREE; } +/* Handle the "fd_arg", "fd_arg_read" and "fd_arg_write" attributes */ + +static tree +handle_fd_arg_attribute (tree *node, tree name, tree args, + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + tree type = *node; + if (!args) + { + if (!prototype_p (type)) + { + error ("%qE attribute without arguments on a non-prototype", name); + *no_add_attrs = true; + } + return NULL_TREE; + } + + if (tree val = positional_argument (*node, name, TREE_VALUE (args), + INTEGER_TYPE)) + return NULL_TREE; + + *no_add_attrs = true; + return NULL_TREE; +} + /* Handle the "nonstring" variable attribute. */ static tree diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index dfbe33ac652..1d08324a70c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3007,6 +3007,43 @@ produced by @command{gold}. For other linkers that cannot generate resolution file, explicit @code{externally_visible} attributes are still necessary. +@item fd_arg +@itemx fd_arg (@var{N}) +@cindex @code{fd_arg} function attribute +The @code{fd_arg} attribute may be applied to a function that takes an open +file descriptor at referenced argument @var{N}. + +It indicates that the passed filedescriptor must not have been closed. +Therefore, when the analyzer is enabled with @option{-fanalyzer}, the +analyzer may emit a @option{-Wanalyzer-fd-use-after-close} diagnostic +if it detects a code path in which a function with this attribute is +called with a closed file descriptor. + +The attribute also indicates that the file descriptor must have been checked for +validity before usage. Therefore, analyzer may emit +@option{-Wanalyzer-fd-use-without-check} diagnostic if it detects a code path in +which a function with this attribute is called with a file descriptor that has +not been checked for validity. + +@item fd_arg_read +@itemx fd_arg_read (@var{N}) +@cindex @code{fd_arg_read} function attribute +The @code{fd_arg_read} is identical to @code{fd_arg}, but with the additional +requirement that it might read from the file descriptor, and thus, the file +descriptor must not have been opened as write-only. + +The analyzer may emit a @option{-Wanalyzer-access-mode-mismatch} +diagnostic if it detects a code path in which a function with this +attribute is called on a file descriptor opened with @code{O_WRONLY}. + +@item fd_arg_write +@itemx fd_arg_write (@var{N}) +@cindex @code{fd_arg_write} function attribute +The @code{fd_arg_write} is identical to @code{fd_arg_read} except that the +analyzer may emit a @option{-Wanalyzer-access-mode-mismatch} diagnostic if +it detects a code path in which a function with this attribute is called on a +file descriptor opened with @code{O_RDONLY}. + @item flatten @cindex @code{flatten} function attribute Generally, inlining into a function is limited. For a function marked with diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d5ff1018372..9234275ca6d 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -9843,7 +9843,12 @@ This warning requires @option{-fanalyzer}, which enables it; use to disable it. This diagnostic warns for paths through code in which a -@code{read} on a write-only file descriptor is attempted, or vice versa +@code{read} on a write-only file descriptor is attempted, or vice versa. + +This diagnostic also warns for code paths in a which a function with attribute +@code{fd_arg_read (N)} is called with a file descriptor opened with @code{O_WRONLY} +at refrenced argument @code{N} or a function with attribute @code{fd_arg_write (N)} +is called with a file descriptor opened with @code{O_RDONLY} at refrenced argument @var{N}, @item -Wno-analyzer-fd-double-close @opindex Wanalyzer-fd-double-close @@ -9875,6 +9880,11 @@ to disable it. This diagnostic warns for paths through code in which a read or write is called on a closed file descriptor. +This diagnostic also warns for paths through code in which +a function with attribute @code{fd_arg (N)} or @code{fd_arg_read (N)} +or @code{fd_arg_write (N)} is called with a closed file descriptor at +refrenced argument @code{N}. + @item -Wno-analyzer-fd-use-without-check @opindex Wanalyzer-fd-use-without-check @opindex Wno-analyzer-fd-use-without-check @@ -9885,6 +9895,11 @@ to disable it. This diagnostic warns for paths through code in which a file descriptor is used without being checked for validity. +This diagnostic also warns for paths through code in which +a function with attribute @code{fd_arg (N)} or @code{fd_arg_read (N)} +or @code{fd_arg_write (N)} is called with a file descriptor, at refrenced +argument @code{N}, without being checked for validity. + @item -Wno-analyzer-file-leak @opindex Wanalyzer-file-leak @opindex Wno-analyzer-file-leak diff --git a/gcc/testsuite/c-c++-common/attr-fd.c b/gcc/testsuite/c-c++-common/attr-fd.c new file mode 100644 index 00000000000..292c329856e --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-fd.c @@ -0,0 +1,18 @@ + +int not_a_fn __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute only applies to function types" } */ + +void f (char *p) __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute argument value '1' refers to parameter type 'char \\\*'" } */ + + +int not_a_fn_b __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute only applies to function types" } */ + +void g (char *p) __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute argument value '1' refers to parameter type 'char \\\*'" } */ + + +int not_a_fn_c __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute only applies to function types" } */ + +void f (char *p) __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute argument value '1' refers to parameter type 'char \\\*'" } */ + + +void fn_a (int fd) __attribute__ ((fd_arg(0))); /* { dg-warning "'fd_arg' attribute argument value '0' does not refer to a function parameter" } */ +void fd_a_1 (int fd) __attribute__ ((fd_arg("notint"))); /* { dg-warning "'fd_arg' attribute argument has type 'char\\\[7\\\]'" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-5.c b/gcc/testsuite/gcc.dg/analyzer/fd-5.c new file mode 100644 index 00000000000..9805f7f79c5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/fd-5.c @@ -0,0 +1,53 @@ +int open(const char *, int mode); +void close(int fd); +int write (int fd, void *buf, int nbytes); +int read (int fd, void *buf, int nbytes); + +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 + +void f (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'f' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */ + +void +test_1 (const char *path) +{ + int fd = open (path, O_RDWR); + close(fd); + f(fd); /* { dg-warning "'f' on closed file descriptor 'fd'" } */ + /* { dg-message "\\(3\\) 'f' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" { target *-*-* } .-1 } */ +} + +void g (int fd) __attribute__((fd_arg_read(1))); /* { dg-message "argument 1 of 'g' must be a readable file descriptor, due to '__attribute__\\(\\(fd_arg_read\\(1\\)\\)\\)'" } */ + +void +test_2 (const char *path) +{ + int fd = open (path, O_WRONLY); + if (fd != -1) + { + g (fd); /* { dg-warning "'g' on 'write-only' file descriptor 'fd'" } */ + } + close (fd); +} + +void h (int fd) __attribute__((fd_arg_write(1))); /* { dg-message "argument 1 of 'h' must be a writable file descriptor, due to '__attribute__\\(\\(fd_arg_write\\(1\\)\\)\\)'" } */ +void +test_3 (const char *path) +{ + int fd = open (path, O_RDONLY); + if (fd != -1) + { + h (fd); /* { dg-warning "'h' on 'read-only' file descriptor 'fd'" } */ + } + close(fd); +} + +void ff (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'ff' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */ + +void test_4 (const char *path) +{ + int fd = open (path, O_RDWR); + ff (fd); /* { dg-warning "'ff' on possibly invalid file descriptor 'fd'" } */ + close(fd); +} \ No newline at end of file