1 /* An experimental state machine, for tracking exposure of sensitive
2 data (e.g. through logging).
3 Copyright (C) 2019-2021 Free Software Foundation, Inc.
4 Contributed by David Malcolm <dmalcolm@redhat.com>.
6 This file is part of GCC.
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)
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.
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/>. */
24 #include "coretypes.h"
28 #include "basic-block.h"
31 #include "diagnostic-path.h"
32 #include "diagnostic-metadata.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"
47 /* An experimental state machine, for tracking exposure of sensitive
48 data (e.g. through logging). */
50 class sensitive_state_machine
: public state_machine
53 sensitive_state_machine (logger
*logger
);
55 bool inherited_state_p () const FINAL OVERRIDE
{ return true; }
57 bool on_stmt (sm_context
*sm_ctxt
,
58 const supernode
*node
,
59 const gimple
*stmt
) const FINAL OVERRIDE
;
61 void on_condition (sm_context
*sm_ctxt
,
62 const supernode
*node
,
66 tree rhs
) const FINAL OVERRIDE
;
68 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
70 /* State for "sensitive" data, such as a password. */
73 /* Stop state, for a value we don't want to track any more. */
77 void warn_for_any_exposure (sm_context
*sm_ctxt
,
78 const supernode
*node
,
83 class exposure_through_output_file
84 : public pending_diagnostic_subclass
<exposure_through_output_file
>
87 exposure_through_output_file (const sensitive_state_machine
&sm
, tree arg
)
88 : m_sm (sm
), m_arg (arg
)
91 const char *get_kind () const FINAL OVERRIDE
93 return "exposure_through_output_file";
96 bool operator== (const exposure_through_output_file
&other
) const
98 return same_tree_p (m_arg
, other
.m_arg
);
101 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
103 diagnostic_metadata m
;
104 /* CWE-532: Information Exposure Through Log Files */
106 return warning_meta (rich_loc
, m
,
107 OPT_Wanalyzer_exposure_through_output_file
,
108 "sensitive value %qE written to output file",
112 label_text
describe_state_change (const evdesc::state_change
&change
)
115 if (change
.m_new_state
== m_sm
.m_sensitive
)
117 m_first_sensitive_event
= change
.m_event_id
;
118 return change
.formatted_print ("sensitive value acquired here");
120 return label_text ();
123 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
126 if (info
.m_state
== m_sm
.m_sensitive
)
127 return info
.formatted_print
128 ("passing sensitive value %qE in call to %qE from %qE",
129 info
.m_expr
, info
.m_callee_fndecl
, info
.m_caller_fndecl
);
130 return label_text ();
133 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
136 if (info
.m_state
== m_sm
.m_sensitive
)
137 return info
.formatted_print ("returning sensitive value to %qE from %qE",
138 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
139 return label_text ();
142 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
144 if (m_first_sensitive_event
.known_p ())
145 return ev
.formatted_print ("sensitive value %qE written to output file"
147 m_arg
, &m_first_sensitive_event
);
149 return ev
.formatted_print ("sensitive value %qE written to output file",
154 const sensitive_state_machine
&m_sm
;
156 diagnostic_event_id_t m_first_sensitive_event
;
159 /* sensitive_state_machine's ctor. */
161 sensitive_state_machine::sensitive_state_machine (logger
*logger
)
162 : state_machine ("sensitive", logger
)
164 m_sensitive
= add_state ("sensitive");
165 m_stop
= add_state ("stop");
168 /* Warn about an exposure at NODE and STMT if ARG is in the "sensitive"
172 sensitive_state_machine::warn_for_any_exposure (sm_context
*sm_ctxt
,
173 const supernode
*node
,
177 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
178 if (sm_ctxt
->get_state (stmt
, arg
) == m_sensitive
)
179 sm_ctxt
->warn (node
, stmt
, arg
,
180 new exposure_through_output_file (*this, diag_arg
));
183 /* Implementation of state_machine::on_stmt vfunc for
184 sensitive_state_machine. */
187 sensitive_state_machine::on_stmt (sm_context
*sm_ctxt
,
188 const supernode
*node
,
189 const gimple
*stmt
) const
191 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
192 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
194 if (is_named_call_p (callee_fndecl
, "getpass", call
, 1))
196 tree lhs
= gimple_call_lhs (call
);
198 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_sensitive
);
201 else if (is_named_call_p (callee_fndecl
, "fprintf")
202 || is_named_call_p (callee_fndecl
, "printf"))
204 /* Handle a match at any position in varargs. */
205 for (unsigned idx
= 1; idx
< gimple_call_num_args (call
); idx
++)
207 tree arg
= gimple_call_arg (call
, idx
);
208 warn_for_any_exposure (sm_ctxt
, node
, stmt
, arg
);
212 else if (is_named_call_p (callee_fndecl
, "fwrite", call
, 4))
214 tree arg
= gimple_call_arg (call
, 0);
215 warn_for_any_exposure (sm_ctxt
, node
, stmt
, arg
);
218 // TODO: ...etc. This is just a proof-of-concept at this point.
224 sensitive_state_machine::on_condition (sm_context
*sm_ctxt ATTRIBUTE_UNUSED
,
225 const supernode
*node ATTRIBUTE_UNUSED
,
226 const gimple
*stmt ATTRIBUTE_UNUSED
,
227 tree lhs ATTRIBUTE_UNUSED
,
228 enum tree_code op ATTRIBUTE_UNUSED
,
229 tree rhs ATTRIBUTE_UNUSED
) const
235 sensitive_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED
) const
240 } // anonymous namespace
242 /* Internal interface to this file. */
245 make_sensitive_state_machine (logger
*logger
)
247 return new sensitive_state_machine (logger
);
252 #endif /* #if ENABLE_ANALYZER */