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 <m...@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 <m...@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