LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-signal.cc (source / functions) Hit Total Coverage
Test: gcc.info Lines: 94 100 94.0 %
Date: 2020-03-28 11:57:23 Functions: 15 18 83.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /* An experimental state machine, for tracking bad calls from within
       2                 :            :    signal handlers.
       3                 :            : 
       4                 :            :    Copyright (C) 2019-2020 Free Software Foundation, Inc.
       5                 :            :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       6                 :            : 
       7                 :            : This file is part of GCC.
       8                 :            : 
       9                 :            : GCC is free software; you can redistribute it and/or modify it
      10                 :            : under the terms of the GNU General Public License as published by
      11                 :            : the Free Software Foundation; either version 3, or (at your option)
      12                 :            : any later version.
      13                 :            : 
      14                 :            : GCC is distributed in the hope that it will be useful, but
      15                 :            : WITHOUT ANY WARRANTY; without even the implied warranty of
      16                 :            : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17                 :            : General Public License for more details.
      18                 :            : 
      19                 :            : You should have received a copy of the GNU General Public License
      20                 :            : along with GCC; see the file COPYING3.  If not see
      21                 :            : <http://www.gnu.org/licenses/>.  */
      22                 :            : 
      23                 :            : #include "config.h"
      24                 :            : #include "system.h"
      25                 :            : #include "coretypes.h"
      26                 :            : #include "tree.h"
      27                 :            : #include "function.h"
      28                 :            : #include "basic-block.h"
      29                 :            : #include "gimple.h"
      30                 :            : #include "options.h"
      31                 :            : #include "bitmap.h"
      32                 :            : #include "diagnostic-path.h"
      33                 :            : #include "diagnostic-metadata.h"
      34                 :            : #include "function.h"
      35                 :            : #include "analyzer/analyzer.h"
      36                 :            : #include "diagnostic-event-id.h"
      37                 :            : #include "analyzer/analyzer-logging.h"
      38                 :            : #include "analyzer/sm.h"
      39                 :            : #include "analyzer/pending-diagnostic.h"
      40                 :            : #include "sbitmap.h"
      41                 :            : #include "tristate.h"
      42                 :            : #include "ordered-hash-map.h"
      43                 :            : #include "selftest.h"
      44                 :            : #include "analyzer/region-model.h"
      45                 :            : #include "analyzer/program-state.h"
      46                 :            : #include "analyzer/checker-path.h"
      47                 :            : #include "digraph.h"
      48                 :            : #include "cfg.h"
      49                 :            : #include "gimple-iterator.h"
      50                 :            : #include "cgraph.h"
      51                 :            : #include "analyzer/supergraph.h"
      52                 :            : #include "analyzer/call-string.h"
      53                 :            : #include "analyzer/program-point.h"
      54                 :            : #include "alloc-pool.h"
      55                 :            : #include "fibonacci_heap.h"
      56                 :            : #include "analyzer/diagnostic-manager.h"
      57                 :            : #include "shortest-paths.h"
      58                 :            : #include "analyzer/exploded-graph.h"
      59                 :            : #include "analyzer/function-set.h"
      60                 :            : #include "analyzer/analyzer-selftests.h"
      61                 :            : 
      62                 :            : #if ENABLE_ANALYZER
      63                 :            : 
      64                 :            : namespace ana {
      65                 :            : 
      66                 :            : namespace {
      67                 :            : 
      68                 :            : /* An experimental state machine, for tracking calls to async-signal-unsafe
      69                 :            :    functions from within signal handlers.  */
      70                 :            : 
      71                 :            : class signal_state_machine : public state_machine
      72                 :            : {
      73                 :            : public:
      74                 :            :   signal_state_machine (logger *logger);
      75                 :            : 
      76                 :       1920 :   bool inherited_state_p () const FINAL OVERRIDE { return false; }
      77                 :            : 
      78                 :            :   bool on_stmt (sm_context *sm_ctxt,
      79                 :            :                 const supernode *node,
      80                 :            :                 const gimple *stmt) const FINAL OVERRIDE;
      81                 :            : 
      82                 :            :   void on_condition (sm_context *sm_ctxt,
      83                 :            :                      const supernode *node,
      84                 :            :                      const gimple *stmt,
      85                 :            :                      tree lhs,
      86                 :            :                      enum tree_code op,
      87                 :            :                      tree rhs) const FINAL OVERRIDE;
      88                 :            : 
      89                 :            :   bool can_purge_p (state_t s) const FINAL OVERRIDE;
      90                 :            : 
      91                 :            :   /* These states are "global", rather than per-expression.  */
      92                 :            : 
      93                 :            :   /* Start state.  */
      94                 :            :   state_t m_start;
      95                 :            : 
      96                 :            :   /* State for when we're in a signal handler.  */
      97                 :            :   state_t m_in_signal_handler;
      98                 :            : 
      99                 :            :   /* Stop state.  */
     100                 :            :   state_t m_stop;
     101                 :            : };
     102                 :            : 
     103                 :            : /* Concrete subclass for describing call to an async-signal-unsafe function
     104                 :            :    from a signal handler.  */
     105                 :            : 
     106                 :            : class signal_unsafe_call
     107                 :            :   : public pending_diagnostic_subclass<signal_unsafe_call>
     108                 :            : {
     109                 :            : public:
     110                 :          8 :   signal_unsafe_call (const signal_state_machine &sm, const gcall *unsafe_call,
     111                 :            :                       tree unsafe_fndecl)
     112                 :          8 :   : m_sm (sm), m_unsafe_call (unsafe_call), m_unsafe_fndecl (unsafe_fndecl)
     113                 :            :   {
     114                 :          8 :     gcc_assert (m_unsafe_fndecl);
     115                 :            :   }
     116                 :            : 
     117                 :         24 :   const char *get_kind () const FINAL OVERRIDE { return "signal_unsafe_call"; }
     118                 :            : 
     119                 :          8 :   bool operator== (const signal_unsafe_call &other) const
     120                 :            :   {
     121                 :          8 :     return m_unsafe_call == other.m_unsafe_call;
     122                 :            :   }
     123                 :            : 
     124                 :          8 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     125                 :            :   {
     126                 :          8 :     diagnostic_metadata m;
     127                 :            :     /* CWE-479: Signal Handler Use of a Non-reentrant Function.  */
     128                 :          8 :     m.add_cwe (479);
     129                 :          8 :     return warning_meta (rich_loc, m,
     130                 :            :                          OPT_Wanalyzer_unsafe_call_within_signal_handler,
     131                 :            :                          "call to %qD from within signal handler",
     132                 :          8 :                          m_unsafe_fndecl);
     133                 :            :   }
     134                 :            : 
     135                 :         16 :   label_text describe_state_change (const evdesc::state_change &change)
     136                 :            :     FINAL OVERRIDE
     137                 :            :   {
     138                 :         16 :     if (change.is_global_p ()
     139                 :         16 :         && change.m_new_state == m_sm.m_in_signal_handler)
     140                 :            :       {
     141                 :         16 :         function *handler
     142                 :         16 :           = change.m_event.m_dst_state.m_region_model->get_current_function ();
     143                 :         16 :         return change.formatted_print ("registering %qD as signal handler",
     144                 :         16 :                                        handler->decl);
     145                 :            :       }
     146                 :          0 :     return label_text ();
     147                 :            :   }
     148                 :            : 
     149                 :         16 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     150                 :            :   {
     151                 :         16 :     return ev.formatted_print ("call to %qD from within signal handler",
     152                 :         16 :                                m_unsafe_fndecl);
     153                 :            :   }
     154                 :            : 
     155                 :            : private:
     156                 :            :   const signal_state_machine &m_sm;
     157                 :            :   const gcall *m_unsafe_call;
     158                 :            :   tree m_unsafe_fndecl;
     159                 :            : };
     160                 :            : 
     161                 :            : /* signal_state_machine's ctor.  */
     162                 :            : 
     163                 :        379 : signal_state_machine::signal_state_machine (logger *logger)
     164                 :        379 : : state_machine ("signal", logger)
     165                 :            : {
     166                 :        379 :   m_start = add_state ("start");
     167                 :        379 :   m_in_signal_handler = add_state ("in_signal_handler");
     168                 :        379 :   m_stop = add_state ("stop");
     169                 :        379 : }
     170                 :            : 
     171                 :            : /* Update MODEL for edges that simulate HANDLER_FUN being called as
     172                 :            :    an signal-handler in response to a signal.  */
     173                 :            : 
     174                 :            : static void
     175                 :         15 : update_model_for_signal_handler (region_model *model,
     176                 :            :                                  function *handler_fun)
     177                 :            : {
     178                 :            :   /* Purge all state within MODEL.  */
     179                 :         15 :   *model = region_model ();
     180                 :         15 :   model->push_frame (handler_fun, NULL, NULL);
     181                 :         15 : }
     182                 :            : 
     183                 :            : /* Custom exploded_edge info: entry into a signal-handler.  */
     184                 :            : 
     185                 :          7 : class signal_delivery_edge_info_t : public exploded_edge::custom_info_t
     186                 :            : {
     187                 :            : public:
     188                 :          0 :   void print (pretty_printer *pp) FINAL OVERRIDE
     189                 :            :   {
     190                 :          0 :     pp_string (pp, "signal delivered");
     191                 :          0 :   }
     192                 :            : 
     193                 :          8 :   void update_model (region_model *model,
     194                 :            :                      const exploded_edge &eedge) FINAL OVERRIDE
     195                 :            :   {
     196                 :         16 :     update_model_for_signal_handler (model, eedge.m_dest->get_function ());
     197                 :          8 :   }
     198                 :            : 
     199                 :          8 :   void add_events_to_path (checker_path *emission_path,
     200                 :            :                            const exploded_edge &eedge ATTRIBUTE_UNUSED)
     201                 :            :     FINAL OVERRIDE
     202                 :            :   {
     203                 :          8 :     emission_path->add_event
     204                 :          8 :       (new custom_event (UNKNOWN_LOCATION, NULL_TREE, 0,
     205                 :            :                          "later on,"
     206                 :          8 :                          " when the signal is delivered to the process"));
     207                 :          8 :   }
     208                 :            : };
     209                 :            : 
     210                 :            : /* Concrete subclass of custom_transition for modeling registration of a
     211                 :            :    signal handler and the signal handler later being called.  */
     212                 :            : 
     213                 :          7 : class register_signal_handler : public custom_transition
     214                 :            : {
     215                 :            : public:
     216                 :          7 :   register_signal_handler (const signal_state_machine &sm,
     217                 :            :                            tree fndecl)
     218                 :          7 :   : m_sm (sm), m_fndecl (fndecl) {}
     219                 :            : 
     220                 :            :   /* Model a signal-handler FNDECL being called at some later point
     221                 :            :      by injecting an edge to a new function-entry node with an empty
     222                 :            :      callstring, setting the 'in-signal-handler' global state
     223                 :            :      on the node.  */
     224                 :          7 :   void impl_transition (exploded_graph *eg,
     225                 :            :                         exploded_node *src_enode,
     226                 :            :                         int sm_idx) FINAL OVERRIDE
     227                 :            :   {
     228                 :          7 :     function *handler_fun = DECL_STRUCT_FUNCTION (m_fndecl);
     229                 :          7 :     if (!handler_fun)
     230                 :          0 :       return;
     231                 :          7 :     program_point entering_handler
     232                 :            :       = program_point::from_function_entry (eg->get_supergraph (),
     233                 :         14 :                                             handler_fun);
     234                 :            : 
     235                 :         14 :     program_state state_entering_handler (eg->get_ext_state ());
     236                 :          7 :     update_model_for_signal_handler (state_entering_handler.m_region_model,
     237                 :            :                                      handler_fun);
     238                 :          7 :     state_entering_handler.m_checker_states[sm_idx]->set_global_state
     239                 :          7 :       (m_sm.m_in_signal_handler);
     240                 :            : 
     241                 :          7 :     exploded_node *dst_enode = eg->get_or_create_node (entering_handler,
     242                 :            :                                                        state_entering_handler,
     243                 :            :                                                        NULL);
     244                 :          7 :     if (dst_enode)
     245                 :          7 :       eg->add_edge (src_enode, dst_enode, NULL, state_change (),
     246                 :          7 :                     new signal_delivery_edge_info_t ());
     247                 :            :   }
     248                 :            : 
     249                 :            :   const signal_state_machine &m_sm;
     250                 :            :   tree m_fndecl;
     251                 :            : };
     252                 :            : 
     253                 :            : /* Get a set of functions that are known to be unsafe to call from an
     254                 :            :    async signal handler.  */
     255                 :            : 
     256                 :            : static function_set
     257                 :         16 : get_async_signal_unsafe_fns ()
     258                 :            : {
     259                 :            :   // TODO: populate this list more fully
     260                 :         16 :   static const char * const async_signal_unsafe_fns[] = {
     261                 :            :     /* This array must be kept sorted.  */
     262                 :            :     "fprintf",
     263                 :            :     "free",
     264                 :            :     "malloc",
     265                 :            :     "printf",
     266                 :            :     "snprintf",
     267                 :            :     "sprintf",
     268                 :            :     "vfprintf",
     269                 :            :     "vprintf",
     270                 :            :     "vsnprintf",
     271                 :            :     "vsprintf"
     272                 :            :   };
     273                 :         16 :   const size_t count
     274                 :            :     = sizeof(async_signal_unsafe_fns) / sizeof (async_signal_unsafe_fns[0]);
     275                 :         16 :   function_set fs (async_signal_unsafe_fns, count);
     276                 :         16 :   return fs;
     277                 :            : };
     278                 :            : 
     279                 :            : /* Return true if FNDECL is known to be unsafe to call from a signal
     280                 :            :    handler.  */
     281                 :            : 
     282                 :            : static bool
     283                 :         14 : signal_unsafe_p (tree fndecl)
     284                 :            : {
     285                 :          0 :   function_set fs = get_async_signal_unsafe_fns ();
     286                 :         14 :   return fs.contains_decl_p (fndecl);
     287                 :            : }
     288                 :            : 
     289                 :            : /* Implementation of state_machine::on_stmt vfunc for signal_state_machine.  */
     290                 :            : 
     291                 :            : bool
     292                 :      15270 : signal_state_machine::on_stmt (sm_context *sm_ctxt,
     293                 :            :                                const supernode *node,
     294                 :            :                                const gimple *stmt) const
     295                 :            : {
     296                 :      15270 :   const state_t global_state = sm_ctxt->get_global_state ();
     297                 :      15270 :   if (global_state == m_start)
     298                 :            :     {
     299                 :      15236 :       if (const gcall *call = dyn_cast <const gcall *> (stmt))
     300                 :       3632 :         if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
     301                 :       3606 :           if (is_named_call_p (callee_fndecl, "signal", call, 2))
     302                 :            :             {
     303                 :          7 :               tree handler = gimple_call_arg (call, 1);
     304                 :          7 :               if (TREE_CODE (handler) == ADDR_EXPR
     305                 :          7 :                   && TREE_CODE (TREE_OPERAND (handler, 0)) == FUNCTION_DECL)
     306                 :            :                 {
     307                 :          7 :                   tree fndecl = TREE_OPERAND (handler, 0);
     308                 :          7 :                   register_signal_handler rsh (*this, fndecl);
     309                 :          7 :                   sm_ctxt->on_custom_transition (&rsh);
     310                 :            :                 }
     311                 :            :             }
     312                 :            :     }
     313                 :         34 :   else if (global_state == m_in_signal_handler)
     314                 :            :     {
     315                 :         34 :       if (const gcall *call = dyn_cast <const gcall *> (stmt))
     316                 :         14 :         if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
     317                 :         14 :           if (signal_unsafe_p (callee_fndecl))
     318                 :          8 :             sm_ctxt->warn_for_state (node, stmt, NULL_TREE, m_in_signal_handler,
     319                 :            :                                      new signal_unsafe_call (*this, call,
     320                 :          8 :                                                              callee_fndecl));
     321                 :            :     }
     322                 :            : 
     323                 :      15270 :   return false;
     324                 :            : }
     325                 :            : 
     326                 :            : /* Implementation of state_machine::on_condition vfunc for
     327                 :            :    signal_state_machine.  */
     328                 :            : 
     329                 :            : void
     330                 :       5149 : signal_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
     331                 :            :                                     const supernode *node ATTRIBUTE_UNUSED,
     332                 :            :                                     const gimple *stmt ATTRIBUTE_UNUSED,
     333                 :            :                                     tree lhs ATTRIBUTE_UNUSED,
     334                 :            :                                     enum tree_code op ATTRIBUTE_UNUSED,
     335                 :            :                                     tree rhs ATTRIBUTE_UNUSED) const
     336                 :            : {
     337                 :            :   // Empty
     338                 :       5149 : }
     339                 :            : 
     340                 :            : bool
     341                 :      18125 : signal_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
     342                 :            : {
     343                 :      18125 :   return true;
     344                 :            : }
     345                 :            : 
     346                 :            : } // anonymous namespace
     347                 :            : 
     348                 :            : /* Internal interface to this file. */
     349                 :            : 
     350                 :            : state_machine *
     351                 :        379 : make_signal_state_machine (logger *logger)
     352                 :            : {
     353                 :        379 :   return new signal_state_machine (logger);
     354                 :            : }
     355                 :            : 
     356                 :            : #if CHECKING_P
     357                 :            : 
     358                 :            : namespace selftest {
     359                 :            : 
     360                 :            : /* Run all of the selftests within this file.  */
     361                 :            : 
     362                 :            : void
     363                 :          2 : analyzer_sm_signal_cc_tests ()
     364                 :            : {
     365                 :          2 :   function_set fs = get_async_signal_unsafe_fns ();
     366                 :          2 :   fs.assert_sorted ();
     367                 :          2 :   fs.assert_sane ();
     368                 :          2 : }
     369                 :            : 
     370                 :            : } // namespace selftest
     371                 :            : 
     372                 :            : #endif /* CHECKING_P */
     373                 :            : 
     374                 :            : } // namespace ana
     375                 :            : 
     376                 :            : #endif /* #if ENABLE_ANALYZER */

Generated by: LCOV version 1.0

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto --enable-host-shared. GCC test suite is run with the built compiler.