From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from NAM11-CO1-obe.outbound.protection.outlook.com (mail-co1nam11olkn2064.outbound.protection.outlook.com [40.92.18.64]) by sourceware.org (Postfix) with ESMTPS id 94528385840F for ; Mon, 25 Jul 2022 16:29:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 94528385840F ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=m1CrsB/NsQN7iq8jGpai1CShdmeDDIkaJf3NqQaBAjOYppsEvIRK88xYk35chlArxXbzQHC6gaA9YOJqn3GdutmB/bIRg+joV9YZmnJTNaWwm4+cVaHlS8QYQw+Dppr0Ba81wlGr4PtVkfYZCatfGtsJMYqn5vI2LBBj57ltTt/JhewceCvT+FO5Tw5LLukUpcuh1AeOUdZs/QsDvPt/6TRfFR750fkFAyPtEBqSMgFxOyZwxIMfbTyzJjw4tv2SD85035wa15Zn1+FsExNCTSfvyhv4BJDv3c2rgy+aNQt3/AdjEj8UR/I2ONLJoQWH36/SzTDsl9nPrcLu7OGHDg== 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=rA2SpeoDH168SvizkDimQsjH28v3HLgDQt+RZOuvTDM=; b=cBiMnutQqDpVU5843nUUZypFzOFHZtjKVWRlplGd3uovIicQXlk2/QZQIV2M8JTnumOcUz/MXPV/Di2IP+jeSsFraIE7fsZCqz8TtT1bSnctA0RxCrUY6i56kbi5cH1W++zuLQbbo8Qc99dqK9R1d3QrVzADZ769+HyZOQXM4jgjKuOWiP+7cpALA8d6HKhfn6umIyn8IbBFtGFcI+vMAcvlOQlWJ4sI6Uau2Z1fz6A/BIBSgxrAWxKHFCIfq9rWyKGxYNGxzhjaW8DAltL99H82bVps345fIVQE6OJdTDEwgQtKMXSkM7ljSuljZ2UopbC2Y3k6oZT58aix6886Og== 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 BN9PR18MB4202.namprd18.prod.outlook.com (2603:10b6:408:137::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5458.24; Mon, 25 Jul 2022 16:29:53 +0000 Received: from CY4PR1801MB1910.namprd18.prod.outlook.com ([fe80::c5c4:ae52:8e5f:e5c5]) by CY4PR1801MB1910.namprd18.prod.outlook.com ([fe80::c5c4:ae52:8e5f:e5c5%6]) with mapi id 15.20.5458.024; Mon, 25 Jul 2022 16:29:53 +0000 From: Immad Mir To: =?Windows-1252?Q?Martin_Li=9Aka?= , "dmalcolm@redhat.com" , "gcc-patches@gcc.gnu.org" Subject: RE: [PATCH] analyzer: fix coding style in sm-fd.cc Thread-Topic: [PATCH] analyzer: fix coding style in sm-fd.cc Thread-Index: AQHYn/H7eTB9ubC14kKchRVxnhO78a2PR5AA Date: Mon, 25 Jul 2022 16:29:53 +0000 Message-ID: References: In-Reply-To: Accept-Language: en-US Content-Language: en-IN X-MS-Has-Attach: X-MS-TNEF-Correlator: x-tmn: [hUqiDUZ7sSnsZ3zcvjSiS4Z8XVF3Lw0ZZDGm1rMn74HWSJtKSen06NL1zWgLodwC] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 6a15cf0f-457b-49c0-3df6-08da6e5ae745 x-ms-traffictypediagnostic: BN9PR18MB4202:EE_ x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: mxHqnSDUqTe7MuJ/Lc1clMCoAspJli2V203Ok2j+IACYMnEzSSyx+cpUH7G6mko8Zkyq6Cx/eQQr7WrcjQapGeKGHTediuavCFsBYoj6ZzUmS0qd9v7c5qAPX1qL13z5NJCnNldtOXXu7qiOD72A8NLyvdffdz3onpqag6a4MOlCsdIHVa+kx425Ut1pEpr+ObzmPUPrYD13+fmk/cnk4qfH3Lc+/phX8u5Am6HwOoyhJMEwNZgLM6T9n1idkfLPdVWWzVxfDdz/+7XGAQ0n2ch6fq38O7bDWO4ZG/mlzBxNoKr5HwTCsxGSZrlj6xzQEXDIrG2d+je56ILoEPaIKG/mUVDHW1T43AViCD2W7P86u49ilo8wSruy23atc0IZvdncawTiNs2dcznKSO+eMaOkzKNEaAkH/qE4Kh9S9f36T2el00CAQEmRWq+1wW6KesdTSKnKDHgl8WSDRVIPgu+07lWcTkixPWX0y8qlkhv6d68vguFNsmkd/vXwOO8VYddMrR019ow35VzM/agq8aiRxfrRVcvbsWzyl7yl38XPaenfdIa2DYZWplAnwJU8dlz9m3jUD9oBl0eSaKybNeo4Nk5rMRqlBtFlbTEZDs8l5b16/HxVnDJ3fLbjJ9KQfj1/BTsG8KzvoisJAZ0H9rgatMXK8aqUcONR5QCFVW/SRVfnkww1wpfSdKgH8TTjdCl6ekv705HEXYU8gf0dAq2rucuiXFLFyGIEr7f6Tec= x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?Windows-1252?Q?rTIdfLoab420sZtNOG/cFBVbzJsdraf5/L/g28nUlVKlCaZ5l6VR5vlO?= =?Windows-1252?Q?OlY/OgLhcNMbCVR94hdLroNsNGU80GLeTTqRC1uwZ4mkTNeUsqUwTLJ9?= =?Windows-1252?Q?pKBo466dBExO4Rx/tp31sJgtr9WIcdm5yETm7JRDqUEy9vrEwjuSYOsm?= =?Windows-1252?Q?REVFFujyQl/jCaO6c2IFNaLzHhyIRm3wp6DqTqm5BAc1rk1u2pQUOBig?= =?Windows-1252?Q?+ARS/L6oItpstLe0NnaAUomxq6gQQcxo2fBSjl2xrGkJmDu6N/HGIFIV?= =?Windows-1252?Q?fycbUNmIsu4TYvJHeVeyZkrAstw9VVxc+dg2yw+v41+OXpG/ideurF3c?= =?Windows-1252?Q?UK2NULm/yl8n6hPAGX+5s7eRsNTllA3cY830w8vN9ozeMIg4GsAKBTyw?= =?Windows-1252?Q?W/hr2wQW9CjKwMTvxqTCjAPx/CL8tLVSS59CFSkX0Hg0I1GFr96FtyYF?= =?Windows-1252?Q?I+poaQDI7GA2hmeFAcrwX0qfi8DZSQZkdJ5HFl16GvpKvKNqwXBfs8oG?= =?Windows-1252?Q?jgCCvcTiluxLgoffS8KzUKQDeQsg7ocy6bV9frKo1yjJX2EWAWNeJoxH?= =?Windows-1252?Q?WT5pkqjCxsmYT418aLBliCRFRz17ykl5zYWu/yT9aqAEM6zsxgkh3wcM?= =?Windows-1252?Q?mR0Ny4CI4j27ZkogpPZ73RA9CGacWMfNpHS4217U9dnGJhxUwSBDdDmT?= =?Windows-1252?Q?LCVp/+owWoWZpDAqk5uAEowwAdBj6BJSZrQRi0+DhKLaI6aTyHiPExgO?= =?Windows-1252?Q?iINjRZAqL02nyRM02zMbQAxROUVT3VZZpUng9EpznhiUl6218kajtTJQ?= =?Windows-1252?Q?p6B+I1nZciJsUc4B7XlGXIWnA+oM+I3UXroPQFtJg2PkQnV0QrhhJpno?= =?Windows-1252?Q?LWtzWBUPXLJLzn1Q1buDJVXkEIBm+Alk1EeZvSOr61plGuYI+9aQyitV?= =?Windows-1252?Q?xa32d8aXCDBB+klzHQ6oJWLYpHyJNHfDyvCbop3nlAh0DGPjzPJwgtKu?= =?Windows-1252?Q?WeN90+FoQ+rlcsjnzwGWzTIRt7dokVUbyaSbln1Ma3xBREoY1+IML1aD?= =?Windows-1252?Q?DmKJWBgy5HjbRQ/zl0OmyJF1ANnwr5AHmNMs3gEheRXMBlMp/9P3RvbM?= =?Windows-1252?Q?mjzoiANwJbdBC2MTtqNgo87vTqbLOwlfnreljY/7fFIeyUH8d3bA8RKw?= =?Windows-1252?Q?4Q4xIypv0UL+HkEXELwqZwthgURSaEiT3m5a/aKBX7JOzHnNZ7JyZVgd?= =?Windows-1252?Q?FJDiw45lMfqgzrOj6COQG2tDpNb2NYEmUY5h448SaqDF3qJntl+wH4mh?= =?Windows-1252?Q?fS3vy1A69ZAceLvkQsRwK7f+udlZB89aXzBencjst6NZW2ZwWFXEgb2L?= =?Windows-1252?Q?fR/omeou+P5bLKnnl1SNnh+qGwnfQdxy8k0BWosR/izutb6LqqKgbTMe?= =?Windows-1252?Q?QS6xR7rc70kFLMRWztrqAQ=3D=3D?= MIME-Version: 1.0 X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: CY4PR1801MB1910.namprd18.prod.outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 6a15cf0f-457b-49c0-3df6-08da6e5ae745 X-MS-Exchange-CrossTenant-originalarrivaltime: 25 Jul 2022 16:29:53.2220 (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: BN9PR18MB4202 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org Content-Type: text/plain; charset="Windows-1252" Content-Transfer-Encoding: quoted-printable X-Content-Filtered-By: Mailman/MimeDel 2.1.29 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-List-Received-Date: Mon, 25 Jul 2022 16:30:08 -0000 Thanks for this. I=92ll keep this in mind for future patches. Immad. Sent from Mail for Window= s 10 From: Martin Li=9Aka Sent: 25 July 2022 12:14 PM To: gcc-patches@gcc.gnu.org Cc: mirimmad@outlook.com; David Malcolm Subject: [PATCH] analyzer: fix coding style in sm-fd.cc Hi. First, thanks Mir for your contribution. The following patch addresses coding style issues I let you know in: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D106003#c3 Most notably, I converted Windows endlines to Unix style, replace 8 spaces = with tabs and removed trailing whitespaces. Please consider using our script that verifies that: git show f8e6e2c046e1015697356ee7079fb39e0cb6add5 > diff && ./contrib/check= _GNU_style.py diff Ready to be installed? Thanks, Martin gcc/analyzer/ChangeLog: * sm-fd.cc: Run dos2unix and fix coding style issues. --- gcc/analyzer/sm-fd.cc | 2114 ++++++++++++++++++++--------------------- 1 file changed, 1057 insertions(+), 1057 deletions(-) diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index c3dac48509e..56b0063ba42 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -1,1057 +1,1057 @@ -/* A state machine for detecting misuses of POSIX file descriptor APIs. - Copyright (C) 2019-2022 Free Software Foundation, Inc. - Contributed by Immad Mir . - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "diagnostic-path.h" -#include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" -#include "analyzer/analyzer-logging.h" -#include "analyzer/sm.h" -#include "analyzer/pending-diagnostic.h" -#include "analyzer/function-set.h" -#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 - -namespace ana { - -namespace { - -/* An enum for distinguishing between three different access modes. */ - -enum access_mode -{ - READ_WRITE, - READ_ONLY, - WRITE_ONLY -}; - -enum access_directions -{ - DIRS_READ_WRITE, - DIRS_READ, - DIRS_WRITE -}; - -class fd_state_machine : public state_machine -{ -public: - fd_state_machine (logger *logger); - - bool - inherited_state_p () const final override - { - return false; - } - - state_machine::state_t - get_default_state (const svalue *sval) const final override - { - if (tree cst =3D sval->maybe_get_constant ()) - { - if (TREE_CODE (cst) =3D=3D INTEGER_CST) - { - int val =3D TREE_INT_CST_LOW (cst); - if (val >=3D 0) - return m_constant_fd; - else - return m_invalid; - } - } - return m_start; - } - - bool on_stmt (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt) const final override; - - void on_condition (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const svalue *lhs, const tree_cod= e op, - const svalue *rhs) const final override; - - bool can_purge_p (state_t s) const final override; - pending_diagnostic *on_leak (tree var) const final override; - - bool is_unchecked_fd_p (state_t s) const; - bool is_valid_fd_p (state_t s) const; - bool is_closed_fd_p (state_t s) const; - bool is_constant_fd_p (state_t s) const; - 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; - - /* State for a constant file descriptor (>=3D 0) */ - state_t m_constant_fd; - - /* States representing a file descriptor that hasn't yet been - checked for validity after opening, for three different - access modes. */ - state_t m_unchecked_read_write; - - state_t m_unchecked_read_only; - - state_t m_unchecked_write_only; - - /* States for representing a file descriptor that is known to be valid (= >=3D - 0), for three different access modes.*/ - state_t m_valid_read_write; - - state_t m_valid_read_only; - - state_t m_valid_write_only; - - /* State for a file descriptor that is known to be invalid (< 0). */ - state_t m_invalid; - - /* State for a file descriptor that has been closed.*/ - state_t m_closed; - - /* State for a file descriptor that we do not want to track anymore . */ - state_t m_stop; - -private: - void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *= stmt, - const gcall *call) const; - void on_close (sm_context *sm_ctxt, const supernode *node, const gimple = *stmt, - const gcall *call) const; - void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *= stmt, - const gcall *call, const tree callee_fndecl) const; - void on_write (sm_context *sm_ctxt, const supernode *node, const gimple = *stmt, - const gcall *call, const tree callee_fndecl) const; - void check_for_open_fd (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, - const tree callee_fndecl, - enum access_directions access_fn) const; - - void make_valid_transitions_on_condition (sm_context *sm_ctxt, - const supernode *node, - const gimple *stmt, - const svalue *lhs) const; - void make_invalid_transitions_on_condition (sm_context *sm_ctxt, - 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 gcall *call, - const tree callee_fndecl, const char *attr_name= , - access_directions fd_attr_access_dir) const; -}; - -/* Base diagnostic class relative to fd_state_machine. */ -class fd_diagnostic : public pending_diagnostic -{ -public: - fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg = (arg) - { - } - - bool - subclass_equal_p (const pending_diagnostic &base_other) const override - { - return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg); - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (change.m_old_state =3D=3D m_sm.get_start_state () - && m_sm.is_unchecked_fd_p (change.m_new_state)) - { - if (change.m_new_state =3D=3D m_sm.m_unchecked_read_write) - return change.formatted_print ("opened here as read-write"); - - if (change.m_new_state =3D=3D m_sm.m_unchecked_read_only) - return change.formatted_print ("opened here as read-only"); - - if (change.m_new_state =3D=3D m_sm.m_unchecked_write_only) - return change.formatted_print ("opened here as write-only"); - } - - if (change.m_new_state =3D=3D m_sm.m_closed) - return change.formatted_print ("closed here"); - - if (m_sm.is_unchecked_fd_p (change.m_old_state) - && m_sm.is_valid_fd_p (change.m_new_state)) - { - if (change.m_expr) - return change.formatted_print ( - "assuming %qE is a valid file descriptor (>=3D 0)", change.m= _expr); - else - return change.formatted_print ("assuming a valid file descriptor= "); - } - - if (m_sm.is_unchecked_fd_p (change.m_old_state) - && change.m_new_state =3D=3D m_sm.m_invalid) - { - if (change.m_expr) - return change.formatted_print ( - "assuming %qE is an invalid file descriptor (< 0)", - change.m_expr); - else - return change.formatted_print ("assuming an invalid file descrip= tor"); - } - - return label_text (); - } - -protected: - const fd_state_machine &m_sm; - tree m_arg; -}; - -class fd_param_diagnostic : public fd_diagnostic -{ -public: - fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_f= ndecl, - 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_f= ndecl) - : 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 - =3D (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 =3D=3D sub_other.m_arg_idx - && ((m_attr_name) - ? (strcmp (m_attr_name, sub_other.m_attr_name) =3D=3D = 0) - : 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: - fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)= {} - - const char * - get_kind () const final override - { - return "fd_leak"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_leak; - } - - bool - emit (rich_location *rich_loc) final override - { - /*CWE-775: Missing Release of File Descriptor or Handle after Effectiv= e - Lifetime - */ - diagnostic_metadata m; - m.add_cwe (775); - if (m_arg) - return warning_meta (rich_loc, m, get_controlling_option (), - "leak of file descriptor %qE", m_arg); - else - return warning_meta (rich_loc, m, get_controlling_option (), - "leak of file descriptor"); - } - - label_text - describe_state_change (const evdesc::state_change &change) final overrid= e - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - { - m_open_event =3D change.m_event_id; - return label_text::borrow ("opened here"); - } - - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_open_event.known_p ()) - { - if (ev.m_expr) - return ev.formatted_print ("%qE leaks here; was opened at %@", - ev.m_expr, &m_open_event); - else - return ev.formatted_print ("leaks here; was opened at %@", - &m_open_event); - } - else - { - if (ev.m_expr) - return ev.formatted_print ("%qE leaks here", ev.m_expr); - else - return ev.formatted_print ("leaks here"); - } - } - -private: - diagnostic_event_id_t m_open_event; -}; - -class fd_access_mode_mismatch : public fd_param_diagnostic -{ -public: - fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, - 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 - { - return "fd_access_mode_mismatch"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_access_mode_mismatch; - } - - bool - emit (rich_location *rich_loc) final override - { - bool warned; - switch (m_fd_dir) - { - case DIRS_READ: - warned =3D warning_at (rich_loc, get_controlling_option (), - "%qE on read-only file descriptor %qE", - m_callee_fndecl, m_arg); - break; - case DIRS_WRITE: - warned =3D warning_at (rich_loc, get_controlling_option (), - "%qE on write-only file descriptor %qE", - m_callee_fndecl, m_arg); - break; - default: - gcc_unreachable (); - } - if (warned) - inform_filedescriptor_attribute (m_fd_dir); - return warned; - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - switch (m_fd_dir) - { - case DIRS_READ: - return ev.formatted_print ("%qE on read-only file descriptor %qE", - m_callee_fndecl, m_arg); - case DIRS_WRITE: - return ev.formatted_print ("%qE on write-only file descriptor %qE"= , - m_callee_fndecl, m_arg); - default: - gcc_unreachable (); - } - } - -private: - enum access_directions m_fd_dir; -}; - -class fd_double_close : public fd_diagnostic -{ -public: - fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (= sm, arg) - { - } - - const char * - get_kind () const final override - { - return "fd_double_close"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_double_close; - } - bool - emit (rich_location *rich_loc) final override - { - diagnostic_metadata m; - // CWE-1341: Multiple Releases of Same Resource or Handle - m.add_cwe (1341); - return warning_meta (rich_loc, m, get_controlling_option (), - "double % of file descriptor %qE", m_arg)= ; - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - return label_text::borrow ("opened here"); - - if (change.m_new_state =3D=3D m_sm.m_closed) - { - m_first_close_event =3D change.m_event_id; - return change.formatted_print ("first %qs here", "close"); - } - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_first_close_event.known_p ()) - return ev.formatted_print ("second %qs here; first %qs was at %@", - "close", "close", &m_first_close_event); - return ev.formatted_print ("second %qs here", "close"); - } - -private: - diagnostic_event_id_t m_first_close_event; -}; - -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_param_diagnostic (sm, arg, callee_fndecl) - { - } - - const char * - get_kind () const final override - { - return "fd_use_after_close"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_use_after_close; - } - - bool - emit (rich_location *rich_loc) final override - { - bool warned; - warned =3D warning_at (rich_loc, get_controlling_option (), - "%qE on closed file descriptor %qE", m_callee_fndec= l, - m_arg); - if (warned) - inform_filedescriptor_attribute (DIRS_READ_WRITE); - return warned; - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - return label_text::borrow ("opened here"); - - if (change.m_new_state =3D=3D m_sm.m_closed) - { - m_first_close_event =3D change.m_event_id; - return change.formatted_print ("closed here"); - } - - return fd_diagnostic::describe_state_change (change); - } - - label_text - 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_f= ndecl, - 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; -}; - -class fd_use_without_check : public fd_param_diagnostic -{ -public: - 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 "fd_use_without_check"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_use_without_check; - } - - bool - emit (rich_location *rich_loc) final override - { - bool warned; - warned =3D 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); - return warned; - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - { - m_first_open_event =3D change.m_event_id; - return label_text::borrow ("opened here"); - } - - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_first_open_event.known_p ()) - return ev.formatted_print ( - "%qE could be invalid: unchecked value from %@", m_arg, - &m_first_open_event); - else - return ev.formatted_print ("%qE could be invalid", m_arg); - } - -private: - diagnostic_event_id_t m_first_open_event; -}; - -fd_state_machine::fd_state_machine (logger *logger) - : state_machine ("file-descriptor", logger), - m_constant_fd (add_state ("fd-constant")), - m_unchecked_read_write (add_state ("fd-unchecked-read-write")), - m_unchecked_read_only (add_state ("fd-unchecked-read-only")), - m_unchecked_write_only (add_state ("fd-unchecked-write-only")), - m_valid_read_write (add_state ("fd-valid-read-write")), - m_valid_read_only (add_state ("fd-valid-read-only")), - m_valid_write_only (add_state ("fd-valid-write-only")), - m_invalid (add_state ("fd-invalid")), - m_closed (add_state ("fd-closed")), - m_stop (add_state ("fd-stop")) -{ -} - -bool -fd_state_machine::is_unchecked_fd_p (state_t s) const -{ - return (s =3D=3D m_unchecked_read_write - || s =3D=3D m_unchecked_read_only - || s =3D=3D m_unchecked_write_only); -} - -bool -fd_state_machine::is_valid_fd_p (state_t s) const -{ - return (s =3D=3D m_valid_read_write - || s =3D=3D m_valid_read_only - || s =3D=3D m_valid_write_only); -} - -enum access_mode -fd_state_machine::get_access_mode_from_flag (int flag) const -{ - /* FIXME: this code assumes the access modes on the host and - target are the same, which in practice might not be the case. */ - - if ((flag & O_ACCMODE) =3D=3D O_RDONLY) - { - return READ_ONLY; - } - else if ((flag & O_ACCMODE) =3D=3D O_WRONLY) - { - return WRITE_ONLY; - } - return READ_WRITE; -} - -bool -fd_state_machine::is_readonly_fd_p (state_t state) const -{ - return (state =3D=3D m_unchecked_read_only || state =3D=3D m_valid_read_= only); -} - -bool -fd_state_machine::is_writeonly_fd_p (state_t state) const -{ - return (state =3D=3D m_unchecked_write_only || state =3D=3D m_valid_writ= e_only); -} - -bool -fd_state_machine::is_closed_fd_p (state_t state) const -{ - return (state =3D=3D m_closed); -} - -bool -fd_state_machine::is_constant_fd_p (state_t state) const -{ - return (state =3D=3D m_constant_fd); -} - -bool -fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt) const -{ - if (const gcall *call =3D dyn_cast (stmt)) - if (tree callee_fndecl =3D sm_ctxt->get_fndecl_for_call (call)) - { - if (is_named_call_p (callee_fndecl, "open", call, 2)) - { - on_open (sm_ctxt, node, stmt, call); - return true; - } // "open" - - if (is_named_call_p (callee_fndecl, "close", call, 1)) - { - on_close (sm_ctxt, node, stmt, call); - return true; - } // "close" - - if (is_named_call_p (callee_fndecl, "write", call, 3)) - { - on_write (sm_ctxt, node, stmt, call, callee_fndecl); - return true; - } // "write" - - if (is_named_call_p (callee_fndecl, "read", call, 3)) - { - on_read (sm_ctxt, node, stmt, call, callee_fndecl); - return true; - } // "read" - - - { - // Handle __attribute__((fd_arg)) - - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, - "fd_arg", DIRS_READ_WRITE); - - // Handle __attribute__((fd_arg_read)) - - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, - "fd_arg_read", DIRS_READ); - - // Handle __attribute__((fd_arg_write)) - - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, - "fd_arg_write", DIRS_WRITE); - } - } - - 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, const char *attr_name, - access_directions fd_attr_access_dir) const -{ - - tree attrs =3D TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); - attrs =3D lookup_attribute (attr_name, attrs); - if (!attrs) - return; - - if (!TREE_VALUE (attrs)) - return; - - auto_bitmap argmap; - - for (tree idx =3D TREE_VALUE (attrs); idx; idx =3D TREE_CHAIN (idx)) - { - unsigned int val =3D TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1; - bitmap_set_bit (argmap, val); - } - if (bitmap_empty_p (argmap)) - return; - - for (unsigned arg_idx =3D 0; arg_idx < gimple_call_num_args (call); arg_= idx++) - { - tree arg =3D gimple_call_arg (call, arg_idx); - tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); - state_t state =3D sm_ctxt->get_state (stmt, arg); - bool bit_set =3D bitmap_bit_p (argmap, arg_idx); - if (TREE_CODE (TREE_TYPE (arg)) !=3D INTEGER_TYPE) - continue; - if (bit_set) // Check if arg_idx is marked by any of the file descri= ptor - // attributes - { - - if (is_closed_fd_p (state)) - { - - sm_ctxt->warn (node, stmt, arg, - new fd_use_after_close (*this, diag_arg, - callee_fndecl, attr_n= ame, - arg_idx)); - continue; - } - - if (!(is_valid_fd_p (state) || (state =3D=3D m_stop))) - { - if (!is_constant_fd_p (state)) - sm_ctxt->warn (node, stmt, arg, - new fd_use_without_check (*this, diag_arg, - callee_fndecl, att= r_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_W= RITE, - callee_fndecl, attr_nam= e, 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_R= EAD, - callee_fndecl, attr_nam= e, arg_idx)); - } - - break; - } - } - } -} - - -void -fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const -{ - tree lhs =3D gimple_call_lhs (call); - if (lhs) - { - tree arg =3D gimple_call_arg (call, 1); - if (TREE_CODE (arg) =3D=3D INTEGER_CST) - { - int flag =3D TREE_INT_CST_LOW (arg); - enum access_mode mode =3D get_access_mode_from_flag (flag); - - switch (mode) - { - case READ_ONLY: - sm_ctxt->on_transition (node, stmt, lhs, m_start, - m_unchecked_read_only); - break; - case WRITE_ONLY: - sm_ctxt->on_transition (node, stmt, lhs, m_start, - m_unchecked_write_only); - break; - default: - sm_ctxt->on_transition (node, stmt, lhs, m_start, - m_unchecked_read_write); - } - } - } - else - { - sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE)= ); - } -} - -void -fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const -{ - tree arg =3D gimple_call_arg (call, 0); - state_t state =3D sm_ctxt->get_state (stmt, arg); - tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); - - sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_close= d); - sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed= ); - sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_close= d); - sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed); - - if (is_closed_fd_p (state)) - { - sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg= )); - sm_ctxt->set_next_state (stmt, arg, m_stop); - } -} -void -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, 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, 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_directions callee_fndecl_dir) const -{ - tree arg =3D gimple_call_arg (call, 0); - tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); - state_t state =3D sm_ctxt->get_state (stmt, arg); - - if (is_closed_fd_p (state)) - { - sm_ctxt->warn (node, stmt, arg, - new fd_use_after_close (*this, diag_arg, callee_fndec= l)); - } - - else - { - if (!(is_valid_fd_p (state) || (state =3D=3D m_stop))) - { - if (!is_constant_fd_p (state)) - sm_ctxt->warn ( - node, stmt, arg, - new fd_use_without_check (*this, diag_arg, callee_fndecl))= ; - } - switch (callee_fndecl_dir) - { - case DIRS_READ: - if (is_writeonly_fd_p (state)) - { - tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); - sm_ctxt->warn (node, stmt, arg, - new fd_access_mode_mismatch ( - *this, diag_arg, DIRS_WRITE, callee_fndec= l)); - } - - break; - case DIRS_WRITE: - - if (is_readonly_fd_p (state)) - { - tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); - sm_ctxt->warn (node, stmt, arg, - new fd_access_mode_mismatch ( - *this, diag_arg, DIRS_READ, callee_fndecl= )); - } - break; - default: - gcc_unreachable (); - } - } -} - -void -fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node= , - const gimple *stmt, const svalue *lhs, - enum tree_code op, const svalue *rhs) cons= t -{ - if (tree cst =3D rhs->maybe_get_constant ()) - { - if (TREE_CODE (cst) =3D=3D INTEGER_CST) - { - int val =3D TREE_INT_CST_LOW (cst); - if (val =3D=3D -1) - { - if (op =3D=3D NE_EXPR) - make_valid_transitions_on_condition (sm_ctxt, node, stmt, = lhs); - - else if (op =3D=3D EQ_EXPR) - make_invalid_transitions_on_condition (sm_ctxt, node, stmt= , - lhs); - } - } - } - - if (rhs->all_zeroes_p ()) - { - if (op =3D=3D GE_EXPR) - make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs); - else if (op =3D=3D LT_EXPR) - make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs); - } -} - -void -fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt= , - const supernode *no= de, - const gimple *stmt, - const svalue *lhs) = const -{ - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, - m_valid_read_write); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, - m_valid_read_only); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, - m_valid_write_only); -} - -void -fd_state_machine::make_invalid_transitions_on_condition ( - sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const svalue *lhs) const -{ - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_inval= id); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invali= d); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_inval= id); -} - -bool -fd_state_machine::can_purge_p (state_t s) const -{ - if (is_unchecked_fd_p (s) || is_valid_fd_p (s)) - return false; - else - return true; -} - -pending_diagnostic * -fd_state_machine::on_leak (tree var) const -{ - return new fd_leak (*this, var); -} -} // namespace - -state_machine * -make_fd_state_machine (logger *logger) -{ - return new fd_state_machine (logger); -} -} // namespace ana - -#endif // ENABLE_ANALYZER \ No newline at end of file +/* A state machine for detecting misuses of POSIX file descriptor APIs. + Copyright (C) 2019-2022 Free Software Foundation, Inc. + Contributed by Immad Mir . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "function.h" +#include "basic-block.h" +#include "gimple.h" +#include "options.h" +#include "diagnostic-path.h" +#include "diagnostic-metadata.h" +#include "function.h" +#include "json.h" +#include "analyzer/analyzer.h" +#include "diagnostic-event-id.h" +#include "analyzer/analyzer-logging.h" +#include "analyzer/sm.h" +#include "analyzer/pending-diagnostic.h" +#include "analyzer/function-set.h" +#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 + +namespace ana { + +namespace { + +/* An enum for distinguishing between three different access modes. */ + +enum access_mode +{ + READ_WRITE, + READ_ONLY, + WRITE_ONLY +}; + +enum access_directions +{ + DIRS_READ_WRITE, + DIRS_READ, + DIRS_WRITE +}; + +class fd_state_machine : public state_machine +{ +public: + fd_state_machine (logger *logger); + + bool + inherited_state_p () const final override + { + return false; + } + + state_machine::state_t + get_default_state (const svalue *sval) const final override + { + if (tree cst =3D sval->maybe_get_constant ()) + { + if (TREE_CODE (cst) =3D=3D INTEGER_CST) + { + int val =3D TREE_INT_CST_LOW (cst); + if (val >=3D 0) + return m_constant_fd; + else + return m_invalid; + } + } + return m_start; + } + + bool on_stmt (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt) const final override; + + void on_condition (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const svalue *lhs, const tree_code= op, + const svalue *rhs) const final override; + + bool can_purge_p (state_t s) const final override; + pending_diagnostic *on_leak (tree var) const final override; + + bool is_unchecked_fd_p (state_t s) const; + bool is_valid_fd_p (state_t s) const; + bool is_closed_fd_p (state_t s) const; + bool is_constant_fd_p (state_t s) const; + 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; + + /* State for a constant file descriptor (>=3D 0) */ + state_t m_constant_fd; + + /* States representing a file descriptor that hasn't yet been + checked for validity after opening, for three different + access modes. */ + state_t m_unchecked_read_write; + + state_t m_unchecked_read_only; + + state_t m_unchecked_write_only; + + /* States for representing a file descriptor that is known to be valid (= >=3D + 0), for three different access modes. */ + state_t m_valid_read_write; + + state_t m_valid_read_only; + + state_t m_valid_write_only; + + /* State for a file descriptor that is known to be invalid (< 0). */ + state_t m_invalid; + + /* State for a file descriptor that has been closed. */ + state_t m_closed; + + /* State for a file descriptor that we do not want to track anymore . */ + state_t m_stop; + +private: + void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *= stmt, + const gcall *call) const; + void on_close (sm_context *sm_ctxt, const supernode *node, const gimple = *stmt, + const gcall *call) const; + void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *= stmt, + const gcall *call, const tree callee_fndecl) const; + void on_write (sm_context *sm_ctxt, const supernode *node, const gimple = *stmt, + const gcall *call, const tree callee_fndecl) const; + void check_for_open_fd (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl, + enum access_directions access_fn) const; + + void make_valid_transitions_on_condition (sm_context *sm_ctxt, + const supernode *node, + const gimple *stmt, + const svalue *lhs) const; + void make_invalid_transitions_on_condition (sm_context *sm_ctxt, + 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 gcall *call, + const tree callee_fndecl, const char *attr_name, + access_directions fd_attr_access_dir) const; +}; + +/* Base diagnostic class relative to fd_state_machine. */ +class fd_diagnostic : public pending_diagnostic +{ +public: + fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg = (arg) + { + } + + bool + subclass_equal_p (const pending_diagnostic &base_other) const override + { + return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg); + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (change.m_old_state =3D=3D m_sm.get_start_state () + && m_sm.is_unchecked_fd_p (change.m_new_state)) + { + if (change.m_new_state =3D=3D m_sm.m_unchecked_read_write) + return change.formatted_print ("opened here as read-write"); + + if (change.m_new_state =3D=3D m_sm.m_unchecked_read_only) + return change.formatted_print ("opened here as read-only"); + + if (change.m_new_state =3D=3D m_sm.m_unchecked_write_only) + return change.formatted_print ("opened here as write-only"); + } + + if (change.m_new_state =3D=3D m_sm.m_closed) + return change.formatted_print ("closed here"); + + if (m_sm.is_unchecked_fd_p (change.m_old_state) + && m_sm.is_valid_fd_p (change.m_new_state)) + { + if (change.m_expr) + return change.formatted_print ( + "assuming %qE is a valid file descriptor (>=3D 0)", change.m_= expr); + else + return change.formatted_print ("assuming a valid file descriptor"= ); + } + + if (m_sm.is_unchecked_fd_p (change.m_old_state) + && change.m_new_state =3D=3D m_sm.m_invalid) + { + if (change.m_expr) + return change.formatted_print ( + "assuming %qE is an invalid file descriptor (< 0)", + change.m_expr); + else + return change.formatted_print ("assuming an invalid file descript= or"); + } + + return label_text (); + } + +protected: + const fd_state_machine &m_sm; + tree m_arg; +}; + +class fd_param_diagnostic : public fd_diagnostic +{ +public: + fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_f= ndecl, + 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_f= ndecl) + : 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 + =3D (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 =3D=3D sub_other.m_arg_idx + && ((m_attr_name) + ? (strcmp (m_attr_name, sub_other.m_attr_name) =3D=3D 0= ) + : 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, d= ue " + "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, d= ue " + "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: + fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)= {} + + const char * + get_kind () const final override + { + return "fd_leak"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_leak; + } + + bool + emit (rich_location *rich_loc) final override + { + /*CWE-775: Missing Release of File Descriptor or Handle after Effectiv= e + Lifetime + */ + diagnostic_metadata m; + m.add_cwe (775); + if (m_arg) + return warning_meta (rich_loc, m, get_controlling_option (), + "leak of file descriptor %qE", m_arg); + else + return warning_meta (rich_loc, m, get_controlling_option (), + "leak of file descriptor"); + } + + label_text + describe_state_change (const evdesc::state_change &change) final overrid= e + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + { + m_open_event =3D change.m_event_id; + return label_text::borrow ("opened here"); + } + + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_open_event.known_p ()) + { + if (ev.m_expr) + return ev.formatted_print ("%qE leaks here; was opened at %@", + ev.m_expr, &m_open_event); + else + return ev.formatted_print ("leaks here; was opened at %@", + &m_open_event); + } + else + { + if (ev.m_expr) + return ev.formatted_print ("%qE leaks here", ev.m_expr); + else + return ev.formatted_print ("leaks here"); + } + } + +private: + diagnostic_event_id_t m_open_event; +}; + +class fd_access_mode_mismatch : public fd_param_diagnostic +{ +public: + fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, + 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 + { + return "fd_access_mode_mismatch"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_access_mode_mismatch; + } + + bool + emit (rich_location *rich_loc) final override + { + bool warned; + switch (m_fd_dir) + { + case DIRS_READ: + warned =3D warning_at (rich_loc, get_controlling_option (), + "%qE on read-only file descriptor %qE", + m_callee_fndecl, m_arg); + break; + case DIRS_WRITE: + warned =3D warning_at (rich_loc, get_controlling_option (), + "%qE on write-only file descriptor %qE", + m_callee_fndecl, m_arg); + break; + default: + gcc_unreachable (); + } + if (warned) + inform_filedescriptor_attribute (m_fd_dir); + return warned; + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + switch (m_fd_dir) + { + case DIRS_READ: + return ev.formatted_print ("%qE on read-only file descriptor %qE", + m_callee_fndecl, m_arg); + case DIRS_WRITE: + return ev.formatted_print ("%qE on write-only file descriptor %qE", + m_callee_fndecl, m_arg); + default: + gcc_unreachable (); + } + } + +private: + enum access_directions m_fd_dir; +}; + +class fd_double_close : public fd_diagnostic +{ +public: + fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (= sm, arg) + { + } + + const char * + get_kind () const final override + { + return "fd_double_close"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_double_close; + } + bool + emit (rich_location *rich_loc) final override + { + diagnostic_metadata m; + // CWE-1341: Multiple Releases of Same Resource or Handle + m.add_cwe (1341); + return warning_meta (rich_loc, m, get_controlling_option (), + "double % of file descriptor %qE", m_arg); + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + return label_text::borrow ("opened here"); + + if (change.m_new_state =3D=3D m_sm.m_closed) + { + m_first_close_event =3D change.m_event_id; + return change.formatted_print ("first %qs here", "close"); + } + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_first_close_event.known_p ()) + return ev.formatted_print ("second %qs here; first %qs was at %@", + "close", "close", &m_first_close_event); + return ev.formatted_print ("second %qs here", "close"); + } + +private: + diagnostic_event_id_t m_first_close_event; +}; + +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_param_diagnostic (sm, arg, callee_fndecl) + { + } + + const char * + get_kind () const final override + { + return "fd_use_after_close"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_use_after_close; + } + + bool + emit (rich_location *rich_loc) final override + { + bool warned; + warned =3D 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 + describe_state_change (const evdesc::state_change &change) override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + return label_text::borrow ("opened here"); + + if (change.m_new_state =3D=3D m_sm.m_closed) + { + m_first_close_event =3D change.m_event_id; + return change.formatted_print ("closed here"); + } + + return fd_diagnostic::describe_state_change (change); + } + + label_text + 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_fn= decl, + 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; +}; + +class fd_use_without_check : public fd_param_diagnostic +{ +public: + 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 "fd_use_without_check"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_use_without_check; + } + + bool + emit (rich_location *rich_loc) final override + { + bool warned; + warned =3D 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); + return warned; + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + { + m_first_open_event =3D change.m_event_id; + return label_text::borrow ("opened here"); + } + + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_first_open_event.known_p ()) + return ev.formatted_print ( + "%qE could be invalid: unchecked value from %@", m_arg, + &m_first_open_event); + else + return ev.formatted_print ("%qE could be invalid", m_arg); + } + +private: + diagnostic_event_id_t m_first_open_event; +}; + +fd_state_machine::fd_state_machine (logger *logger) + : state_machine ("file-descriptor", logger), + m_constant_fd (add_state ("fd-constant")), + m_unchecked_read_write (add_state ("fd-unchecked-read-write")), + m_unchecked_read_only (add_state ("fd-unchecked-read-only")), + m_unchecked_write_only (add_state ("fd-unchecked-write-only")), + m_valid_read_write (add_state ("fd-valid-read-write")), + m_valid_read_only (add_state ("fd-valid-read-only")), + m_valid_write_only (add_state ("fd-valid-write-only")), + m_invalid (add_state ("fd-invalid")), + m_closed (add_state ("fd-closed")), + m_stop (add_state ("fd-stop")) +{ +} + +bool +fd_state_machine::is_unchecked_fd_p (state_t s) const +{ + return (s =3D=3D m_unchecked_read_write + || s =3D=3D m_unchecked_read_only + || s =3D=3D m_unchecked_write_only); +} + +bool +fd_state_machine::is_valid_fd_p (state_t s) const +{ + return (s =3D=3D m_valid_read_write + || s =3D=3D m_valid_read_only + || s =3D=3D m_valid_write_only); +} + +enum access_mode +fd_state_machine::get_access_mode_from_flag (int flag) const +{ + /* FIXME: this code assumes the access modes on the host and + target are the same, which in practice might not be the case. */ + + if ((flag & O_ACCMODE) =3D=3D O_RDONLY) + { + return READ_ONLY; + } + else if ((flag & O_ACCMODE) =3D=3D O_WRONLY) + { + return WRITE_ONLY; + } + return READ_WRITE; +} + +bool +fd_state_machine::is_readonly_fd_p (state_t state) const +{ + return (state =3D=3D m_unchecked_read_only || state =3D=3D m_valid_read_= only); +} + +bool +fd_state_machine::is_writeonly_fd_p (state_t state) const +{ + return (state =3D=3D m_unchecked_write_only || state =3D=3D m_valid_writ= e_only); +} + +bool +fd_state_machine::is_closed_fd_p (state_t state) const +{ + return (state =3D=3D m_closed); +} + +bool +fd_state_machine::is_constant_fd_p (state_t state) const +{ + return (state =3D=3D m_constant_fd); +} + +bool +fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt) const +{ + if (const gcall *call =3D dyn_cast (stmt)) + if (tree callee_fndecl =3D sm_ctxt->get_fndecl_for_call (call)) + { + if (is_named_call_p (callee_fndecl, "open", call, 2)) + { + on_open (sm_ctxt, node, stmt, call); + return true; + } // "open" + + if (is_named_call_p (callee_fndecl, "close", call, 1)) + { + on_close (sm_ctxt, node, stmt, call); + return true; + } // "close" + + if (is_named_call_p (callee_fndecl, "write", call, 3)) + { + on_write (sm_ctxt, node, stmt, call, callee_fndecl); + return true; + } // "write" + + if (is_named_call_p (callee_fndecl, "read", call, 3)) + { + on_read (sm_ctxt, node, stmt, call, callee_fndecl); + return true; + } // "read" + + + { + // Handle __attribute__((fd_arg)) + + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + "fd_arg", DIRS_READ_WRITE); + + // Handle __attribute__((fd_arg_read)) + + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + "fd_arg_read", DIRS_READ); + + // Handle __attribute__((fd_arg_write)) + + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + "fd_arg_write", DIRS_WRITE); + } + } + + 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, const char *attr_name, + access_directions fd_attr_access_dir) const +{ + + tree attrs =3D TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); + attrs =3D lookup_attribute (attr_name, attrs); + if (!attrs) + return; + + if (!TREE_VALUE (attrs)) + return; + + auto_bitmap argmap; + + for (tree idx =3D TREE_VALUE (attrs); idx; idx =3D TREE_CHAIN (idx)) + { + unsigned int val =3D TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1; + bitmap_set_bit (argmap, val); + } + if (bitmap_empty_p (argmap)) + return; + + for (unsigned arg_idx =3D 0; arg_idx < gimple_call_num_args (call); arg_= idx++) + { + tree arg =3D gimple_call_arg (call, arg_idx); + tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); + state_t state =3D sm_ctxt->get_state (stmt, arg); + bool bit_set =3D bitmap_bit_p (argmap, arg_idx); + if (TREE_CODE (TREE_TYPE (arg)) !=3D INTEGER_TYPE) + continue; + if (bit_set) // Check if arg_idx is marked by any of the file descri= ptor + // attributes + { + + if (is_closed_fd_p (state)) + { + + sm_ctxt->warn (node, stmt, arg, + new fd_use_after_close (*this, diag_arg, + callee_fndecl, attr_na= me, + arg_idx)); + continue; + } + + if (!(is_valid_fd_p (state) || (state =3D=3D 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_WR= ITE, + 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_RE= AD, + 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 +{ + tree lhs =3D gimple_call_lhs (call); + if (lhs) + { + tree arg =3D gimple_call_arg (call, 1); + if (TREE_CODE (arg) =3D=3D INTEGER_CST) + { + int flag =3D TREE_INT_CST_LOW (arg); + enum access_mode mode =3D get_access_mode_from_flag (flag); + + switch (mode) + { + case READ_ONLY: + sm_ctxt->on_transition (node, stmt, lhs, m_start, + m_unchecked_read_only); + break; + case WRITE_ONLY: + sm_ctxt->on_transition (node, stmt, lhs, m_start, + m_unchecked_write_only); + break; + default: + sm_ctxt->on_transition (node, stmt, lhs, m_start, + m_unchecked_read_write); + } + } + } + else + { + sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE)= ); + } +} + +void +fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call) const +{ + tree arg =3D gimple_call_arg (call, 0); + state_t state =3D sm_ctxt->get_state (stmt, arg); + tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); + + sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_close= d); + sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed= ); + sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_close= d); + sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed); + + if (is_closed_fd_p (state)) + { + sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg= )); + sm_ctxt->set_next_state (stmt, arg, m_stop); + } +} +void +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, 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, 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_directions callee_fndecl_dir) const +{ + tree arg =3D gimple_call_arg (call, 0); + tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); + state_t state =3D sm_ctxt->get_state (stmt, arg); + + if (is_closed_fd_p (state)) + { + sm_ctxt->warn (node, stmt, arg, + new fd_use_after_close (*this, diag_arg, callee_fndecl= )); + } + + else + { + if (!(is_valid_fd_p (state) || (state =3D=3D m_stop))) + { + if (!is_constant_fd_p (state)) + sm_ctxt->warn ( + node, stmt, arg, + new fd_use_without_check (*this, diag_arg, callee_fndecl)); + } + switch (callee_fndecl_dir) + { + case DIRS_READ: + if (is_writeonly_fd_p (state)) + { + tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); + sm_ctxt->warn (node, stmt, arg, + new fd_access_mode_mismatch ( + *this, diag_arg, DIRS_WRITE, callee_fndecl= )); + } + + break; + case DIRS_WRITE: + + if (is_readonly_fd_p (state)) + { + tree diag_arg =3D sm_ctxt->get_diagnostic_tree (arg); + sm_ctxt->warn (node, stmt, arg, + new fd_access_mode_mismatch ( + *this, diag_arg, DIRS_READ, callee_fndecl)= ); + } + break; + default: + gcc_unreachable (); + } + } +} + +void +fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node= , + const gimple *stmt, const svalue *lhs, + enum tree_code op, const svalue *rhs) const +{ + if (tree cst =3D rhs->maybe_get_constant ()) + { + if (TREE_CODE (cst) =3D=3D INTEGER_CST) + { + int val =3D TREE_INT_CST_LOW (cst); + if (val =3D=3D -1) + { + if (op =3D=3D NE_EXPR) + make_valid_transitions_on_condition (sm_ctxt, node, stmt, l= hs); + + else if (op =3D=3D EQ_EXPR) + make_invalid_transitions_on_condition (sm_ctxt, node, stmt, + lhs); + } + } + } + + if (rhs->all_zeroes_p ()) + { + if (op =3D=3D GE_EXPR) + make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs); + else if (op =3D=3D LT_EXPR) + make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs); + } +} + +void +fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt= , + const supernode *nod= e, + const gimple *stmt, + const svalue *lhs) c= onst +{ + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, + m_valid_read_write); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, + m_valid_read_only); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, + m_valid_write_only); +} + +void +fd_state_machine::make_invalid_transitions_on_condition ( + sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const svalue *lhs) const +{ + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_inval= id); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invali= d); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_inval= id); +} + +bool +fd_state_machine::can_purge_p (state_t s) const +{ + if (is_unchecked_fd_p (s) || is_valid_fd_p (s)) + return false; + else + return true; +} + +pending_diagnostic * +fd_state_machine::on_leak (tree var) const +{ + return new fd_leak (*this, var); +} +} // namespace + +state_machine * +make_fd_state_machine (logger *logger) +{ + return new fd_state_machine (logger); +} +} // namespace ana + +#endif // ENABLE_ANALYZER -- 2.37.1