From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 59BBD3857809 for ; Mon, 25 Jul 2022 15:54:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 59BBD3857809 Received: from mail-qk1-f198.google.com (mail-qk1-f198.google.com [209.85.222.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-66-6fD8X1EwOF6GI1g8FTiCiw-1; Mon, 25 Jul 2022 11:54:25 -0400 X-MC-Unique: 6fD8X1EwOF6GI1g8FTiCiw-1 Received: by mail-qk1-f198.google.com with SMTP id bl27-20020a05620a1a9b00b0069994eeb30cso10056998qkb.11 for ; Mon, 25 Jul 2022 08:54:25 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:subject:from:to:cc:date:in-reply-to :references:user-agent:mime-version:content-transfer-encoding; bh=Kt9wHQtJgMVvPrrQiCCPaKY2L4xrB5n5qoNg+jW56bY=; b=ia/zY8AzaPnicV8CojP2iWDcJJfQSmAci5jLDWH5gPalVbGV3lANF81dZsEoQXtrB6 VsLQr3l3YH2k7xua8zg4s0iSmoM1zNj7ECNTH3DPMnLpuh9S2U2WOfsW4nY4dPCCjvgd e5iknsx9umuVyAwIwnqdsWiK8Uz3rUA+5EZGP7HO311nQX7uUXYBFQNVS8N8/JNEtGEu D9LY3YwqXvEVnLuEDdLX++1ndXzHxwAfxnl3708ocoRAwlUhg4hg27Bgto12JvaCYnNl UviQ0u+oatRGGB1W8WLv9i9kvza243CWbK+X5NHh9wXf7+7g3d0pzrna9jlrIPyRaRQt yk+A== X-Gm-Message-State: AJIora/b/PjQc9D9UA7bCp0uLmiKawrKb/voDTF3tMbcCNMc8uVUPMSd rc7OdnzN8nMXh3PAweSj/+MNP1vg59R5Xo1cRpJLQetbDnuAxS+r6EfcTr6YLUhn5038ef5h60k dl8ylCKHpxxDFw0uxVw== X-Received: by 2002:a05:622a:5d2:b0:31f:229d:441d with SMTP id d18-20020a05622a05d200b0031f229d441dmr10828270qtb.277.1658764464897; Mon, 25 Jul 2022 08:54:24 -0700 (PDT) X-Google-Smtp-Source: AGRyM1viCXj6McC3fDiHNtd/tffWrZOX7rwM8US9Y5qfkBs8g8SR5rJrFndhR7Nnsnca1NI0ftX8jA== X-Received: by 2002:a05:622a:5d2:b0:31f:229d:441d with SMTP id d18-20020a05622a05d200b0031f229d441dmr10828240qtb.277.1658764464410; Mon, 25 Jul 2022 08:54:24 -0700 (PDT) Received: from t14s.localdomain (c-73-69-212-193.hsd1.nh.comcast.net. [73.69.212.193]) by smtp.gmail.com with ESMTPSA id l5-20020a37f905000000b006b5e48997c2sm9232038qkj.32.2022.07.25.08.54.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 Jul 2022 08:54:23 -0700 (PDT) Message-ID: Subject: Re: [PATCH] analyzer: fix coding style in sm-fd.cc From: David Malcolm To: Martin =?UTF-8?Q?Li=C5=A1ka?= , gcc-patches@gcc.gnu.org Cc: mirimmad@outlook.com Date: Mon, 25 Jul 2022 11:54:22 -0400 In-Reply-To: References: User-Agent: Evolution 3.38.4 (3.38.4-1.fc33) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-10.7 required=5.0 tests=BAYES_00, BODY_8BITS, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, 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 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 15:54:34 -0000 On Mon, 2022-07-25 at 08:44 +0200, Martin Liška wrote: > 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=106003#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? Yes, thanks Dave > 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 = sval->maybe_get_constant ()) > -      { > -        if (TREE_CODE (cst) == INTEGER_CST) > -          { > -            int val = TREE_INT_CST_LOW (cst); > -            if (val >= 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 (>= 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 (>= > -    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 == m_sm.get_start_state () > -        && m_sm.is_unchecked_fd_p (change.m_new_state)) > -      { > -        if (change.m_new_state == m_sm.m_unchecked_read_write) > -          return change.formatted_print ("opened here as read- > write"); > - > -        if (change.m_new_state == m_sm.m_unchecked_read_only) > -          return change.formatted_print ("opened here as read- > only"); > - > -        if (change.m_new_state == m_sm.m_unchecked_write_only) > -          return change.formatted_print ("opened here as write- > only"); > -      } > - > -    if (change.m_new_state == 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 (>= 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 == 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 > descriptor"); > -      } > - > -    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_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) > -                    ? (strcmp (m_attr_name, sub_other.m_attr_name) > == 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 > Effective > -      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 > override > -  { > -    if (m_sm.is_unchecked_fd_p (change.m_new_state)) > -      { > -        m_open_event = 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 =  warning_at (rich_loc, get_controlling_option (), > -                           "%qE on read-only file descriptor %qE", > -                           m_callee_fndecl, m_arg); > -        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 (); > -      } > -      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 == m_sm.m_closed) > -      { > -        m_first_close_event = 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 = 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 == m_sm.m_closed) > -      { > -        m_first_close_event = 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_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; > -}; > - > -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 = 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 = 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 == m_unchecked_read_write > -       || s == m_unchecked_read_only > -       || s == m_unchecked_write_only); > -} > - > -bool > -fd_state_machine::is_valid_fd_p (state_t s) const > -{ > -  return (s == m_valid_read_write > -       || s == m_valid_read_only > -       || s == 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) == O_RDONLY) > -    { > -      return READ_ONLY; > -    } > -  else if ((flag & O_ACCMODE) == O_WRONLY) > -    { > -      return WRITE_ONLY; > -    } > -  return READ_WRITE; > -} > - > -bool > -fd_state_machine::is_readonly_fd_p (state_t state) const > -{ > -  return (state == m_unchecked_read_only || state == > m_valid_read_only); > -} > - > -bool > -fd_state_machine::is_writeonly_fd_p (state_t state) const > -{ > -  return (state == m_unchecked_write_only || state == > m_valid_write_only); > -} > - > -bool > -fd_state_machine::is_closed_fd_p (state_t state) const > -{ > -  return (state == m_closed); > -} > - > -bool > -fd_state_machine::is_constant_fd_p (state_t state) const > -{ > -  return (state == m_constant_fd); > -} > - > -bool > -fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode > *node, > -                           const gimple *stmt) const > -{ > -  if (const gcall *call = dyn_cast (stmt)) > -    if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) > -      { > -        if (is_named_call_p (callee_fndecl, "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 = 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 (call); > arg_idx++) > -    { > -      tree arg = gimple_call_arg (call, 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 > -{ > -  tree lhs = gimple_call_lhs (call); > -  if (lhs) > -    { > -      tree arg = gimple_call_arg (call, 1); > -      if (TREE_CODE (arg) == INTEGER_CST) > -        { > -          int flag = TREE_INT_CST_LOW (arg); > -          enum access_mode mode = 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 = gimple_call_arg (call, 0); > -  state_t state = sm_ctxt->get_state (stmt, arg); > -  tree diag_arg = 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_closed); > -  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_closed); > -  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 = gimple_call_arg (call, 0); > -  tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); > -  state_t state = 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 == 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 = 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 = 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 = rhs->maybe_get_constant ()) > -    { > -      if (TREE_CODE (cst) == INTEGER_CST) > -        { > -          int val = TREE_INT_CST_LOW (cst); > -          if (val == -1) > -            { > -              if (op == NE_EXPR) > -                make_valid_transitions_on_condition (sm_ctxt, node, > stmt, lhs); > - > -              else if (op == EQ_EXPR) > -                make_invalid_transitions_on_condition (sm_ctxt, > node, stmt, > -                                                       lhs); > -            } > -        } > -    } > - > -  if (rhs->all_zeroes_p ()) > -    { > -      if (op == GE_EXPR) > -        make_valid_transitions_on_condition (sm_ctxt, node, stmt, > lhs); > -      else if (op == 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 *node, > -                                                       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_invalid); > -  sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, > m_invalid); > -  sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, > m_invalid); > -} > - > -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 = sval->maybe_get_constant ()) > +      { > +       if (TREE_CODE (cst) == INTEGER_CST) > +         { > +           int val = TREE_INT_CST_LOW (cst); > +           if (val >= 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 (>= 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 (>= > +    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 == m_sm.get_start_state () > +       && m_sm.is_unchecked_fd_p (change.m_new_state)) > +      { > +       if (change.m_new_state == m_sm.m_unchecked_read_write) > +         return change.formatted_print ("opened here as read- > write"); > + > +       if (change.m_new_state == m_sm.m_unchecked_read_only) > +         return change.formatted_print ("opened here as read-only"); > + > +       if (change.m_new_state == m_sm.m_unchecked_write_only) > +         return change.formatted_print ("opened here as write- > only"); > +      } > + > +    if (change.m_new_state == 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 (>= 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 == 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 > descriptor"); > +      } > + > +    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_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) > +                   ? (strcmp (m_attr_name, sub_other.m_attr_name) == > 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 > Effective > +      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 > override > +  { > +    if (m_sm.is_unchecked_fd_p (change.m_new_state)) > +      { > +       m_open_event = 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 =  warning_at (rich_loc, get_controlling_option (), > +                          "%qE on read-only file descriptor %qE", > +                          m_callee_fndecl, m_arg); > +       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 (); > +      } > +      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 == m_sm.m_closed) > +      { > +       m_first_close_event = 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 = 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 == m_sm.m_closed) > +      { > +       m_first_close_event = 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_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; > +}; > + > +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 = 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 = 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 == m_unchecked_read_write > +       || s == m_unchecked_read_only > +       || s == m_unchecked_write_only); > +} > + > +bool > +fd_state_machine::is_valid_fd_p (state_t s) const > +{ > +  return (s == m_valid_read_write > +       || s == m_valid_read_only > +       || s == 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) == O_RDONLY) > +    { > +      return READ_ONLY; > +    } > +  else if ((flag & O_ACCMODE) == O_WRONLY) > +    { > +      return WRITE_ONLY; > +    } > +  return READ_WRITE; > +} > + > +bool > +fd_state_machine::is_readonly_fd_p (state_t state) const > +{ > +  return (state == m_unchecked_read_only || state == > m_valid_read_only); > +} > + > +bool > +fd_state_machine::is_writeonly_fd_p (state_t state) const > +{ > +  return (state == m_unchecked_write_only || state == > m_valid_write_only); > +} > + > +bool > +fd_state_machine::is_closed_fd_p (state_t state) const > +{ > +  return (state == m_closed); > +} > + > +bool > +fd_state_machine::is_constant_fd_p (state_t state) const > +{ > +  return (state == m_constant_fd); > +} > + > +bool > +fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode > *node, > +                          const gimple *stmt) const > +{ > +  if (const gcall *call = dyn_cast (stmt)) > +    if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) > +      { > +       if (is_named_call_p (callee_fndecl, "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 = 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 (call); > arg_idx++) > +    { > +      tree arg = gimple_call_arg (call, 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 > +{ > +  tree lhs = gimple_call_lhs (call); > +  if (lhs) > +    { > +      tree arg = gimple_call_arg (call, 1); > +      if (TREE_CODE (arg) == INTEGER_CST) > +       { > +         int flag = TREE_INT_CST_LOW (arg); > +         enum access_mode mode = 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 = gimple_call_arg (call, 0); > +  state_t state = sm_ctxt->get_state (stmt, arg); > +  tree diag_arg = 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_closed); > +  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_closed); > +  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 = gimple_call_arg (call, 0); > +  tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); > +  state_t state = 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 == 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 = 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 = 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 = rhs->maybe_get_constant ()) > +    { > +      if (TREE_CODE (cst) == INTEGER_CST) > +       { > +         int val = TREE_INT_CST_LOW (cst); > +         if (val == -1) > +           { > +             if (op == NE_EXPR) > +               make_valid_transitions_on_condition (sm_ctxt, node, > stmt, lhs); > + > +             else if (op == EQ_EXPR) > +               make_invalid_transitions_on_condition (sm_ctxt, node, > stmt, > +                                                      lhs); > +           } > +       } > +    } > + > +  if (rhs->all_zeroes_p ()) > +    { > +      if (op == GE_EXPR) > +       make_valid_transitions_on_condition (sm_ctxt, node, stmt, > lhs); > +      else if (op == 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 *node, > +                                                      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_invalid); > +  sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, > m_invalid); > +  sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, > m_invalid); > +} > + > +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