LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-sensitive.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 90.0 % 70 63
Test Date: 2024-12-21 13:15:12 Functions: 92.9 % 14 13
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-2024 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 "make-unique.h"
      26                 :             : #include "tree.h"
      27                 :             : #include "function.h"
      28                 :             : #include "basic-block.h"
      29                 :             : #include "gimple.h"
      30                 :             : #include "options.h"
      31                 :             : #include "diagnostic-core.h"
      32                 :             : #include "diagnostic-path.h"
      33                 :             : #include "analyzer/analyzer.h"
      34                 :             : #include "diagnostic-event-id.h"
      35                 :             : #include "analyzer/analyzer-logging.h"
      36                 :             : #include "analyzer/sm.h"
      37                 :             : #include "analyzer/pending-diagnostic.h"
      38                 :             : 
      39                 :             : #if ENABLE_ANALYZER
      40                 :             : 
      41                 :             : namespace ana {
      42                 :             : 
      43                 :             : namespace {
      44                 :             : 
      45                 :             : /* An experimental state machine, for tracking exposure of sensitive
      46                 :             :    data (e.g. through logging).  */
      47                 :             : 
      48                 :             : class sensitive_state_machine : public state_machine
      49                 :             : {
      50                 :             : public:
      51                 :             :   sensitive_state_machine (logger *logger);
      52                 :             : 
      53                 :     1031989 :   bool inherited_state_p () const final override { return true; }
      54                 :             : 
      55                 :             :   bool on_stmt (sm_context &sm_ctxt,
      56                 :             :                 const supernode *node,
      57                 :             :                 const gimple *stmt) const final override;
      58                 :             : 
      59                 :             :   bool can_purge_p (state_t s) const final override;
      60                 :             : 
      61                 :             :   /* State for "sensitive" data, such as a password.  */
      62                 :             :   state_t m_sensitive;
      63                 :             : 
      64                 :             :   /* Stop state, for a value we don't want to track any more.  */
      65                 :             :   state_t m_stop;
      66                 :             : 
      67                 :             : private:
      68                 :             :   void warn_for_any_exposure (sm_context &sm_ctxt,
      69                 :             :                               const supernode *node,
      70                 :             :                               const gimple *stmt,
      71                 :             :                               tree arg) const;
      72                 :             : };
      73                 :             : 
      74                 :           0 : class exposure_through_output_file
      75                 :             :   : public pending_diagnostic_subclass<exposure_through_output_file>
      76                 :             : {
      77                 :             : public:
      78                 :           6 :   exposure_through_output_file (const sensitive_state_machine &sm, tree arg)
      79                 :           6 :   : m_sm (sm), m_arg (arg)
      80                 :             :   {}
      81                 :             : 
      82                 :          24 :   const char *get_kind () const final override
      83                 :             :   {
      84                 :          24 :     return "exposure_through_output_file";
      85                 :             :   }
      86                 :             : 
      87                 :           6 :   bool operator== (const exposure_through_output_file &other) const
      88                 :             :   {
      89                 :           6 :     return same_tree_p (m_arg, other.m_arg);
      90                 :             :   }
      91                 :             : 
      92                 :          12 :   int get_controlling_option () const final override
      93                 :             :   {
      94                 :          12 :     return OPT_Wanalyzer_exposure_through_output_file;
      95                 :             :   }
      96                 :             : 
      97                 :           6 :   bool emit (diagnostic_emission_context &ctxt) final override
      98                 :             :   {
      99                 :             :     /* CWE-532: Information Exposure Through Log Files */
     100                 :           6 :     ctxt.add_cwe (532);
     101                 :           6 :     return ctxt.warn ("sensitive value %qE written to output file",
     102                 :           6 :                       m_arg);
     103                 :             :   }
     104                 :             : 
     105                 :             :   bool
     106                 :          12 :   describe_state_change (pretty_printer &pp,
     107                 :             :                          const evdesc::state_change &change) final override
     108                 :             :   {
     109                 :          12 :     if (change.m_new_state == m_sm.m_sensitive)
     110                 :             :       {
     111                 :          12 :         m_first_sensitive_event = change.m_event_id;
     112                 :          12 :         pp_string (&pp, "sensitive value acquired here");
     113                 :          12 :         return true;
     114                 :             :       }
     115                 :             :     return false;
     116                 :             :   }
     117                 :             : 
     118                 :             :   diagnostic_event::meaning
     119                 :           0 :   get_meaning_for_state_change (const evdesc::state_change &change)
     120                 :             :     const final override
     121                 :             :   {
     122                 :           0 :     if (change.m_new_state == m_sm.m_sensitive)
     123                 :           0 :       return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
     124                 :           0 :                                         diagnostic_event::NOUN_sensitive);
     125                 :           0 :     return diagnostic_event::meaning ();
     126                 :             :   }
     127                 :             :   bool
     128                 :           2 :   describe_call_with_state (pretty_printer &pp,
     129                 :             :                             const evdesc::call_with_state &info) final override
     130                 :             :   {
     131                 :           2 :     if (info.m_state == m_sm.m_sensitive)
     132                 :             :       {
     133                 :           2 :         pp_printf (&pp,
     134                 :             :                    "passing sensitive value %qE in call to %qE from %qE",
     135                 :           2 :                    info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
     136                 :           2 :         return true;
     137                 :             :       }
     138                 :             :     return false;
     139                 :             :   }
     140                 :             : 
     141                 :             :   bool
     142                 :           2 :   describe_return_of_state (pretty_printer &pp,
     143                 :             :                             const evdesc::return_of_state &info) final override
     144                 :             :   {
     145                 :           2 :     if (info.m_state == m_sm.m_sensitive)
     146                 :             :       {
     147                 :           2 :         pp_printf (&pp,
     148                 :             :                    "returning sensitive value to %qE from %qE",
     149                 :           2 :                    info.m_caller_fndecl, info.m_callee_fndecl);
     150                 :           2 :         return true;
     151                 :             :       }
     152                 :             :     return false;
     153                 :             :   }
     154                 :             : 
     155                 :             :   bool
     156                 :          12 :   describe_final_event (pretty_printer &pp,
     157                 :             :                         const evdesc::final_event &) final override
     158                 :             :   {
     159                 :          12 :     if (m_first_sensitive_event.known_p ())
     160                 :          12 :       pp_printf (&pp,
     161                 :             :                  "sensitive value %qE written to output file"
     162                 :             :                  "; acquired at %@",
     163                 :             :                  m_arg, &m_first_sensitive_event);
     164                 :             :     else
     165                 :           0 :       pp_printf (&pp,
     166                 :             :                  "sensitive value %qE written to output file",
     167                 :             :                  m_arg);
     168                 :          12 :     return true;
     169                 :             :   }
     170                 :             : 
     171                 :             : private:
     172                 :             :   const sensitive_state_machine &m_sm;
     173                 :             :   tree m_arg;
     174                 :             :   diagnostic_event_id_t m_first_sensitive_event;
     175                 :             : };
     176                 :             : 
     177                 :             : /* sensitive_state_machine's ctor.  */
     178                 :             : 
     179                 :        3199 : sensitive_state_machine::sensitive_state_machine (logger *logger)
     180                 :             : : state_machine ("sensitive", logger),
     181                 :        6398 :   m_sensitive (add_state ("sensitive")),
     182                 :        3199 :   m_stop (add_state ("stop"))
     183                 :             : {
     184                 :        3199 : }
     185                 :             : 
     186                 :             : /* Warn about an exposure at NODE and STMT if ARG is in the "sensitive"
     187                 :             :    state.  */
     188                 :             : 
     189                 :             : void
     190                 :        2092 : sensitive_state_machine::warn_for_any_exposure (sm_context &sm_ctxt,
     191                 :             :                                                 const supernode *node,
     192                 :             :                                                 const gimple *stmt,
     193                 :             :                                                 tree arg) const
     194                 :             : {
     195                 :        2092 :   if (sm_ctxt.get_state (stmt, arg) == m_sensitive)
     196                 :             :     {
     197                 :           6 :       tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
     198                 :           6 :       sm_ctxt.warn (node, stmt, arg,
     199                 :          12 :                     make_unique<exposure_through_output_file> (*this,
     200                 :             :                                                                diag_arg));
     201                 :             :     }
     202                 :        2092 : }
     203                 :             : 
     204                 :             : /* Implementation of state_machine::on_stmt vfunc for
     205                 :             :    sensitive_state_machine.  */
     206                 :             : 
     207                 :             : bool
     208                 :      267689 : sensitive_state_machine::on_stmt (sm_context &sm_ctxt,
     209                 :             :                                   const supernode *node,
     210                 :             :                                   const gimple *stmt) const
     211                 :             : {
     212                 :      267689 :   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     213                 :       55539 :     if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call))
     214                 :             :       {
     215                 :       54178 :         if (is_named_call_p (callee_fndecl, "getpass", call, 1))
     216                 :             :           {
     217                 :           7 :             tree lhs = gimple_call_lhs (call);
     218                 :           7 :             if (lhs)
     219                 :           7 :               sm_ctxt.on_transition (node, stmt, lhs, m_start, m_sensitive);
     220                 :           7 :             return true;
     221                 :             :           }
     222                 :       54171 :         else if (is_named_call_p (callee_fndecl, "fprintf")
     223                 :       54171 :                  || is_named_call_p (callee_fndecl, "printf"))
     224                 :             :           {
     225                 :             :             /* Handle a match at any position in varargs.  */
     226                 :        2944 :             for (unsigned idx = 1; idx < gimple_call_num_args (call); idx++)
     227                 :             :               {
     228                 :        1935 :                 tree arg = gimple_call_arg (call, idx);
     229                 :        1935 :                 warn_for_any_exposure (sm_ctxt, node, stmt, arg);
     230                 :             :               }
     231                 :             :             return true;
     232                 :             :           }
     233                 :       53162 :         else if (is_named_call_p (callee_fndecl, "fwrite", call, 4))
     234                 :             :           {
     235                 :         157 :             tree arg = gimple_call_arg (call, 0);
     236                 :         157 :             warn_for_any_exposure (sm_ctxt, node, stmt, arg);
     237                 :         157 :             return true;
     238                 :             :           }
     239                 :             :         // TODO: ...etc.  This is just a proof-of-concept at this point.
     240                 :             :       }
     241                 :             :   return false;
     242                 :             : }
     243                 :             : 
     244                 :             : bool
     245                 :     1001238 : sensitive_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
     246                 :             : {
     247                 :     1001238 :   return true;
     248                 :             : }
     249                 :             : 
     250                 :             : } // anonymous namespace
     251                 :             : 
     252                 :             : /* Internal interface to this file. */
     253                 :             : 
     254                 :             : state_machine *
     255                 :        3199 : make_sensitive_state_machine (logger *logger)
     256                 :             : {
     257                 :        3199 :   return new sensitive_state_machine (logger);
     258                 :             : }
     259                 :             : 
     260                 :             : } // namespace ana
     261                 :             : 
     262                 :             : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.1-beta

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,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.