public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
From: Mir Immad <mirimnan017@gmail.com>
To: gcc@gcc.gnu.org, David Malcolm <dmalcolm@redhat.com>
Subject: Re: [PATCH] PR 106003
Date: Sat, 2 Jul 2022 19:35:00 +0530	[thread overview]
Message-ID: <CAE1-7oxowvkaC4ZBgobr185F7vAx2EUj=vs6u=1WA=DeLPdr9w@mail.gmail.com> (raw)
In-Reply-To: <CAE1-7oy1LqwhM-B7ohAkSe0AV3dNBr7AkVej4OaM04FmKe78zw@mail.gmail.com>

Hi everyone,

This is a patch for PR 106003
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106003; tested on x86_64.

On Sat, Jul 2, 2022 at 7:34 PM Mir Immad <mirimnan017@gmail.com> wrote:

> From 62b7b7736975172f03b30783436fbc9217324223 Mon Sep 17 00:00:00 2001
> From: mir <mirimmad17@gmail.com>
> Date: Sat, 2 Jul 2022 15:04:37 +0530
> Subject: [PATCH] analyzer: implement five new warnings for misuse of POSIX
>  file descriptor APIs [PR106003].
>
> This patch adds a new state machine to the analyzer for checking usage of
> POSIX file descriptor
> APIs with five new warnings.
>
> It adds:
> - check for FD leaks (CWE 775).
> - check for double "close" of a FD (CWE-1341).
> - check for read/write of a closed file descriptor.
> - check whether a file descriptor was used without being checked for
> validity.
> - check for read/write of a descriptor opened for just writing/reading.
>
> gcc/ChangeLog:
> PR analyzer/106003
> * Makefile.in (ANALYZER_OBJS): Add sm-fd.o.
> * doc/invoke.texi:  Add -Wanalyzer-fd-double-close, -Wanalyzer-fd-leak,
> -Wanalyzer-fd-access-mode-mismatch, -Wanalyzer-fd-use-without-check,
> -Wanalyzer-fd-use-after-close.
>
> gcc/analyzer/ChangeLog:
> PR analyzer/106003
> * analyzer.opt (Wanalyzer-fd-leak): New option.
> (Wanalyzer-fd-access-mode-mismatch): New option.
> (Wanalyzer-fd-use-without-check): New option.
> (Wanalyzer-fd-double-close): New option.
> (Wanalyzer-fd-use-after-close): New option.
> * sm.h (make_fd_state_machine): New decl.
> * sm.cc (make_checkers): Call make_fd_state_machine.
> * sm-fd.cc: New file.
>
> gcc/testsuite/ChangeLog:
> PR analyzer/106003
> * gcc.dg/analyzer/fd-1.c: New test.
> * gcc.dg/analyzer/fd-2.c: New test.
> * gcc.dg/analyzer/fd-3.c: New test.
> * gcc.dg/analyzer/fd-4.c: New test.
> ---
>  gcc/Makefile.in                      |   1 +
>  gcc/analyzer/analyzer.opt            |  20 +
>  gcc/analyzer/sm-fd.cc                | 847 +++++++++++++++++++++++++++
>  gcc/analyzer/sm.cc                   |   1 +
>  gcc/analyzer/sm.h                    |   1 +
>  gcc/doc/invoke.texi                  |  55 ++
>  gcc/testsuite/gcc.dg/analyzer/fd-1.c |  39 ++
>  gcc/testsuite/gcc.dg/analyzer/fd-2.c |  49 ++
>  gcc/testsuite/gcc.dg/analyzer/fd-3.c |  85 +++
>  gcc/testsuite/gcc.dg/analyzer/fd-4.c |  62 ++
>  10 files changed, 1160 insertions(+)
>  create mode 100644 gcc/analyzer/sm-fd.cc
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-3.c
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/fd-4.c
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index b6dcc45a58a..04631f737ea 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1269,6 +1269,7 @@ ANALYZER_OBJS = \
>   analyzer/region-model-reachability.o \
>   analyzer/sm.o \
>   analyzer/sm-file.o \
> + analyzer/sm-fd.o \
>   analyzer/sm-malloc.o \
>   analyzer/sm-pattern-test.o \
>   analyzer/sm-sensitive.o \
> diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
> index 23dfc797cea..479990b0e5e 100644
> --- a/gcc/analyzer/analyzer.opt
> +++ b/gcc/analyzer/analyzer.opt
> @@ -66,6 +66,26 @@ Wanalyzer-exposure-through-output-file
>  Common Var(warn_analyzer_exposure_through_output_file) Init(1) Warning
>  Warn about code paths in which sensitive data is written to a file.
>
> +Wanalyzer-fd-access-mode-mismatch
> +Common Var(warn_analyzer_fd_mode_mismatch) Init(1) Warning
> +Warn about code paths in which read on a write-only file descriptor is
> attempted, or vice versa.
> +
> +Wanalyzer-fd-double-close
> +Common Var(warn_analyzer_fd_double_close) Init(1) Warning
> +Warn about code paths in which a file descriptor can be closed more than
> once.
> +
> +Wanalyzer-fd-leak
> +Common Var(warn_analyzer_fd_leak) Init(1) Warning
> +Warn about code paths in which a file descriptor is not closed.
> +
> +Wanalyzer-fd-use-after-close
> +Common Var(warn_analyzer_fd_use_after_close) Init(1) Warning
> +Warn about code paths in which a read or write is performed on a closed
> file descriptor.
> +
> +Wanalyzer-fd-use-without-check
> +Common Var(warn_analyzer_fd_use_without_check) Init(1) Warning
> +Warn about code paths in which a file descriptor is used without being
> checked for validity.
> +
>  Wanalyzer-file-leak
>  Common Var(warn_analyzer_file_leak) Init(1) Warning
>  Warn about code paths in which a stdio FILE is not closed.
> diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
> new file mode 100644
> index 00000000000..4058ac53308
> --- /dev/null
> +++ b/gcc/analyzer/sm-fd.cc
> @@ -0,0 +1,847 @@
> +/* 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 "analyzer/call-string.h"
> +#include "analyzer/program-point.h"
> +#include "analyzer/store.h"
> +#include "analyzer/region-model.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
> +};
> +
> +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_direction 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;
> +};
> +
> +/* 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_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_diagnostic
> +{
> +public:
> +  fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
> +                           enum access_direction fd_dir,
> +                           const tree callee_fndecl)
> +      : fd_diagnostic (sm, arg), m_fd_dir (fd_dir),
> +        m_callee_fndecl (callee_fndecl)
> +
> +  {
> +  }
> +
> +  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
> +  {
> +    switch (m_fd_dir)
> +      {
> +      case DIR_READ:
> +        return warning_at (rich_loc, get_controlling_option (),
> +                           "%qE on %<read-only%> file descriptor %qE",
> +                           m_callee_fndecl, m_arg);
> +      case DIR_WRITE:
> +        return warning_at (rich_loc, get_controlling_option (),
> +                           "%qE on %<write-only%> file descriptor %qE",
> +                           m_callee_fndecl, m_arg);
> +      default:
> +        gcc_unreachable ();
> +      }
> +  }
> +
> +  bool
> +  subclass_equal_p (const pending_diagnostic &base_other) const override
> +  {
> +    const fd_access_mode_mismatch &sub_other
> +        = (const fd_access_mode_mismatch &)base_other;
> +    return (same_tree_p (m_arg, sub_other.m_arg)
> +            && m_callee_fndecl == sub_other.m_callee_fndecl
> +            && m_fd_dir == sub_other.m_fd_dir);
> +  }
> +
> +  label_text
> +  describe_final_event (const evdesc::final_event &ev) final override
> +  {
> +    switch (m_fd_dir)
> +      {
> +      case DIR_READ:
> +        return ev.formatted_print ("%qE on %<read-only%> file descriptor
> %qE",
> +                                   m_callee_fndecl, m_arg);
> +      case DIR_WRITE:
> +        return ev.formatted_print ("%qE on %<write-only%> file descriptor
> %qE",
> +                                   m_callee_fndecl, m_arg);
> +      default:
> +        gcc_unreachable ();
> +      }
> +  }
> +
> +private:
> +  enum access_direction m_fd_dir;
> +  const tree m_callee_fndecl;
> +};
> +
> +class double_close : public fd_diagnostic
> +{
> +public:
> +  double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic
> (sm, arg)
> +  {
> +  }
> +
> +  const char *
> +  get_kind () const final override
> +  {
> +    return "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_diagnostic
> +{
> +public:
> +  fd_use_after_close (const fd_state_machine &sm, tree arg,
> +                      const tree callee_fndecl)
> +      : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl)
> +  {
> +  }
> +
> +  const 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
> +  {
> +    return warning_at (rich_loc, get_controlling_option (),
> +                       "%qE on closed file descriptor %qE",
> m_callee_fndecl,
> +                       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)
> +      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
> +  {
> +    return ev.formatted_print ("%qE on closed file descriptor %qE here",
> +                               m_callee_fndecl, m_arg);
> +  }
> +
> +private:
> +  const tree m_callee_fndecl;
> +};
> +
> +class unchecked_use_of_fd : public fd_diagnostic
> +{
> +public:
> +  unchecked_use_of_fd (const fd_state_machine &sm, tree arg,
> +                       const tree callee_fndecl)
> +      : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl)
> +  {
> +  }
> +
> +  const char *
> +  get_kind () const final override
> +  {
> +    return "unchecked_use_of_fd";
> +  }
> +
> +  int
> +  get_controlling_option () const final override
> +  {
> +    return OPT_Wanalyzer_fd_use_without_check;
> +  }
> +
> +  bool
> +  emit (rich_location *rich_loc) final override
> +  {
> +    return warning_at (rich_loc, get_controlling_option (),
> +                       "%qE on possibly invalid file descriptor %qE",
> +                       m_callee_fndecl, m_arg);
> +  }
> +
> +  bool
> +  subclass_equal_p (const pending_diagnostic &base_other) const override
> +  {
> +    const unchecked_use_of_fd &sub_other
> +        = (const unchecked_use_of_fd &)base_other;
> +    return (same_tree_p (m_arg, sub_other.m_arg)
> +            && m_callee_fndecl == sub_other.m_callee_fndecl);
> +  }
> +
> +  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;
> +  const tree m_callee_fndecl;
> +};
> +
> +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_invalid (add_state ("fd-invalid")),
> +      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_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"
> +      }
> +
> +  return false;
> +}
> +
> +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 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, DIR_READ);
> +}
> +void
> +fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,
> +                            const gimple *stmt, const gcall *call,
> +                            const tree callee_fndecl) const
> +{
> +  check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIR_WRITE);
> +}
> +
> +void
> +fd_state_machine::check_for_open_fd (
> +    sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
> +    const gcall *call, const tree callee_fndecl,
> +    enum access_direction callee_fndecl_dir) const
> +{
> +  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 unchecked_use_of_fd (*this, diag_arg, callee_fndecl));
> +        }
> +      switch (callee_fndecl_dir)
> +        {
> +        case DIR_READ:
> +          if (is_writeonly_fd_p (state))
> +            {
> +              tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
> +              sm_ctxt->warn (node, stmt, arg,
> +                             new fd_access_mode_mismatch (
> +                                 *this, diag_arg, DIR_WRITE,
> callee_fndecl));
> +            }
> +
> +          break;
> +        case DIR_WRITE:
> +
> +          if (is_readonly_fd_p (state))
> +            {
> +              tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
> +              sm_ctxt->warn (node, stmt, arg,
> +                             new fd_access_mode_mismatch (
> +                                 *this, diag_arg, DIR_READ,
> callee_fndecl));
> +            }
> +          break;
> +        }
> +    }
> +}
> +
> +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
> diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc
> index 622cb0b7ab3..24c20b894cd 100644
> --- a/gcc/analyzer/sm.cc
> +++ b/gcc/analyzer/sm.cc
> @@ -167,6 +167,7 @@ make_checkers (auto_delete_vec <state_machine> &out,
> logger *logger)
>  {
>    out.safe_push (make_malloc_state_machine (logger));
>    out.safe_push (make_fileptr_state_machine (logger));
> +  out.safe_push (make_fd_state_machine (logger));
>    /* The "taint" checker must be explicitly enabled (as it currently
>       leads to state explosions that stop the other checkers working).  */
>    if (flag_analyzer_checker)
> diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h
> index 4cc54531c56..e80ef1fac37 100644
> --- a/gcc/analyzer/sm.h
> +++ b/gcc/analyzer/sm.h
> @@ -301,6 +301,7 @@ extern state_machine *make_sensitive_state_machine
> (logger *logger);
>  extern state_machine *make_signal_state_machine (logger *logger);
>  extern state_machine *make_pattern_test_state_machine (logger *logger);
>  extern state_machine *make_va_list_state_machine (logger *logger);
> +extern state_machine *make_fd_state_machine (logger *logger);
>
>  } // namespace ana
>
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 174bc09e5cf..b4a46dddfbb 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -9709,6 +9709,11 @@ Enabling this option effectively enables the
> following warnings:
>  -Wanalyzer-double-fclose @gol
>  -Wanalyzer-double-free @gol
>  -Wanalyzer-exposure-through-output-file @gol
> +-Wanalyzer-fd-access-mode-mismatch @gol
> +-Wanalyzer-fd-double-close @gol
> +-Wanalyzer-fd-leak @gol
> +-Wanalyzer-fd-use-after-close @gol
> +-Wanalyzer-fd-use-without-check @gol
>  -Wanalyzer-file-leak @gol
>  -Wanalyzer-free-of-non-heap @gol
>  -Wanalyzer-malloc-leak @gol
> @@ -9783,6 +9788,56 @@ This diagnostic warns for paths through the code in
> which a
>  security-sensitive value is written to an output file
>  (such as writing a password to a log file).
>
> +@item -Wno-analyzer-fd-access-mode-mismatch
> +@opindex Wanalyzer-fd-access-mode-mismatch
> +@opindex Wno-analyzer-fd-access-mode-mismatch
> +This warning requires @option{-fanalyzer}, which enables it; use
> +@option{-Wno-analyzer-fd-access-mode-mismatch}
> +to disable it.
> +
> +This diagnostic warns for paths through code in which a
> +@code{read} on a write-only file descriptor is attempted, or vice versa
> +
> +@item -Wno-analyzer-fd-double-close
> +@opindex Wanalyzer-fd-double-close
> +@opindex Wno-analyzer-fd-double-close
> +This warning requires @option{-fanalyzer}, which enables it; use
> +@option{-Wno-analyzer-fd-double-close}
> +to disable it.
> +
> +This diagnostic warns for paths through code in which a
> +file descriptor can be closed more than once.
> +
> +@item -Wno-analyzer-fd-leak
> +@opindex Wanalyzer-fd-leak
> +@opindex Wno-analyzer-fd-leak
> +This warning requires @option{-fanalyzer}, which enables it; use
> +@option{-Wno-analyzer-fd-leak}
> +to disable it.
> +
> +This diagnostic warns for paths through code in which an
> +open file descriptor is leaked.
> +
> +@item -Wno-analyzer-fd-use-after-close
> +@opindex Wanalyzer-fd-use-after-close
> +@opindex Wno-analyzer-fd-use-after-close
> +This warning requires @option{-fanalyzer}, which enables it; use
> +@option{-Wno-analyzer-fd-use-after-close}
> +to disable it.
> +
> +This diagnostic warns for paths through code in which a
> +read or write is called on a closed file descriptor.
> +
> +@item -Wno-analyzer-fd-use-without-check
> +@opindex Wanalyzer-fd-use-without-check
> +@opindex Wno-analyzer-fd-use-without-check
> +This warning requires @option{-fanalyzer}, which enables it; use
> +@option{-Wno-analyzer-fd-use-without-check}
> +to disable it.
> +
> +This diagnostic warns for paths through code in which a
> +file descriptor is used without being checked for validity.
> +
>  @item -Wno-analyzer-file-leak
>  @opindex Wanalyzer-file-leak
>  @opindex Wno-analyzer-file-leak
> diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-1.c
> b/gcc/testsuite/gcc.dg/analyzer/fd-1.c
> new file mode 100644
> index 00000000000..8a72e63833c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/analyzer/fd-1.c
> @@ -0,0 +1,39 @@
> +int open(const char *, int mode);
> +#define O_RDONLY 0
> +#define O_WRONLY 1
> +#define O_RDWR 2
> +
> +void
> +test_1 (const char *path)
> +{
> +  int fd = open (path, O_RDONLY); /* { dg-message "\\(1\\) opened here" }
> */
> +  return; /* { dg-warning "leak of file descriptor 'fd' \\\[CWE-775\\\]"
> "warning" } */
> + /* { dg-message "\\(2\\) 'fd' leaks here; was opened at \\(1\\)" "event"
> { target *-*-* } .-1 } */
> +}
> +
> +void
> +test_2 (const char *path)
> +{
> +  int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
> +  if (fd >= 0) /* { dg-message "\\(2\\) assuming 'fd' is a valid file
> descriptor" "event1" } */
> +  /* { dg-message "\\(3\\) following 'true' branch \\(when 'fd >=
> 0'\\)..." "event2" { target *-*-* } .-1 } */
> +  {
> +    return; /* { dg-warning "leak of file descriptor 'fd'
> \\\[CWE-775\\\]" "warning" } */
> +    /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 }
> */
> +    /* { dg-message "\\(5\\) 'fd' leaks here; was opened at \\(1\\)"
> "event2" { target *-*-* } .-2 } */
> +  }
> +}
> +
> +void
> +test_3 (const char *path)
> +{
> +  int fd = open (path, O_WRONLY); /* { dg-message "\\(1\\) opened here" }
> */
> +  return; /* { dg-warning "leak of file descriptor 'fd' \\\[CWE-775\\\]"
> "warning" } */
> +}
> +
> +void test_4 (const char *path)
> +{
> +  open(path, O_RDONLY); /* { dg-warning "leak of file descriptor
> \\\[CWE-775\\\]" } */
> +  /* { dg-message "\\(1\\) leaks here" "" { target *-*-* } .-1 } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-2.c
> b/gcc/testsuite/gcc.dg/analyzer/fd-2.c
> new file mode 100644
> index 00000000000..96ccf2f7ba8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/analyzer/fd-2.c
> @@ -0,0 +1,49 @@
> +int open(const char *, int mode);
> +void close(int fd);
> +#define O_RDONLY 0
> +#define O_WRONLY 1
> +#define O_RDWR 2
> +#define STDIN 0
> +
> +void
> +test_1 (const char *path)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    close (fd); /* { dg-message "\\(2\\) first 'close' here" "event1" } */
> +    close (fd); /* { dg-warning "double 'close' of file descriptor 'fd'
> \\\[CWE-1341\\\]" "warning" } */
> +    /* { dg-message "\\(3\\) second 'close' here; first 'close' was at
> \\(2\\)" "event2" { target *-*-* } .-1 } */
> +}
> +
> +void
> +test_2 (const char *path)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    if (fd < 0) /* { dg-message "\\(2\\) assuming 'fd' is a valid file
> descriptor \\(>= 0\\)" "event1" } */
> +    /* { dg-message "\\(3\\) following 'false' branch \\(when 'fd >=
> 0'\\)..." "event2" { target *-*-* } .-1 } */
> +        return;
> +    close (fd); /* { dg-message "\\(4\\) ...to here" "event1" } */
> +    /* { dg-message "\\(5\\) first 'close' here" "event2" { target *-*-*
> } .-1 } */
> +    close (fd); /* { dg-warning "double 'close' of file descriptor 'fd'
> \\\[CWE-1341\\\]" "warning" } */
> +    /* {dg-message "\\(6\\) second 'close' here; first was at \\(5\\)" ""
> { target *-*-* } .-1 } */
> +}
> +
> +void
> +test_3 ()
> +{
> +    /* FD 0 is stdin at the entry to "main" and thus read-only, but we
> have no
> +    guarantees here that it hasn't been closed and then reopened for
> +    writing, so we can't issue a warning */
> +
> +    int fd = STDIN;
> +    close(fd); /* { dg-message "\\(1\\) first 'close' here" } */
> +    close(fd); /* { dg-warning "double 'close' of file descriptor 'fd'
> \\\[CWE-1341\\\]" "warning" } */
> +     /* { dg-message "\\(2\\) second 'close' here; first 'close' was at
> \\(1\\)" "event2" { target *-*-* } .-1 } */
> +}
> +
> +void
> +test_4 ()
> +{
> +    int fd = -1;
> +    close(fd);
> +    close(fd);
> +}
> \ No newline at end of file
> diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-3.c
> b/gcc/testsuite/gcc.dg/analyzer/fd-3.c
> new file mode 100644
> index 00000000000..40fc8af27b5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/analyzer/fd-3.c
> @@ -0,0 +1,85 @@
> +int open(const char *, int mode);
> +void close(int fd);
> +int write (int fd, void *buf, int nbytes);
> +int read (int fd, void *buf, int nbytes);
> +int some_condition();
> +
> +#define O_RDONLY 0
> +#define O_WRONLY 1
> +#define O_RDWR 2
> +#define STDIN 0
> +#define O_NOATIME 262144
> +
> +void
> +test_1 (const char *path, void *buf)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    write (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid:
> unchecked value from \\(1\\)" } */
> +    /* { dg-warning "'write' on possibly invalid file descriptor 'fd'"
> "warning" { target *-*-* } .-1 } */
> +    close(fd);
> +}
> +
> +void
> +test_2 (const char *path, void *buf)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    read (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid:
> unchecked value from \\(1\\)" } */
> +    /* { dg-warning "'read' on possibly invalid file descriptor 'fd'"
> "warning" { target *-*-* } .-1 } */
> +    close (fd);
> +}
> +
> +void
> +test_3 (void *buf)
> +{
> +    int fd = -1;
> +    read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file
> descriptor 'fd'" } */
> +    /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* }
> .-1 } */
> +}
> +
> +void
> +test_4 (void *buf)
> +{
> +    int fd = STDIN;
> +    read (fd, buf, 1);
> +    close(fd);
> +}
> +
> +void
> +test_5 (char *path, void *buf)
> +{
> +    int flags = O_RDONLY;
> +    if (some_condition())
> +        flags |= O_NOATIME;
> +    int fd = open (path, flags);
> +    read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file
> descriptor 'fd'" } */
> +    /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* }
> .-1 } */
> +    close (fd);
> +}
> +
> +
> +void
> +test_6 (char *path, void *buf)
> +{
> +    int fd = open (path, O_RDONLY);
> +    if (fd != -1)
> +    {
> +        read (fd, buf, 1);
> +    }
> +    close (fd);
> +}
> +
> +
> +void
> +test_7 (char *path, void *buf)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    if (fd != -1) /* { dg-message "\\(2\\) assuming 'fd' is an invalid
> file descriptor \\(< 0\\)" } */
> +    {
> +        read (fd, buf, 1);
> +    } else
> +    {
> +        write (fd, buf, 1); /* { dg-warning "'write' on possibly invalid
> file descriptor 'fd'" } */
> +
> +    }
> +    close(fd);
> +}
> \ No newline at end of file
> diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-4.c
> b/gcc/testsuite/gcc.dg/analyzer/fd-4.c
> new file mode 100644
> index 00000000000..a973704f403
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/analyzer/fd-4.c
> @@ -0,0 +1,62 @@
> +int open(const char *, int mode);
> +void close(int fd);
> +int write (int fd, void *buf, int nbytes);
> +int read (int fd, void *buf, int nbytes);
> +
> +#define O_RDONLY 0
> +#define O_WRONLY 1
> +#define O_RDWR 2
> +
> +
> +void
> +test_1 (const char *path, void *buf)
> +{
> +    int fd = open (path, O_RDONLY); /* { dg-message "opened here as
> read-only" } */
> +    if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file
> descriptor \\(>= 0\\)" "event1" } */
> +    /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..."
> "event2" { target *-*-* } .-1 } */
> +    {
> +        write (fd, buf, 1); /* { dg-warning "'write' on 'read-only' file
> descriptor 'fd'" "warning" } */
> +        /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* }
> .-1 } */
> +        /* { dg-message "\\(5\\) 'write' on 'read-only' file descriptor
> 'fd'" "event2" { target *-*-* } .-2 } */
> +        close (fd);
> +    }
> +}
> +
> +void
> +test_2 (const char *path, void *buf)
> +{
> +    int fd = open (path, O_WRONLY); /* { dg-message "opened here as
> write-only" } */
> +    if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file
> descriptor \\(>= 0\\)" "event1" } */
> +    /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..."
> "event2" { target *-*-* } .-1 } */
> +    {
> +        read (fd, buf, 1); /* { dg-warning "'read' on 'write-only' file
> descriptor 'fd'" "warning" } */
> +        /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* }
> .-1 } */
> +        /* { dg-message "\\(5\\) 'read' on 'write-only' file descriptor
> 'fd'" "event2" { target *-*-* } .-2 } */
> +        close (fd);
> +    }
> +}
> +
> +
> +void
> +test_3 (const char *path, void *buf)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    if (fd >= 0)
> +    {
> +        close(fd); /* {dg-message "\\(2\\) closed here"} */
> +        read(fd, buf, 1); /* { dg-warning "'read' on closed file
> descriptor 'fd'" }  */
> +        /* {dg-message "\\(3\\) 'read' on closed file descriptor 'fd'
> here" "" {target *-*-*} .-1 } */
> +    }
> +}
> +
> +void
> +test_4 (const char *path, void *buf)
> +{
> +    int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" }
> */
> +    if (fd >= 0)
> +    {
> +        close(fd); /* {dg-message "\\(2\\) closed here"} */
> +        write(fd, buf, 1); /* { dg-warning "'write' on closed file
> descriptor 'fd'" }  */
> +        /* {dg-message "\\(3\\) 'write' on closed file descriptor 'fd'
> here" "" {target *-*-*} .-1 } */
> +    }
> +}
> --
> 2.25.1
>
>
>

  reply	other threads:[~2022-07-02 14:05 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-02 14:04 Mir Immad
2022-07-02 14:05 ` Mir Immad [this message]
2022-07-02 15:32 ` David Malcolm
2022-07-03  9:06   ` Mir Immad
2022-07-03 19:30     ` David Malcolm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAE1-7oxowvkaC4ZBgobr185F7vAx2EUj=vs6u=1WA=DeLPdr9w@mail.gmail.com' \
    --to=mirimnan017@gmail.com \
    --cc=dmalcolm@redhat.com \
    --cc=gcc@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).