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 : 1023421 : 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 : 3197 : sensitive_state_machine::sensitive_state_machine (logger *logger)
180 : : : state_machine ("sensitive", logger),
181 : 6394 : m_sensitive (add_state ("sensitive")),
182 : 3197 : m_stop (add_state ("stop"))
183 : : {
184 : 3197 : }
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 : 267622 : sensitive_state_machine::on_stmt (sm_context &sm_ctxt,
209 : : const supernode *node,
210 : : const gimple *stmt) const
211 : : {
212 : 267622 : if (const gcall *call = dyn_cast <const gcall *> (stmt))
213 : 55531 : if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call))
214 : : {
215 : 54170 : 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 : 54163 : else if (is_named_call_p (callee_fndecl, "fprintf")
223 : 54163 : || 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 : 53154 : 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 : 992645 : sensitive_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
246 : : {
247 : 992645 : return true;
248 : : }
249 : :
250 : : } // anonymous namespace
251 : :
252 : : /* Internal interface to this file. */
253 : :
254 : : state_machine *
255 : 3197 : make_sensitive_state_machine (logger *logger)
256 : : {
257 : 3197 : return new sensitive_state_machine (logger);
258 : : }
259 : :
260 : : } // namespace ana
261 : :
262 : : #endif /* #if ENABLE_ANALYZER */
|