public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] analyzer: fix coding style in sm-fd.cc
@ 2022-07-25  6:44 Martin Liška
  2022-07-25 15:54 ` David Malcolm
  2022-07-25 16:29 ` Immad Mir
  0 siblings, 2 replies; 3+ messages in thread
From: Martin Liška @ 2022-07-25  6:44 UTC (permalink / raw)
  To: gcc-patches; +Cc: mirimmad, David Malcolm

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?
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 <mir@sourceware.org>.
-
-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
-<http://www.gnu.org/licenses/>.  */
-
-#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 %<close%> 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<const gcall *> (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 <mir@sourceware.org>.
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#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 %<close%> 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<const gcall *> (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
-- 
2.37.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] analyzer: fix coding style in sm-fd.cc
  2022-07-25  6:44 [PATCH] analyzer: fix coding style in sm-fd.cc Martin Liška
@ 2022-07-25 15:54 ` David Malcolm
  2022-07-25 16:29 ` Immad Mir
  1 sibling, 0 replies; 3+ messages in thread
From: David Malcolm @ 2022-07-25 15:54 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: mirimmad

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 <mir@sourceware.org>.
> -
> -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
> -<http://www.gnu.org/licenses/>.  */
> -
> -#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 %<close%> 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<const gcall *> (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 <mir@sourceware.org>.
> +
> +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
> +<http://www.gnu.org/licenses/>.  */
> +
> +#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 %<close%> 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<const gcall *> (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



^ permalink raw reply	[flat|nested] 3+ messages in thread

* RE: [PATCH] analyzer: fix coding style in sm-fd.cc
  2022-07-25  6:44 [PATCH] analyzer: fix coding style in sm-fd.cc Martin Liška
  2022-07-25 15:54 ` David Malcolm
@ 2022-07-25 16:29 ` Immad Mir
  1 sibling, 0 replies; 3+ messages in thread
From: Immad Mir @ 2022-07-25 16:29 UTC (permalink / raw)
  To: Martin Liška, dmalcolm, gcc-patches

Thanks for this. I’ll keep this in mind for future patches.

Immad.


Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows 10

From: Martin Liška<mailto:mliska@suse.cz>
Sent: 25 July 2022 12:14 PM
To: gcc-patches@gcc.gnu.org<mailto:gcc-patches@gcc.gnu.org>
Cc: mirimmad@outlook.com<mailto:mirimmad@outlook.com>; David Malcolm<mailto:dmalcolm@redhat.com>
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=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?
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 <mir@sourceware.org>.
-
-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
-<http://www.gnu.org/licenses/>.  */
-
-#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 %<close%> 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<const gcall *> (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 <mir@sourceware.org>.
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#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 %<close%> 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<const gcall *> (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
--
2.37.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-07-25 16:29 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-25  6:44 [PATCH] analyzer: fix coding style in sm-fd.cc Martin Liška
2022-07-25 15:54 ` David Malcolm
2022-07-25 16:29 ` Immad Mir

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).