From patchwork Fri Jul 15 15:38:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Immad Mir X-Patchwork-Id: 56100 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8CE713856DD6 for ; Fri, 15 Jul 2022 15:39:16 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8CE713856DD6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1657899556; bh=1Q8Wu/gtsn0Uy0vo9FmiFWwg4wFEpDK+DL4yTkbMjEs=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=Alj2iMFr+ROWxgHUKGRzF2d+enJbAGnJtQtf3Ftm02xtzHwUmxFHStuPMD9Xq9GKC mYvRjsBO2lhFgIXVdMgkz6ssMW6oPcawfBN2q0MDE4gl4zz2u23NpulLIF5VXEzS+U VFS5lvP1PKfnWI9DWPtx1mhmiV9RQGH95reoBrZw= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from NAM02-DM3-obe.outbound.protection.outlook.com (mail-dm3nam02olkn2066.outbound.protection.outlook.com [40.92.43.66]) by sourceware.org (Postfix) with ESMTPS id 2D1733857BBD for ; Fri, 15 Jul 2022 15:38:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 2D1733857BBD ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=IwuXzhYFTEs6+7BkT2pa3M1PSRiNREd1l2v6AquI3ZsycJVkBnCbTILC0M6h5d4kUasIGJ7ySpaMUNmNUPxTN1/EqqYNJ4KCz4laqY+37BdshwGQUV6iXvqpPM0CEDNeZj0v6BkIze1cheB0yV3yc2xTTgslznw8fpzVx1BjO/QKR/da+Y0Z4gj8UNsFGv0UBxzuBhbFvkxEcZX9Xbizt/k6p1AwqaNxzoFVX0Z8A0RkwYip9XhRf4lo57hemMDpnngNX0vCZo7qP3VyD0ycR1X+k8Fy2WydSPsU/viviWLtEzxNoH7xB90K2ffrGePCNOxM8TCamb8Nah+KGKtEBQ== 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=1Q8Wu/gtsn0Uy0vo9FmiFWwg4wFEpDK+DL4yTkbMjEs=; b=E1Q1XiJrVYXi7AiC27g1BXetZXW5vuU8t/8fSpj9sFSuMh+r7LhkscqqF5sy8qi2Ize3SMJNPCjXSnyxzSSuuM5FjxrMRiXehBdLlYgHGsQvu9IvaoEZHsMJ7RIF9Me/cDroqnX+v9I7vi8dVeIJ6e19S5TKAPo31as0aXvuE/7qyVu1Innvz5B4cLPliobq3LKFNmbRl/yN8iMavCMjZ8PdetTendpvKg2gdea83RFonibLap6ACfkxjYsp5w+HIcOV57zFvH6gvueebPCIYxbISJWeeWnckbBkRESVadg8QXn5hpJ/Rk9u4z1KNNxhGneuE1rDXAAYx5HBxJF6zQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none Received: from MWHPR1801MB1919.namprd18.prod.outlook.com (2603:10b6:301:64::30) by BY5PR18MB3425.namprd18.prod.outlook.com (2603:10b6:a03:1af::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5438.19; Fri, 15 Jul 2022 15:38:42 +0000 Received: from MWHPR1801MB1919.namprd18.prod.outlook.com ([fe80::2011:bbe1:6867:3eee]) by MWHPR1801MB1919.namprd18.prod.outlook.com ([fe80::2011:bbe1:6867:3eee%7]) with mapi id 15.20.5438.014; Fri, 15 Jul 2022 15:38:42 +0000 To: gcc-patches@gcc.gnu.org Subject: [PATCH] Adding three new function attributes for static analysis of file descriptors Date: Fri, 15 Jul 2022 21:08:16 +0530 Message-ID: X-Mailer: git-send-email 2.25.1 X-TMN: [IvmNhWOkneK3V6E/EdZI81slf/NmpxIvLe7p3t1APqsIH3LEv2UGIVnCg/Dr9tHe] X-ClientProxiedBy: BMXPR01CA0075.INDPRD01.PROD.OUTLOOK.COM (2603:1096:b00:54::15) To MWHPR1801MB1919.namprd18.prod.outlook.com (2603:10b6:301:64::30) X-Microsoft-Original-Message-ID: <20220715153816.10741-1-mirimmad@outlook.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 0808a290-36f8-4c49-9a17-08da66781852 X-MS-Exchange-SLBlob-MailProps: uIjBWfsAVx9c52Rras6kbyMilq/6aclYWVqM8CR4WaCoemeD3VbBk5Ksi7AWCjdWPJu0fmvjMGFya7YXshrosjCaSE9o3htyIylfFk7qF0krEbXomJfOTTNGByaSbGfhFeAXoVcv5krM03PRDmrWXp4c478f0hs+EZGinRWwvvCAJcBBRKlBIRcSF4zAA/qKRvR6qK8ycJLL0abY/z07OQXOkq+P4443/+mLcwV31OlSmB8xOZBgugqEmH1gHe9t5/hkM587+4DKuEMpTAzkSIV4DdWrOHyMokpD4El3roH1uQ2UYsFdyoGWn5IDRzt3o8C/gRV2lVXSCbR+BLnkcxguJ+jU8r6diGWbyAIoDKDn4L5Xc1b6wAn0D23po7NNOiT35M9OlgEfR+uG7knBlc62Niy4hq4v32BetAfwvarhq2ptZsJaXzfURDkLovyuEjZA2xOP903r1cGZXwBRijajZpg6JEzoVig9+ol9yFOlqvScrVlhDr7AoItPVgUf3Cs7NlQxC3cx7+V8pCjYUwonDHm8cmDFsSUPKC6UOmRB+EkXq4aLGOZNj6CIZSdwwClggIyzdk02i812n23G4cs4DU4R0spvLOvHL/jJPiVPlM5hn2GAbm2F2D1FteoWQhfz7nhU/FoktToEUl8wK0ejOJxvBOLC1S0gvmseE3UyME1HYKFpGIubGi5PU5Zuw/ahONGp6E5Jq8VAfzVHIA== X-MS-TrafficTypeDiagnostic: BY5PR18MB3425:EE_ X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: OS2V5LCkLI64/3/XzXmhERI70VZ0t9Y3mLXjOE5EAKmSY0f4ofkRVk/6L/r8qyagrpSO8k3s2rYFPUVEF8uaBVo1Piv7EpOEEikHaMklQA5qGlWTv+NbvDZLdQFVlJXJfRndrlyt8YUe6RwxwuljlVIHIj9Q1sXDaEuMBs0tDnrzDIvyFrtSVXxp3t9CstIZ5vgujBfZTrWqLs+dUN72zxLMG3ZRjoiPt17pfoFLuhHvEjIQQLLClQg/gTwjxxv9xS1eUh+nYHQ1jPEfxaBac2jEroP4ew0HQdE3kffcFmG+v70LZ+EkjxWOW4II4/0H6yHZhXj4lx7mWn8NaCAUrFyBucDmgfvjzJO5/OgPH2N8YjyffWpdKsfLUSwBS7RNMmPVzJNoV8FaxRpW9yY3b9r/ayIO57/VoVyyyJM2CkZYMQTyQBLX1DOPQFubI1w3UWBpXYwwAoWv4d0U1d3FSP2UAG2e7LEpBpVIm6oCBat4WiTy6HgcJ6IF7O36nS4hOAz9wfdAtiNzDJRhoCrudLZZ2k7yE53sJhXiRJXCQkRnSY/997n/Y4OLcqU5yzXJZ8PQG1p00VQqRp3O5UwtMRXlOEv3p/Tqyeec3nanX4XKjn/McPqwu9uVHysj1ad0d+7zrzmqHmOnE17a0ukT6JFUXeN87JUANaafYT8fBisS591dnqYnF1h0NPKT4Ba6acA9sadwxCl1agkt4h+clA== X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: Nw04oDxBV0ilSSH8lQJx8FCWWwveM85/hVJgHcGGfbrHSbmzCYsxhQxp2FL/jsKiEsaTK7MsSYaxxxnIEnN0altgdqwQmKQwGW1+kp97nFCwTuqIfIayRxMA+HMa/mlv+CgE+LT1kCW2y+3IvNNNJmIFivXZI5k0bgMmS+UR/lAPfGoCnZZBW6Ppt3CPPR68W8QJ3Of3+Vw34SLUYgJPwkoYTvEzHOTptzMhgK0Sk1uC2MDah4iIT5RKfTGx5nI7/7xkJNIPLabiwCfbRcxhCV8WiCYTu2moAR3bTX+Ykr49hdtCHo/FjKAnXBhiVRwZQvk1fg6ySaG5Nq9VgGd/2m4Q6XWZdGMCGcO/Bcjq+eu10AMRSYxjL3DGElVa2zzTPOFeQ4EyTXMyAFnUlWmqJ4eYlFk9gCPfq8Cm07LdqH7lsWQpF5H+sSED1piu2Sm7sWWNj2FeebY7qWLBcycB/OpEFpuKQJkDqWXd0TRdw4P3PQkq7VEdVCmHBB/uu4bbHIfkhjIkA00iFZzdW1kLyMGkiJV5OABsWmHe8buCHivAEfIG5AKde3+g8RWsn2BXdSS+jv9cfAqGCfkVat+b2in+mgy0e7XvMG+WEne5BxJXtVqnR/U+737uzOlbmJnc2ozrWOpTY5eVwNjbAkvad95oRucV7kjmJtABz3+KvHQknNEmYE9kdIApb3X/nCVyr7qtjue8xvUsXXjBPuPE46cPSH7k+dDXuEiKUOwBbUHyx2yElum6JPe2GYl5F5LY7EqOAFYD6zMCBJDHFJdU0R/rQXa7ZFdXEXmzYpfoAg7e1cyVC5yB5JjGQ1Y/D5w7yMuMH7xxIC/+/cIDxA6+RYFmxRsR8rd/sBU9rYS1EIcd4scZTqBSfAxoJh/3PS8u3f1cUcFNSfDCD1Z618xjq9SuUPFOd/pKGlKFvvkFfztKqbQAnKd80tClVu5VRnEmYAMX+Gqlcwa2lLvolWfzZi4ikpYHdirMbOeAxJR6qAp0Ava8lbXiJEfuuiBi65WjSiyJNjdxTHc6gZRFXmM/hK3fYiDhRzkNs/aedNJH0TEx324Y2p7H46DtT4H9zvK4MSluza3DPVGs3lZ24NF/PJoed1fUtqHhAKb8pEEm+59ubkpBZDi7BxcddcQmTi71ZjOKm3mhWIFR1l+l1LLumVJeaGDUKje5+76vaOJZhgMXzC3pC3CGbvsvKkYYDMTm+e7PZRpgf+TfypDnHGSJYZJ7NjQwlYMcASyrIR802YK5IFyaIhZBxADxJ+gw6Vc2x9Hwr+EbJeFokIpXm2D+UKdz88xdhipkK1CvD81u7A9CuOjRljSQoSK5TbkhfXYTi9hGo4FVFVmvHCGHdF4TIQ== X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0808a290-36f8-4c49-9a17-08da66781852 X-MS-Exchange-CrossTenant-AuthSource: MWHPR1801MB1919.namprd18.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Jul 2022 15:38:42.1365 (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: BY5PR18MB3425 X-Spam-Status: No, score=-11.5 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: Immad Mir Reply-To: mirimnan017@gmail.com Cc: Immad Mir , mirimnan017@gmail.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" --- gcc/analyzer/sm-fd.cc | 257 ++++++++++++++++++++++++--- gcc/c-family/c-attribs.cc | 115 ++++++++++++ gcc/doc/extend.texi | 19 ++ gcc/testsuite/gcc.dg/analyzer/fd-5.c | 53 ++++++ gcc/testsuite/gcc.dg/analyzer/fd-6.c | 14 ++ 5 files changed, 431 insertions(+), 27 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-6.c diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index 8e4300b06e2..20018dfd12b 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 fd_access_direction +{ + DIR_READ_WRITE, + DIR_READ, + DIR_WRITE +}; + class fd_state_machine : public state_machine { public: @@ -104,6 +114,8 @@ public: bool is_readonly_fd_p (state_t s) const; bool is_writeonly_fd_p (state_t s) const; enum access_mode get_access_mode_from_flag (int flag) const; + void inform_filedescriptor_attribute (tree callee_fndecl, int arg, + fd_access_direction fd_dir) const; /* State for a constant file descriptor (>= 0) */ state_t m_constant_fd; @@ -146,7 +158,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 fd_access_direction access_fn) const; void make_valid_transitions_on_condition (sm_context *sm_ctxt, const supernode *node, @@ -156,6 +168,12 @@ private: const supernode *node, const gimple *stmt, const svalue *lhs) const; + bitmap get_fd_attrs (const char *attr_name, tree callee_fndecl) const; + void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl, bitmap argmap, + fd_access_direction fd_attr_access_dir) const; + }; /* Base diagnostic class relative to fd_state_machine. */ @@ -294,10 +312,10 @@ class fd_access_mode_mismatch : public fd_diagnostic { public: fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, - enum access_direction fd_dir, - const tree callee_fndecl) + enum fd_access_direction fd_dir, + const tree callee_fndecl, bool attr, int arg_idx) : fd_diagnostic (sm, arg), m_fd_dir (fd_dir), - m_callee_fndecl (callee_fndecl) + m_callee_fndecl (callee_fndecl), m_attr (attr), m_arg_idx (arg_idx) { } @@ -317,19 +335,27 @@ 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 (), + warned = warning_at (rich_loc, get_controlling_option (), "%qE on % file descriptor %qE", m_callee_fndecl, m_arg); + break; case DIR_WRITE: - return warning_at (rich_loc, get_controlling_option (), + warned = warning_at (rich_loc, get_controlling_option (), "%qE on % file descriptor %qE", m_callee_fndecl, m_arg); + break; default: gcc_unreachable (); } + if (warned && m_attr) + { + m_sm.inform_filedescriptor_attribute (m_callee_fndecl, m_arg_idx, m_fd_dir); + } + return warned; } bool @@ -359,8 +385,10 @@ public: } private: - enum access_direction m_fd_dir; + enum fd_access_direction m_fd_dir; const tree m_callee_fndecl; + bool m_attr; + int m_arg_idx; }; class double_close : public fd_diagnostic @@ -422,8 +450,9 @@ class fd_use_after_close : public fd_diagnostic { public: fd_use_after_close (const fd_state_machine &sm, tree arg, - const tree callee_fndecl) - : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl) + const tree callee_fndecl, bool attr, int arg_idx) + : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), m_attr (attr), + m_arg_idx (arg_idx) { } @@ -439,12 +468,27 @@ public: return OPT_Wanalyzer_fd_use_after_close; } + bool + subclass_equal_p (const pending_diagnostic &base_other) const override + { + const fd_use_after_close &sub_other + = (const fd_use_after_close &)base_other; + return (same_tree_p (m_arg, sub_other.m_arg) + && m_callee_fndecl == sub_other.m_callee_fndecl + && m_attr == sub_other.m_attr + && m_arg_idx == sub_other.m_arg_idx); + } + 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 && m_attr) + m_sm.inform_filedescriptor_attribute (m_callee_fndecl, m_arg_idx, DIR_READ_WRITE); + return warned; } label_text @@ -466,25 +510,29 @@ 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; + bool m_attr; + int m_arg_idx; }; class unchecked_use_of_fd : public fd_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) + const tree callee_fndecl, bool attr, + int arg_idx) + : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), + m_attr (attr), m_arg_idx (arg_idx) { } @@ -503,9 +551,13 @@ 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 warned; + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on possibly invalid file descriptor %qE", + m_callee_fndecl, m_arg); + if (warned && m_attr) + m_sm.inform_filedescriptor_attribute (m_callee_fndecl, m_arg_idx, + DIR_READ_WRITE); } bool @@ -514,7 +566,9 @@ public: 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); + && m_callee_fndecl == sub_other.m_callee_fndecl + && m_attr == sub_other.m_attr + && m_arg_idx == sub_other.m_arg_idx); } label_text @@ -543,8 +597,40 @@ public: private: diagnostic_event_id_t m_first_open_event; const tree m_callee_fndecl; + bool m_attr; + int m_arg_idx; + }; +/* Issue a note regarding misuse of a file descriptor. + ARG_IDX here is 0-based. + */ +void +fd_state_machine::inform_filedescriptor_attribute ( + tree callee_fndecl, int arg_idx, fd_access_direction fd_dir) const +{ + switch (fd_dir) + { + case READ_WRITE: + inform (DECL_SOURCE_LOCATION (callee_fndecl), + "argument %d of %qD must be an open file descriptor", arg_idx + 1, + callee_fndecl); + break; + case DIR_WRITE: + inform (DECL_SOURCE_LOCATION (callee_fndecl), + "argument %d of %qD must be a read-only file descriptor", + arg_idx + 1, callee_fndecl); + break; + case DIR_READ: + inform (DECL_SOURCE_LOCATION (callee_fndecl), + "argument %d of %qD must be a write-only file descriptor", + arg_idx + 1, callee_fndecl); + break; + } +} + + + fd_state_machine::fd_state_machine (logger *logger) : state_machine ("file-descriptor", logger), m_constant_fd (add_state ("fd-constant")), @@ -647,11 +733,126 @@ 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)) + bitmap argmap = get_fd_attrs ("fd_arg", callee_fndecl); + if (argmap) + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, argmap, DIR_READ_WRITE); + + // Handle __attribute__((fd_arg_read)) + bitmap read_argmap = get_fd_attrs ("fd_arg_read", callee_fndecl); + if(read_argmap) + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, read_argmap, DIR_READ); + + // Handle __attribute__((fd_arg_write)) + bitmap write_argmap = get_fd_attrs ("fd_arg_write", callee_fndecl); + if (write_argmap) + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, write_argmap, DIR_WRITE); + + return true; + } + } return false; } +void +fd_state_machine::check_for_fd_attrs (sm_context *sm_ctxt, + const supernode *node, const gimple *stmt, + const gcall *call, + const tree callee_fndecl, bitmap argmap, + fd_access_direction fd_attr_access_dir) const +{ + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + { + unsigned int arg_idx = i; + tree arg = gimple_call_arg (stmt, i); + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + state_t state = sm_ctxt->get_state (stmt, arg); + if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE) + continue; + if (bitmap_bit_p (argmap, arg_idx)) // check if the arg is 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, true, + arg_idx)); + continue; + } + else + { + if (!(is_valid_fd_p (state) || (state == m_stop))) + { + if (!is_constant_fd_p (state)) + sm_ctxt->warn (node, stmt, arg, + new unchecked_use_of_fd (*this, diag_arg, + callee_fndecl, true, + arg_idx)); + } + } + } + switch (fd_attr_access_dir) + { + case DIR_READ: + if (bitmap_bit_p (argmap, arg_idx)) // check if the arg is marked by + // reads_from_fd attribute + { + if (is_writeonly_fd_p (state)) + { + sm_ctxt->warn (node, stmt, arg, + new fd_access_mode_mismatch (*this, diag_arg, + DIR_WRITE, + callee_fndecl, true, i)); + } + } + break; + case DIR_WRITE: + if (bitmap_bit_p (argmap, arg_idx)) // check if the arg is marked by + // writes_to_fd attribute + { + if (is_readonly_fd_p (state)) + { + sm_ctxt->warn (node, stmt, arg, + new fd_access_mode_mismatch ( + *this, diag_arg, DIR_READ, callee_fndecl, true, i)); + } + } + break; + } + } +} + +bitmap +fd_state_machine::get_fd_attrs (const char *attr_name, tree callee_fndecl) const +{ + bitmap argmap = NULL; + tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); + attrs = lookup_attribute (attr_name, attrs); + if (!attrs) + return argmap; + + if (!TREE_VALUE (attrs)) + return argmap; + + argmap = BITMAP_ALLOC (NULL); + + 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); + } + return argmap; +} + + void fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call) const @@ -729,7 +930,7 @@ 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 fd_access_direction callee_fndecl_dir) const { tree arg = gimple_call_arg (call, 0); tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); @@ -738,7 +939,7 @@ fd_state_machine::check_for_open_fd ( if (is_closed_fd_p (state)) { sm_ctxt->warn (node, stmt, arg, - new fd_use_after_close (*this, diag_arg, callee_fndecl)); + new fd_use_after_close (*this, diag_arg, callee_fndecl, false, -1)); } else @@ -748,7 +949,7 @@ 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 unchecked_use_of_fd (*this, diag_arg, callee_fndecl, false, -1)); } switch (callee_fndecl_dir) { @@ -758,7 +959,7 @@ fd_state_machine::check_for_open_fd ( 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, DIR_WRITE, callee_fndecl, false, -1)); } break; @@ -769,9 +970,11 @@ fd_state_machine::check_for_open_fd ( 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, DIR_READ, callee_fndecl, false, -1)); } break; + default: + gcc_unreachable (); } } } diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index c8d96723f4c..1a8f91d6222 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -173,6 +173,10 @@ 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 *); +static tree handle_fd_arg_read_attribute (tree *, tree, tree, int, bool *); +static tree handle_fd_arg_write_attribute (tree *, tree, tree, int, bool *); + /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ @@ -427,6 +431,12 @@ const struct attribute_spec c_common_attribute_table[] = handle_tls_model_attribute, NULL }, { "nonnull", 0, -1, false, true, true, false, handle_nonnull_attribute, NULL }, + { "fd_arg", 0, -1, false, true, true, false, + handle_fd_arg_attribute, NULL}, + { "fd_arg_read", 0, -1, false, true, true, false, + handle_fd_arg_read_attribute, NULL}, + { "fd_arg_write", 0, -1, false, true, true, false, + handle_fd_arg_write_attribute, NULL}, { "nonstring", 0, 0, true, false, false, false, handle_nonstring_attribute, NULL }, { "nothrow", 0, 0, true, false, false, false, @@ -4521,6 +4531,111 @@ handle_nonnull_attribute (tree *node, tree name, return NULL_TREE; } +/* Handle the "fd_arg" attribute */ + +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; + } + + for (int i = 1; args; ++i) + { + tree pos = TREE_VALUE (args); + tree next = TREE_CHAIN (args); + if (tree val = positional_argument (type, name, pos, INTEGER_TYPE, + next || i > 1 ? i : 0)) + TREE_VALUE (args) = val; + else + { + *no_add_attrs = true; + break; + } + args = next; + } + return NULL_TREE; +} + +/* Handle the "fd_arg_read" attribute. */ + +static tree +handle_fd_arg_read_attribute (tree *node, tree name, tree ARG_UNUSED (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; + } + + for (int i = 1; args; ++i) + { + tree pos = TREE_VALUE (args); + tree next = TREE_CHAIN (args); + if (tree val = positional_argument (type, name, pos, INTEGER_TYPE, + next || i > 1 ? i : 0)) + TREE_VALUE (args) = val; + else + { + *no_add_attrs = true; + break; + } + args = next; + } + return NULL_TREE; +} + + +/* Handle the "fd_arg_write" attribute. */ +static tree +handle_fd_arg_write_attribute (tree *node, tree name, tree ARG_UNUSED (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; + } + + for (int i = 1; args; ++i) + { + tree pos = TREE_VALUE (args); + tree next = TREE_CHAIN (args); + if (tree val = positional_argument (type, name, pos, INTEGER_TYPE, + next || i > 1 ? i : 0)) + TREE_VALUE (args) = val; + else + { + *no_add_attrs = true; + break; + } + args = next; + } + return NULL_TREE; +} + + + + /* Handle the "nonstring" variable attribute. */ static tree diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index dfbe33ac652..eba86e9f7ef 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3007,6 +3007,25 @@ produced by @command{gold}. For other linkers that cannot generate resolution file, explicit @code{externally_visible} attributes are still necessary. +@item fd_arg (@Var{N}, @Var{...}) + +The @Var{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 file +descriptor must not have been closed. + +@item fd_arg_read (@Var{N}, @Var{...}) + +The @Var{fd_arg_read} attribute may be applied to function that takes an open file +descriptor at referenced argument @Var{N} that it might read from. It +indicates that the passed file descriptor must not have been closed or +not opened as write-only. + +@item fd_arg_write (@Var{N}, @Var{...}) + +The @Var{fd_arg_write} attribute may be applied to a function that takes an open +file descriptor at referenced argument @Var{N} that it might write to. It indicates +that the passed file descriptor must not have been closed or not opened as read-only. + @item flatten @cindex @code{flatten} function attribute Generally, inlining into a function is limited. For a function marked with 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..6287ddd38fc --- /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" } */ + +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 read-only file descriptor" } */ + +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 write-only file descriptor" } */ +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" } */ + +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 diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-6.c b/gcc/testsuite/gcc.dg/analyzer/fd-6.c new file mode 100644 index 00000000000..7efb9e69b58 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/fd-6.c @@ -0,0 +1,14 @@ + +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 \\\*'" } */ \ No newline at end of file