LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-sensitive.cc (source / functions) Hit Total Coverage
Test: gcc.info Lines: 61 66 92.4 %
Date: 2020-03-28 11:57:23 Functions: 13 13 100.0 %
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 exposure of sensitive
       2                 :            :    data (e.g. through logging).
       3                 :            :    Copyright (C) 2019-2020 Free Software Foundation, Inc.
       4                 :            :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       5                 :            : 
       6                 :            : This file is part of GCC.
       7                 :            : 
       8                 :            : GCC is free software; you can redistribute it and/or modify it
       9                 :            : under the terms of the GNU General Public License as published by
      10                 :            : the Free Software Foundation; either version 3, or (at your option)
      11                 :            : any later version.
      12                 :            : 
      13                 :            : GCC is distributed in the hope that it will be useful, but
      14                 :            : WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :            : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16                 :            : General Public License for more details.
      17                 :            : 
      18                 :            : You should have received a copy of the GNU General Public License
      19                 :            : along with GCC; see the file COPYING3.  If not see
      20                 :            : <http://www.gnu.org/licenses/>.  */
      21                 :            : 
      22                 :            : #include "config.h"
      23                 :            : #include "system.h"
      24                 :            : #include "coretypes.h"
      25                 :            : #include "tree.h"
      26                 :            : #include "function.h"
      27                 :            : #include "function.h"
      28                 :            : #include "basic-block.h"
      29                 :            : #include "gimple.h"
      30                 :            : #include "options.h"
      31                 :            : #include "diagnostic-path.h"
      32                 :            : #include "diagnostic-metadata.h"
      33                 :            : #include "function.h"
      34                 :            : #include "analyzer/analyzer.h"
      35                 :            : #include "diagnostic-event-id.h"
      36                 :            : #include "analyzer/analyzer-logging.h"
      37                 :            : #include "analyzer/sm.h"
      38                 :            : #include "analyzer/pending-diagnostic.h"
      39                 :            : 
      40                 :            : #if ENABLE_ANALYZER
      41                 :            : 
      42                 :            : namespace ana {
      43                 :            : 
      44                 :            : namespace {
      45                 :            : 
      46                 :            : /* An experimental state machine, for tracking exposure of sensitive
      47                 :            :    data (e.g. through logging).  */
      48                 :            : 
      49                 :            : class sensitive_state_machine : public state_machine
      50                 :            : {
      51                 :            : public:
      52                 :            :   sensitive_state_machine (logger *logger);
      53                 :            : 
      54                 :       1920 :   bool inherited_state_p () const FINAL OVERRIDE { return true; }
      55                 :            : 
      56                 :            :   bool on_stmt (sm_context *sm_ctxt,
      57                 :            :                 const supernode *node,
      58                 :            :                 const gimple *stmt) const FINAL OVERRIDE;
      59                 :            : 
      60                 :            :   void on_condition (sm_context *sm_ctxt,
      61                 :            :                      const supernode *node,
      62                 :            :                      const gimple *stmt,
      63                 :            :                      tree lhs,
      64                 :            :                      enum tree_code op,
      65                 :            :                      tree rhs) const FINAL OVERRIDE;
      66                 :            : 
      67                 :            :   bool can_purge_p (state_t s) const FINAL OVERRIDE;
      68                 :            : 
      69                 :            :   /* Start state.  */
      70                 :            :   state_t m_start;
      71                 :            : 
      72                 :            :   /* State for "sensitive" data, such as a password.  */
      73                 :            :   state_t m_sensitive;
      74                 :            : 
      75                 :            :   /* Stop state, for a value we don't want to track any more.  */
      76                 :            :   state_t m_stop;
      77                 :            : 
      78                 :            : private:
      79                 :            :   void warn_for_any_exposure (sm_context *sm_ctxt,
      80                 :            :                               const supernode *node,
      81                 :            :                               const gimple *stmt,
      82                 :            :                               tree arg) const;
      83                 :            : };
      84                 :            : 
      85                 :            : class exposure_through_output_file
      86                 :            :   : public pending_diagnostic_subclass<exposure_through_output_file>
      87                 :            : {
      88                 :            : public:
      89                 :         43 :   exposure_through_output_file (const sensitive_state_machine &sm, tree arg)
      90                 :         43 :   : m_sm (sm), m_arg (arg)
      91                 :            :   {}
      92                 :            : 
      93                 :         18 :   const char *get_kind () const FINAL OVERRIDE
      94                 :            :   {
      95                 :         18 :     return "exposure_through_output_file";
      96                 :            :   }
      97                 :            : 
      98                 :          6 :   bool operator== (const exposure_through_output_file &other) const
      99                 :            :   {
     100                 :          6 :     return same_tree_p (m_arg, other.m_arg);
     101                 :            :   }
     102                 :            : 
     103                 :          6 :   bool emit (rich_location *rich_loc) FINAL OVERRIDE
     104                 :            :   {
     105                 :          6 :     diagnostic_metadata m;
     106                 :            :     /* CWE-532: Information Exposure Through Log Files */
     107                 :          6 :     m.add_cwe (532);
     108                 :          6 :     return warning_meta (rich_loc, m,
     109                 :            :                          OPT_Wanalyzer_exposure_through_output_file,
     110                 :            :                          "sensitive value %qE written to output file",
     111                 :          6 :                          m_arg);
     112                 :            :   }
     113                 :            : 
     114                 :         12 :   label_text describe_state_change (const evdesc::state_change &change)
     115                 :            :     FINAL OVERRIDE
     116                 :            :   {
     117                 :         12 :     if (change.m_new_state == m_sm.m_sensitive)
     118                 :            :       {
     119                 :         12 :         m_first_sensitive_event = change.m_event_id;
     120                 :         12 :         return change.formatted_print ("sensitive value acquired here");
     121                 :            :       }
     122                 :          0 :     return label_text ();
     123                 :            :   }
     124                 :            : 
     125                 :          2 :   label_text describe_call_with_state (const evdesc::call_with_state &info)
     126                 :            :     FINAL OVERRIDE
     127                 :            :   {
     128                 :          2 :     if (info.m_state == m_sm.m_sensitive)
     129                 :          2 :       return info.formatted_print
     130                 :            :         ("passing sensitive value %qE in call to %qE from %qE",
     131                 :          2 :          info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
     132                 :          0 :     return label_text ();
     133                 :            :   }
     134                 :            : 
     135                 :          2 :   label_text describe_return_of_state (const evdesc::return_of_state &info)
     136                 :            :     FINAL OVERRIDE
     137                 :            :   {
     138                 :          2 :     if (info.m_state == m_sm.m_sensitive)
     139                 :          2 :       return info.formatted_print ("returning sensitive value to %qE from %qE",
     140                 :          2 :                                    info.m_caller_fndecl, info.m_callee_fndecl);
     141                 :          0 :     return label_text ();
     142                 :            :   }
     143                 :            : 
     144                 :         12 :   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
     145                 :            :   {
     146                 :         12 :     if (m_first_sensitive_event.known_p ())
     147                 :         12 :       return ev.formatted_print ("sensitive value %qE written to output file"
     148                 :            :                                  "; acquired at %@",
     149                 :         12 :                                  m_arg, &m_first_sensitive_event);
     150                 :            :     else
     151                 :          0 :       return ev.formatted_print ("sensitive value %qE written to output file",
     152                 :          0 :                                  m_arg);
     153                 :            :   }
     154                 :            : 
     155                 :            : private:
     156                 :            :   const sensitive_state_machine &m_sm;
     157                 :            :   tree m_arg;
     158                 :            :   diagnostic_event_id_t m_first_sensitive_event;
     159                 :            : };
     160                 :            : 
     161                 :            : /* sensitive_state_machine's ctor.  */
     162                 :            : 
     163                 :        377 : sensitive_state_machine::sensitive_state_machine (logger *logger)
     164                 :        377 : : state_machine ("sensitive", logger)
     165                 :            : {
     166                 :        377 :   m_start = add_state ("start");
     167                 :        377 :   m_sensitive = add_state ("sensitive");
     168                 :        377 :   m_stop = add_state ("stop");
     169                 :        377 : }
     170                 :            : 
     171                 :            : /* Warn about an exposure at NODE and STMT if ARG is in the "sensitive"
     172                 :            :    state.  */
     173                 :            : 
     174                 :            : void
     175                 :         43 : sensitive_state_machine::warn_for_any_exposure (sm_context *sm_ctxt,
     176                 :            :                                                 const supernode *node,
     177                 :            :                                                 const gimple *stmt,
     178                 :            :                                                 tree arg) const
     179                 :            : {
     180                 :         43 :   sm_ctxt->warn_for_state (node, stmt, arg, m_sensitive,
     181                 :         43 :                            new exposure_through_output_file (*this, arg));
     182                 :         43 : }
     183                 :            : 
     184                 :            : /* Implementation of state_machine::on_stmt vfunc for
     185                 :            :    sensitive_state_machine.  */
     186                 :            : 
     187                 :            : bool
     188                 :      15270 : sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
     189                 :            :                                   const supernode *node,
     190                 :            :                                   const gimple *stmt) const
     191                 :            : {
     192                 :      15270 :   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     193                 :       3646 :     if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
     194                 :            :       {
     195                 :       3620 :         if (is_named_call_p (callee_fndecl, "getpass", call, 1))
     196                 :            :           {
     197                 :          6 :             tree lhs = gimple_call_lhs (call);
     198                 :          6 :             if (lhs)
     199                 :          6 :               sm_ctxt->on_transition (node, stmt, lhs, m_start, m_sensitive);
     200                 :          6 :             return true;
     201                 :            :           }
     202                 :       3614 :         else if (is_named_call_p (callee_fndecl, "fprintf")
     203                 :       3614 :                  || is_named_call_p (callee_fndecl, "printf"))
     204                 :            :           {
     205                 :            :             /* Handle a match at any position in varargs.  */
     206                 :         64 :             for (unsigned idx = 1; idx < gimple_call_num_args (call); idx++)
     207                 :            :               {
     208                 :         42 :                 tree arg = gimple_call_arg (call, idx);
     209                 :         42 :                 warn_for_any_exposure (sm_ctxt, node, stmt, arg);
     210                 :            :               }
     211                 :            :             return true;
     212                 :            :           }
     213                 :       3592 :         else if (is_named_call_p (callee_fndecl, "fwrite", call, 4))
     214                 :            :           {
     215                 :          1 :             tree arg = gimple_call_arg (call, 0);
     216                 :          1 :             warn_for_any_exposure (sm_ctxt, node, stmt, arg);
     217                 :          1 :             return true;
     218                 :            :           }
     219                 :            :         // TODO: ...etc.  This is just a proof-of-concept at this point.
     220                 :            :       }
     221                 :            :   return false;
     222                 :            : }
     223                 :            : 
     224                 :            : void
     225                 :       5149 : sensitive_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
     226                 :            :                                        const supernode *node ATTRIBUTE_UNUSED,
     227                 :            :                                        const gimple *stmt ATTRIBUTE_UNUSED,
     228                 :            :                                        tree lhs ATTRIBUTE_UNUSED,
     229                 :            :                                        enum tree_code op ATTRIBUTE_UNUSED,
     230                 :            :                                        tree rhs ATTRIBUTE_UNUSED) const
     231                 :            : {
     232                 :            :   /* Empty.  */
     233                 :       5149 : }
     234                 :            : 
     235                 :            : bool
     236                 :      18132 : sensitive_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
     237                 :            : {
     238                 :      18132 :   return true;
     239                 :            : }
     240                 :            : 
     241                 :            : } // anonymous namespace
     242                 :            : 
     243                 :            : /* Internal interface to this file. */
     244                 :            : 
     245                 :            : state_machine *
     246                 :        377 : make_sensitive_state_machine (logger *logger)
     247                 :            : {
     248                 :        377 :   return new sensitive_state_machine (logger);
     249                 :            : }
     250                 :            : 
     251                 :            : } // namespace ana
     252                 :            : 
     253                 :            : #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.