diff options
author | David Malcolm <dmalcolm@redhat.com> | 2019-12-19 16:10:41 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2020-01-14 21:10:45 -0500 |
commit | 24918a9b20c771984f41f90c3352c3ebee8b5e6f (patch) | |
tree | c43e7b6c06c3183bee103da0ff484e6846c7454c /gcc/analyzer/sm-file.cc | |
parent | d666ae9862a06cab12108607c516a8475b4fb766 (diff) | |
download | gcc-devel/analyzer.tar.gz |
analyzer: add -Wanalyzer-use-of-closed-filedevel/analyzer
gcc/analyzer/ChangeLog:
* analyzer.opt (Wanalyzer-use-of-closed-file): New option.
* sm-file.cc (class use_of_closed_file): New file_diagnostic subclass.
(find_file_param): New function.
(fileptr_state_machine::on_stmt): Complain about operations on
closed files.
gcc/ChangeLog:
* doc/invoke.texi (-Wanalyzer-use-of-closed-file): Document new
option.
gcc/testsuite/ChangeLog:
* gcc.dg/analyzer/file-1.c (test_5): New test.
Diffstat (limited to 'gcc/analyzer/sm-file.cc')
-rw-r--r-- | gcc/analyzer/sm-file.cc | 92 |
1 files changed, 91 insertions, 1 deletions
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index f731981b0f5..e7fb0f00145 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -161,6 +161,46 @@ private: diagnostic_event_id_t m_first_fclose_event; }; +class use_of_closed_file : public file_diagnostic +{ +public: + use_of_closed_file (const fileptr_state_machine &sm, tree arg) + : file_diagnostic (sm, arg) + {} + + const char *get_kind () const FINAL OVERRIDE { return "use_of_closed_file"; } + + bool emit (rich_location *rich_loc) FINAL OVERRIDE + { + // FIXME: CWE? + return warning_at (rich_loc, OPT_Wanalyzer_use_of_closed_file, + "use of closed FILE %qE", + m_arg); + } + + label_text describe_state_change (const evdesc::state_change &change) + OVERRIDE + { + if (change.m_new_state == m_sm.m_closed) + { + m_fclose_event = change.m_event_id; + return change.formatted_print ("file closed here"); + } + return file_diagnostic::describe_state_change (change); + } + + label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE + { + if (m_fclose_event.known_p ()) + return ev.formatted_print ("use of closed FILE %qE; closed at %@", + ev.m_expr, &m_fclose_event); + return ev.formatted_print ("use of closed FILE %qE", ev.m_expr); + } + +private: + diagnostic_event_id_t m_fclose_event; +}; + class file_leak : public file_diagnostic { public: @@ -294,6 +334,41 @@ is_file_using_fn_p (tree fndecl) return fs.contains_decl_p (fndecl); } +/* If FNDECL takes a FILE * param, write the 0-based index of the first + such param to *OUT_IDX and return true. */ + +static bool +find_file_param (tree fndecl, int *out_idx) +{ + int idx = 0; + for (tree iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + iter_param_types; + iter_param_types = TREE_CHAIN (iter_param_types), idx++) + { + tree param_type = TREE_VALUE (iter_param_types); + + /* Looks like we can't rely on fileptr_type_node being set up; + instead, look for a ptr to record called "FILE". */ + + if (!POINTER_TYPE_P (param_type)) + continue; + tree type = TREE_TYPE (param_type); + if (TREE_CODE (type) == RECORD_TYPE + && TYPE_NAME (type) + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) + { + tree tdecl = TYPE_NAME (type); + if (DECL_NAME (tdecl) + && strcmp (IDENTIFIER_POINTER (DECL_NAME (tdecl)), "FILE") == 0) + { + *out_idx = idx; + return true; + } + } + } + return false; +} + /* Implementation of state_machine::on_stmt vfunc for fileptr_state_machine. */ bool @@ -340,12 +415,27 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt, if (is_file_using_fn_p (callee_fndecl)) { - // TODO: operations on unchecked file + int param_idx; + if (find_file_param (callee_fndecl, ¶m_idx)) + { + /* Look up FILE * param. */ + tree arg = gimple_call_arg (call, param_idx); + arg = sm_ctxt->get_readable_tree (arg); + + // TODO: operations on unchecked file + + /* Complain about operations on closed files. */ + sm_ctxt->warn_for_state (node, stmt, arg, m_closed, + new use_of_closed_file (*this, arg)); + sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop); + } return true; } // etc } + // TODO: fcloseall() (a GNU extension) + return false; } |