Branch data Line data Source code
1 : : /* A state machine for tracking "taint": unsanitized uses
2 : : of data potentially under an attacker's control.
3 : :
4 : : Copyright (C) 2019-2024 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 : : #define INCLUDE_MEMORY
25 : : #include "system.h"
26 : : #include "coretypes.h"
27 : : #include "make-unique.h"
28 : : #include "tree.h"
29 : : #include "function.h"
30 : : #include "basic-block.h"
31 : : #include "gimple.h"
32 : : #include "options.h"
33 : : #include "diagnostic-path.h"
34 : : #include "analyzer/analyzer.h"
35 : : #include "analyzer/analyzer-logging.h"
36 : : #include "gimple-iterator.h"
37 : : #include "ordered-hash-map.h"
38 : : #include "cgraph.h"
39 : : #include "cfg.h"
40 : : #include "digraph.h"
41 : : #include "stringpool.h"
42 : : #include "attribs.h"
43 : : #include "fold-const.h"
44 : : #include "analyzer/supergraph.h"
45 : : #include "analyzer/call-string.h"
46 : : #include "analyzer/program-point.h"
47 : : #include "analyzer/store.h"
48 : : #include "analyzer/region-model.h"
49 : : #include "analyzer/sm.h"
50 : : #include "analyzer/program-state.h"
51 : : #include "analyzer/pending-diagnostic.h"
52 : : #include "analyzer/constraint-manager.h"
53 : : #include "diagnostic-format-sarif.h"
54 : :
55 : : #if ENABLE_ANALYZER
56 : :
57 : : namespace ana {
58 : :
59 : : namespace {
60 : :
61 : : /* An enum for describing tainted values. */
62 : :
63 : : enum bounds
64 : : {
65 : : /* This tainted value has no upper or lower bound. */
66 : : BOUNDS_NONE,
67 : :
68 : : /* This tainted value has an upper bound but not lower bound. */
69 : : BOUNDS_UPPER,
70 : :
71 : : /* This tainted value has a lower bound but no upper bound. */
72 : : BOUNDS_LOWER
73 : : };
74 : :
75 : : static const char *
76 : 0 : bounds_to_str (enum bounds b)
77 : : {
78 : 0 : switch (b)
79 : : {
80 : 0 : default:
81 : 0 : gcc_unreachable ();
82 : : case BOUNDS_NONE:
83 : : return "BOUNDS_NONE";
84 : 0 : case BOUNDS_UPPER:
85 : 0 : return "BOUNDS_UPPER";
86 : 0 : case BOUNDS_LOWER:
87 : 0 : return "BOUNDS_LOWER";
88 : : }
89 : : }
90 : :
91 : : /* An experimental state machine, for tracking "taint": unsanitized uses
92 : : of data potentially under an attacker's control. */
93 : :
94 : : class taint_state_machine : public state_machine
95 : : {
96 : : public:
97 : : taint_state_machine (logger *logger);
98 : :
99 : 1680618 : bool inherited_state_p () const final override { return true; }
100 : :
101 : : state_t alt_get_inherited_state (const sm_state_map &map,
102 : : const svalue *sval,
103 : : const extrinsic_state &ext_state)
104 : : const final override;
105 : :
106 : : bool
107 : 682191 : has_alt_get_inherited_state_p () const final override
108 : : {
109 : 682191 : return true;
110 : : }
111 : :
112 : : bool on_stmt (sm_context *sm_ctxt,
113 : : const supernode *node,
114 : : const gimple *stmt) const final override;
115 : :
116 : : void on_condition (sm_context *sm_ctxt,
117 : : const supernode *node,
118 : : const gimple *stmt,
119 : : const svalue *lhs,
120 : : enum tree_code op,
121 : : const svalue *rhs) const final override;
122 : : void on_bounded_ranges (sm_context *sm_ctxt,
123 : : const supernode *node,
124 : : const gimple *stmt,
125 : : const svalue &sval,
126 : : const bounded_ranges &ranges) const final override;
127 : :
128 : : bool can_purge_p (state_t s) const final override;
129 : :
130 : : bool get_taint (state_t s, tree type, enum bounds *out) const;
131 : :
132 : : state_t combine_states (state_t s0, state_t s1) const;
133 : :
134 : : private:
135 : : void check_control_flow_arg_for_taint (sm_context *sm_ctxt,
136 : : const gimple *stmt,
137 : : tree expr) const;
138 : :
139 : : void check_for_tainted_size_arg (sm_context *sm_ctxt,
140 : : const supernode *node,
141 : : const gcall *call,
142 : : tree callee_fndecl) const;
143 : : void check_for_tainted_divisor (sm_context *sm_ctxt,
144 : : const supernode *node,
145 : : const gassign *assign) const;
146 : :
147 : : public:
148 : : /* State for a "tainted" value: unsanitized data potentially under an
149 : : attacker's control. */
150 : : state_t m_tainted;
151 : :
152 : : /* State for a "tainted" value that has a lower bound. */
153 : : state_t m_has_lb;
154 : :
155 : : /* State for a "tainted" value that has an upper bound. */
156 : : state_t m_has_ub;
157 : :
158 : : /* Stop state, for a value we don't want to track any more. */
159 : : state_t m_stop;
160 : :
161 : : /* Global state, for when the last condition had tainted arguments. */
162 : : state_t m_tainted_control_flow;
163 : : };
164 : :
165 : : /* Class for diagnostics relating to taint_state_machine. */
166 : :
167 : 0 : class taint_diagnostic : public pending_diagnostic
168 : : {
169 : : public:
170 : 284 : taint_diagnostic (const taint_state_machine &sm, tree arg,
171 : : enum bounds has_bounds)
172 : 284 : : m_sm (sm), m_arg (arg), m_has_bounds (has_bounds)
173 : : {}
174 : :
175 : 290 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
176 : : {
177 : 290 : const taint_diagnostic &other = (const taint_diagnostic &)base_other;
178 : 290 : return (same_tree_p (m_arg, other.m_arg)
179 : 290 : && m_has_bounds == other.m_has_bounds);
180 : : }
181 : :
182 : 6 : label_text describe_state_change (const evdesc::state_change &change) override
183 : : {
184 : 6 : if (change.m_new_state == m_sm.m_tainted)
185 : : {
186 : 0 : if (change.m_origin)
187 : 0 : return change.formatted_print ("%qE has an unchecked value here"
188 : : " (from %qE)",
189 : 0 : change.m_expr, change.m_origin);
190 : : else
191 : 0 : return change.formatted_print ("%qE gets an unchecked value here",
192 : 0 : change.m_expr);
193 : : }
194 : 6 : else if (change.m_new_state == m_sm.m_has_lb)
195 : 4 : return change.formatted_print ("%qE has its lower bound checked here",
196 : 4 : change.m_expr);
197 : 2 : else if (change.m_new_state == m_sm.m_has_ub)
198 : 2 : return change.formatted_print ("%qE has its upper bound checked here",
199 : 2 : change.m_expr);
200 : 0 : return label_text ();
201 : : }
202 : :
203 : : diagnostic_event::meaning
204 : 0 : get_meaning_for_state_change (const evdesc::state_change &change)
205 : : const final override
206 : : {
207 : 0 : if (change.m_new_state == m_sm.m_tainted)
208 : 0 : return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
209 : 0 : diagnostic_event::NOUN_taint);
210 : 0 : return diagnostic_event::meaning ();
211 : : }
212 : :
213 : 0 : void maybe_add_sarif_properties (sarif_object &result_obj)
214 : : const override
215 : : {
216 : 0 : sarif_property_bag &props = result_obj.get_or_create_properties ();
217 : : #define PROPERTY_PREFIX "gcc/analyzer/taint_diagnostic/"
218 : 0 : props.set (PROPERTY_PREFIX "arg", tree_to_json (m_arg));
219 : 0 : props.set_string (PROPERTY_PREFIX "has_bounds",
220 : 0 : bounds_to_str (m_has_bounds));
221 : : #undef PROPERTY_PREFIX
222 : 0 : }
223 : :
224 : : protected:
225 : : const taint_state_machine &m_sm;
226 : : tree m_arg;
227 : : enum bounds m_has_bounds;
228 : : };
229 : :
230 : : /* Concrete taint_diagnostic subclass for reporting attacker-controlled
231 : : array index. */
232 : :
233 : 0 : class tainted_array_index : public taint_diagnostic
234 : : {
235 : : public:
236 : 134 : tainted_array_index (const taint_state_machine &sm, tree arg,
237 : : enum bounds has_bounds)
238 : 134 : : taint_diagnostic (sm, arg, has_bounds)
239 : : {}
240 : :
241 : 473 : const char *get_kind () const final override { return "tainted_array_index"; }
242 : :
243 : 205 : int get_controlling_option () const final override
244 : : {
245 : 205 : return OPT_Wanalyzer_tainted_array_index;
246 : : }
247 : :
248 : 71 : bool emit (diagnostic_emission_context &ctxt) final override
249 : : {
250 : : /* CWE-129: "Improper Validation of Array Index". */
251 : 71 : ctxt.add_cwe (129);
252 : 71 : if (m_arg)
253 : 71 : switch (m_has_bounds)
254 : : {
255 : 0 : default:
256 : 0 : gcc_unreachable ();
257 : 53 : case BOUNDS_NONE:
258 : 53 : return ctxt.warn ("use of attacker-controlled value %qE"
259 : : " in array lookup without bounds checking",
260 : 53 : m_arg);
261 : 5 : break;
262 : 5 : case BOUNDS_UPPER:
263 : 5 : return ctxt.warn ("use of attacker-controlled value %qE"
264 : : " in array lookup without checking for negative",
265 : 5 : m_arg);
266 : 13 : break;
267 : 13 : case BOUNDS_LOWER:
268 : 13 : return ctxt.warn ("use of attacker-controlled value %qE"
269 : : " in array lookup without upper-bounds checking",
270 : 13 : m_arg);
271 : 0 : break;
272 : : }
273 : : else
274 : 0 : switch (m_has_bounds)
275 : : {
276 : 0 : default:
277 : 0 : gcc_unreachable ();
278 : 0 : case BOUNDS_NONE:
279 : 0 : return ctxt.warn ("use of attacker-controlled value"
280 : 0 : " in array lookup without bounds checking");
281 : 0 : break;
282 : 0 : case BOUNDS_UPPER:
283 : 0 : return ctxt.warn ("use of attacker-controlled value"
284 : : " in array lookup without checking for"
285 : 0 : " negative");
286 : 0 : break;
287 : 0 : case BOUNDS_LOWER:
288 : 0 : return ctxt.warn ("use of attacker-controlled value"
289 : : " in array lookup without upper-bounds"
290 : 0 : " checking");
291 : : break;
292 : : }
293 : : }
294 : :
295 : 142 : label_text describe_final_event (const evdesc::final_event &ev) final override
296 : : {
297 : 142 : if (m_arg)
298 : 142 : switch (m_has_bounds)
299 : : {
300 : 0 : default:
301 : 0 : gcc_unreachable ();
302 : 106 : case BOUNDS_NONE:
303 : 106 : return ev.formatted_print
304 : : ("use of attacker-controlled value %qE in array lookup"
305 : : " without bounds checking",
306 : 106 : m_arg);
307 : 10 : case BOUNDS_UPPER:
308 : 10 : return ev.formatted_print
309 : : ("use of attacker-controlled value %qE"
310 : : " in array lookup without checking for negative",
311 : 10 : m_arg);
312 : 26 : case BOUNDS_LOWER:
313 : 26 : return ev.formatted_print
314 : : ("use of attacker-controlled value %qE"
315 : : " in array lookup without upper-bounds checking",
316 : 26 : m_arg);
317 : : }
318 : : else
319 : 0 : switch (m_has_bounds)
320 : : {
321 : 0 : default:
322 : 0 : gcc_unreachable ();
323 : 0 : case BOUNDS_NONE:
324 : 0 : return ev.formatted_print
325 : : ("use of attacker-controlled value in array lookup"
326 : 0 : " without bounds checking");
327 : 0 : case BOUNDS_UPPER:
328 : 0 : return ev.formatted_print
329 : : ("use of attacker-controlled value"
330 : 0 : " in array lookup without checking for negative");
331 : 0 : case BOUNDS_LOWER:
332 : 0 : return ev.formatted_print
333 : : ("use of attacker-controlled value"
334 : 0 : " in array lookup without upper-bounds checking");
335 : : }
336 : : }
337 : : };
338 : :
339 : : /* Concrete taint_diagnostic subclass for reporting attacker-controlled
340 : : pointer offset. */
341 : :
342 : 0 : class tainted_offset : public taint_diagnostic
343 : : {
344 : : public:
345 : 45 : tainted_offset (const taint_state_machine &sm, tree arg,
346 : : enum bounds has_bounds,
347 : : const svalue *offset)
348 : 45 : : taint_diagnostic (sm, arg, has_bounds),
349 : 45 : m_offset (offset)
350 : : {}
351 : :
352 : 151 : const char *get_kind () const final override { return "tainted_offset"; }
353 : :
354 : 61 : int get_controlling_option () const final override
355 : : {
356 : 61 : return OPT_Wanalyzer_tainted_offset;
357 : : }
358 : :
359 : 16 : bool emit (diagnostic_emission_context &ctxt) final override
360 : : {
361 : : /* CWE-823: "Use of Out-of-range Pointer Offset". */
362 : 16 : ctxt.add_cwe (823);
363 : 16 : if (m_arg)
364 : 16 : switch (m_has_bounds)
365 : : {
366 : 0 : default:
367 : 0 : gcc_unreachable ();
368 : 4 : case BOUNDS_NONE:
369 : 4 : return ctxt.warn ("use of attacker-controlled value %qE as offset"
370 : : " without bounds checking",
371 : 4 : m_arg);
372 : 2 : break;
373 : 2 : case BOUNDS_UPPER:
374 : 2 : return ctxt.warn ("use of attacker-controlled value %qE as offset"
375 : : " without lower-bounds checking",
376 : 2 : m_arg);
377 : 10 : break;
378 : 10 : case BOUNDS_LOWER:
379 : 10 : return ctxt.warn ("use of attacker-controlled value %qE as offset"
380 : : " without upper-bounds checking",
381 : 10 : m_arg);
382 : 0 : break;
383 : : }
384 : : else
385 : 0 : switch (m_has_bounds)
386 : : {
387 : 0 : default:
388 : 0 : gcc_unreachable ();
389 : 0 : case BOUNDS_NONE:
390 : 0 : return ctxt.warn ("use of attacker-controlled value as offset"
391 : 0 : " without bounds checking");
392 : 0 : break;
393 : 0 : case BOUNDS_UPPER:
394 : 0 : return ctxt.warn ("use of attacker-controlled value as offset"
395 : 0 : " without lower-bounds checking");
396 : 0 : break;
397 : 0 : case BOUNDS_LOWER:
398 : 0 : return ctxt.warn ("use of attacker-controlled value as offset"
399 : 0 : " without upper-bounds checking");
400 : : break;
401 : : }
402 : : }
403 : :
404 : 32 : label_text describe_final_event (const evdesc::final_event &ev) final override
405 : : {
406 : 32 : if (m_arg)
407 : 32 : switch (m_has_bounds)
408 : : {
409 : 0 : default:
410 : 0 : gcc_unreachable ();
411 : 8 : case BOUNDS_NONE:
412 : 8 : return ev.formatted_print ("use of attacker-controlled value %qE"
413 : : " as offset without bounds checking",
414 : 8 : m_arg);
415 : 4 : case BOUNDS_UPPER:
416 : 4 : return ev.formatted_print ("use of attacker-controlled value %qE"
417 : : " as offset without lower-bounds checking",
418 : 4 : m_arg);
419 : 20 : case BOUNDS_LOWER:
420 : 20 : return ev.formatted_print ("use of attacker-controlled value %qE"
421 : : " as offset without upper-bounds checking",
422 : 20 : m_arg);
423 : : }
424 : : else
425 : 0 : switch (m_has_bounds)
426 : : {
427 : 0 : default:
428 : 0 : gcc_unreachable ();
429 : 0 : case BOUNDS_NONE:
430 : 0 : return ev.formatted_print ("use of attacker-controlled value"
431 : 0 : " as offset without bounds checking");
432 : 0 : case BOUNDS_UPPER:
433 : 0 : return ev.formatted_print ("use of attacker-controlled value"
434 : : " as offset without lower-bounds"
435 : 0 : " checking");
436 : 0 : case BOUNDS_LOWER:
437 : 0 : return ev.formatted_print ("use of attacker-controlled value"
438 : : " as offset without upper-bounds"
439 : 0 : " checking");
440 : : }
441 : : }
442 : :
443 : 0 : void maybe_add_sarif_properties (sarif_object &result_obj)
444 : : const final override
445 : : {
446 : 0 : taint_diagnostic::maybe_add_sarif_properties (result_obj);
447 : 0 : sarif_property_bag &props = result_obj.get_or_create_properties ();
448 : : #define PROPERTY_PREFIX "gcc/analyzer/tainted_offset/"
449 : 0 : props.set (PROPERTY_PREFIX "offset", m_offset->to_json ());
450 : : #undef PROPERTY_PREFIX
451 : 0 : }
452 : :
453 : : private:
454 : : const svalue *m_offset;
455 : : };
456 : :
457 : : /* Concrete taint_diagnostic subclass for reporting attacker-controlled
458 : : size. */
459 : :
460 : 0 : class tainted_size : public taint_diagnostic
461 : : {
462 : : public:
463 : 8 : tainted_size (const taint_state_machine &sm, tree arg,
464 : : enum bounds has_bounds)
465 : 8 : : taint_diagnostic (sm, arg, has_bounds)
466 : : {}
467 : :
468 : 11 : const char *get_kind () const override { return "tainted_size"; }
469 : :
470 : 12 : int get_controlling_option () const final override
471 : : {
472 : 12 : return OPT_Wanalyzer_tainted_size;
473 : : }
474 : :
475 : 4 : bool emit (diagnostic_emission_context &ctxt) override
476 : : {
477 : : /* "CWE-129: Improper Validation of Array Index". */
478 : 4 : ctxt.add_cwe (129);
479 : 4 : if (m_arg)
480 : 4 : switch (m_has_bounds)
481 : : {
482 : 0 : default:
483 : 0 : gcc_unreachable ();
484 : 0 : case BOUNDS_NONE:
485 : 0 : return ctxt.warn ("use of attacker-controlled value %qE as size"
486 : : " without bounds checking",
487 : 0 : m_arg);
488 : 0 : break;
489 : 0 : case BOUNDS_UPPER:
490 : 0 : return ctxt.warn ("use of attacker-controlled value %qE as size"
491 : : " without lower-bounds checking",
492 : 0 : m_arg);
493 : 4 : break;
494 : 4 : case BOUNDS_LOWER:
495 : 4 : return ctxt.warn ("use of attacker-controlled value %qE as size"
496 : : " without upper-bounds checking",
497 : 4 : m_arg);
498 : 0 : break;
499 : : }
500 : : else
501 : 0 : switch (m_has_bounds)
502 : : {
503 : 0 : default:
504 : 0 : gcc_unreachable ();
505 : 0 : case BOUNDS_NONE:
506 : 0 : return ctxt.warn ("use of attacker-controlled value as size"
507 : 0 : " without bounds checking");
508 : 0 : break;
509 : 0 : case BOUNDS_UPPER:
510 : 0 : return ctxt.warn ("use of attacker-controlled value as size"
511 : 0 : " without lower-bounds checking");
512 : 0 : break;
513 : 0 : case BOUNDS_LOWER:
514 : 0 : return ctxt.warn ("use of attacker-controlled value as size"
515 : 0 : " without upper-bounds checking");
516 : : break;
517 : : }
518 : : }
519 : :
520 : 8 : label_text describe_final_event (const evdesc::final_event &ev) final override
521 : : {
522 : 8 : if (m_arg)
523 : 8 : switch (m_has_bounds)
524 : : {
525 : 0 : default:
526 : 0 : gcc_unreachable ();
527 : 0 : case BOUNDS_NONE:
528 : 0 : return ev.formatted_print ("use of attacker-controlled value %qE"
529 : : " as size without bounds checking",
530 : 0 : m_arg);
531 : 0 : case BOUNDS_UPPER:
532 : 0 : return ev.formatted_print ("use of attacker-controlled value %qE"
533 : : " as size without lower-bounds checking",
534 : 0 : m_arg);
535 : 8 : case BOUNDS_LOWER:
536 : 8 : return ev.formatted_print ("use of attacker-controlled value %qE"
537 : : " as size without upper-bounds checking",
538 : 8 : m_arg);
539 : : }
540 : : else
541 : 0 : switch (m_has_bounds)
542 : : {
543 : 0 : default:
544 : 0 : gcc_unreachable ();
545 : 0 : case BOUNDS_NONE:
546 : 0 : return ev.formatted_print ("use of attacker-controlled value"
547 : 0 : " as size without bounds checking");
548 : 0 : case BOUNDS_UPPER:
549 : 0 : return ev.formatted_print ("use of attacker-controlled value"
550 : 0 : " as size without lower-bounds checking");
551 : 0 : case BOUNDS_LOWER:
552 : 0 : return ev.formatted_print ("use of attacker-controlled value"
553 : 0 : " as size without upper-bounds checking");
554 : : }
555 : : }
556 : : };
557 : :
558 : : /* Subclass of tainted_size for reporting on tainted size values
559 : : passed to an external function annotated with attribute "access". */
560 : :
561 : 0 : class tainted_access_attrib_size : public tainted_size
562 : : {
563 : : public:
564 : 5 : tainted_access_attrib_size (const taint_state_machine &sm, tree arg,
565 : : enum bounds has_bounds, tree callee_fndecl,
566 : : unsigned size_argno, const char *access_str)
567 : 5 : : tainted_size (sm, arg, has_bounds),
568 : 5 : m_callee_fndecl (callee_fndecl),
569 : 5 : m_size_argno (size_argno), m_access_str (access_str)
570 : : {
571 : : }
572 : :
573 : 17 : const char *get_kind () const override
574 : : {
575 : 17 : return "tainted_access_attrib_size";
576 : : }
577 : :
578 : 2 : bool emit (diagnostic_emission_context &ctxt) final override
579 : : {
580 : 2 : bool warned = tainted_size::emit (ctxt);
581 : 2 : if (warned)
582 : : {
583 : 2 : inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
584 : : "parameter %i of %qD marked as a size via attribute %qs",
585 : 2 : m_size_argno + 1, m_callee_fndecl, m_access_str);
586 : : }
587 : 2 : return warned;
588 : : }
589 : :
590 : : private:
591 : : tree m_callee_fndecl;
592 : : unsigned m_size_argno;
593 : : const char *m_access_str;
594 : : };
595 : :
596 : : /* Concrete taint_diagnostic subclass for reporting attacker-controlled
597 : : divisor (so that an attacker can trigger a divide by zero). */
598 : :
599 : 0 : class tainted_divisor : public taint_diagnostic
600 : : {
601 : : public:
602 : 27 : tainted_divisor (const taint_state_machine &sm, tree arg,
603 : : enum bounds has_bounds)
604 : 27 : : taint_diagnostic (sm, arg, has_bounds)
605 : : {}
606 : :
607 : 63 : const char *get_kind () const final override { return "tainted_divisor"; }
608 : :
609 : 27 : int get_controlling_option () const final override
610 : : {
611 : 27 : return OPT_Wanalyzer_tainted_divisor;
612 : : }
613 : :
614 : 9 : bool emit (diagnostic_emission_context &ctxt) final override
615 : : {
616 : : /* CWE-369: "Divide By Zero". */
617 : 9 : ctxt.add_cwe (369);
618 : 9 : if (m_arg)
619 : 9 : return ctxt.warn ("use of attacker-controlled value %qE as divisor"
620 : : " without checking for zero",
621 : 9 : m_arg);
622 : : else
623 : 0 : return ctxt.warn ("use of attacker-controlled value as divisor"
624 : 0 : " without checking for zero");
625 : : }
626 : :
627 : 18 : label_text describe_final_event (const evdesc::final_event &ev) final override
628 : : {
629 : 18 : if (m_arg)
630 : 18 : return ev.formatted_print
631 : : ("use of attacker-controlled value %qE as divisor"
632 : : " without checking for zero",
633 : 18 : m_arg);
634 : : else
635 : 0 : return ev.formatted_print
636 : : ("use of attacker-controlled value as divisor"
637 : 0 : " without checking for zero");
638 : : }
639 : : };
640 : :
641 : : /* Concrete taint_diagnostic subclass for reporting attacker-controlled
642 : : size of a dynamic allocation. */
643 : :
644 : 0 : class tainted_allocation_size : public taint_diagnostic
645 : : {
646 : : public:
647 : 31 : tainted_allocation_size (const taint_state_machine &sm, tree arg,
648 : : enum bounds has_bounds, enum memory_space mem_space)
649 : 31 : : taint_diagnostic (sm, arg, has_bounds),
650 : 31 : m_mem_space (mem_space)
651 : : {
652 : : }
653 : :
654 : 180 : const char *get_kind () const final override
655 : : {
656 : 180 : return "tainted_allocation_size";
657 : : }
658 : :
659 : 46 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
660 : : {
661 : 46 : if (!taint_diagnostic::subclass_equal_p (base_other))
662 : : return false;
663 : 46 : const tainted_allocation_size &other
664 : : = (const tainted_allocation_size &)base_other;
665 : 46 : return m_mem_space == other.m_mem_space;
666 : : }
667 : :
668 : 54 : int get_controlling_option () const final override
669 : : {
670 : 54 : return OPT_Wanalyzer_tainted_allocation_size;
671 : : }
672 : :
673 : 23 : bool emit (diagnostic_emission_context &ctxt) final override
674 : : {
675 : : /* "CWE-789: Memory Allocation with Excessive Size Value". */
676 : 23 : ctxt.add_cwe (789);
677 : :
678 : 23 : bool warned;
679 : 23 : if (m_arg)
680 : 23 : switch (m_has_bounds)
681 : : {
682 : 0 : default:
683 : 0 : gcc_unreachable ();
684 : 0 : case BOUNDS_NONE:
685 : 0 : warned = ctxt.warn ("use of attacker-controlled value %qE as"
686 : : " allocation size without bounds checking",
687 : : m_arg);
688 : 0 : break;
689 : 0 : case BOUNDS_UPPER:
690 : 0 : warned = ctxt.warn ("use of attacker-controlled value %qE as"
691 : : " allocation size without"
692 : : " lower-bounds checking",
693 : : m_arg);
694 : 0 : break;
695 : 23 : case BOUNDS_LOWER:
696 : 23 : warned = ctxt.warn ("use of attacker-controlled value %qE as"
697 : : " allocation size without"
698 : : " upper-bounds checking",
699 : : m_arg);
700 : 23 : break;
701 : : }
702 : : else
703 : 0 : switch (m_has_bounds)
704 : : {
705 : 0 : default:
706 : 0 : gcc_unreachable ();
707 : 0 : case BOUNDS_NONE:
708 : 0 : warned = ctxt.warn ("use of attacker-controlled value as"
709 : : " allocation size without bounds"
710 : : " checking");
711 : 0 : break;
712 : 0 : case BOUNDS_UPPER:
713 : 0 : warned = ctxt.warn ("use of attacker-controlled value as"
714 : : " allocation size without"
715 : : " lower-bounds checking");
716 : 0 : break;
717 : 0 : case BOUNDS_LOWER:
718 : 0 : warned = ctxt.warn ("use of attacker-controlled value as"
719 : : " allocation size without"
720 : : " upper-bounds checking");
721 : 0 : break;
722 : : }
723 : 23 : if (warned)
724 : : {
725 : 23 : const location_t loc = ctxt.get_location ();
726 : 23 : switch (m_mem_space)
727 : : {
728 : : default:
729 : : break;
730 : 1 : case MEMSPACE_STACK:
731 : 1 : inform (loc, "stack-based allocation");
732 : 1 : break;
733 : 15 : case MEMSPACE_HEAP:
734 : 15 : inform (loc, "heap-based allocation");
735 : 15 : break;
736 : : }
737 : : }
738 : 23 : return warned;
739 : : }
740 : :
741 : 46 : label_text describe_final_event (const evdesc::final_event &ev) final override
742 : : {
743 : 46 : if (m_arg)
744 : 46 : switch (m_has_bounds)
745 : : {
746 : 0 : default:
747 : 0 : gcc_unreachable ();
748 : 0 : case BOUNDS_NONE:
749 : 0 : return ev.formatted_print
750 : : ("use of attacker-controlled value %qE as allocation size"
751 : : " without bounds checking",
752 : 0 : m_arg);
753 : 0 : case BOUNDS_UPPER:
754 : 0 : return ev.formatted_print
755 : : ("use of attacker-controlled value %qE as allocation size"
756 : : " without lower-bounds checking",
757 : 0 : m_arg);
758 : 46 : case BOUNDS_LOWER:
759 : 46 : return ev.formatted_print
760 : : ("use of attacker-controlled value %qE as allocation size"
761 : : " without upper-bounds checking",
762 : 46 : m_arg);
763 : : }
764 : : else
765 : 0 : switch (m_has_bounds)
766 : : {
767 : 0 : default:
768 : 0 : gcc_unreachable ();
769 : 0 : case BOUNDS_NONE:
770 : 0 : return ev.formatted_print
771 : : ("use of attacker-controlled value as allocation size"
772 : 0 : " without bounds checking");
773 : 0 : case BOUNDS_UPPER:
774 : 0 : return ev.formatted_print
775 : : ("use of attacker-controlled value as allocation size"
776 : 0 : " without lower-bounds checking");
777 : 0 : case BOUNDS_LOWER:
778 : 0 : return ev.formatted_print
779 : : ("use of attacker-controlled value as allocation size"
780 : 0 : " without upper-bounds checking");
781 : : }
782 : : }
783 : :
784 : : private:
785 : : enum memory_space m_mem_space;
786 : : };
787 : :
788 : : /* Concrete taint_diagnostic subclass for reporting attacker-controlled
789 : : value being used as part of the condition of an assertion. */
790 : :
791 : 0 : class tainted_assertion : public taint_diagnostic
792 : : {
793 : : public:
794 : 39 : tainted_assertion (const taint_state_machine &sm, tree arg,
795 : : tree assert_failure_fndecl)
796 : 39 : : taint_diagnostic (sm, arg, BOUNDS_NONE),
797 : 39 : m_assert_failure_fndecl (assert_failure_fndecl)
798 : : {
799 : 39 : gcc_assert (m_assert_failure_fndecl);
800 : 39 : }
801 : :
802 : 155 : const char *get_kind () const final override
803 : : {
804 : 155 : return "tainted_assertion";
805 : : }
806 : :
807 : 39 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
808 : : {
809 : 39 : if (!taint_diagnostic::subclass_equal_p (base_other))
810 : : return false;
811 : 39 : const tainted_assertion &other
812 : : = (const tainted_assertion &)base_other;
813 : 39 : return m_assert_failure_fndecl == other.m_assert_failure_fndecl;
814 : : }
815 : :
816 : 77 : int get_controlling_option () const final override
817 : : {
818 : 77 : return OPT_Wanalyzer_tainted_assertion;
819 : : }
820 : :
821 : 38 : bool emit (diagnostic_emission_context &ctxt) final override
822 : : {
823 : : /* "CWE-617: Reachable Assertion". */
824 : 38 : ctxt.add_cwe (617);
825 : :
826 : 38 : return ctxt.warn ("use of attacked-controlled value in"
827 : 38 : " condition for assertion");
828 : : }
829 : :
830 : 307 : location_t fixup_location (location_t loc,
831 : : bool primary) const final override
832 : : {
833 : 307 : if (primary)
834 : : /* For the primary location we want to avoid being in e.g. the
835 : : <assert.h> system header, since this would suppress the
836 : : diagnostic. */
837 : 77 : return expansion_point_location_if_in_system_header (loc);
838 : 230 : else if (in_system_header_at (loc))
839 : : /* For events, we want to show the implemenation of the assert
840 : : macro when we're describing them. */
841 : 4 : return linemap_resolve_location (line_table, loc,
842 : : LRK_SPELLING_LOCATION,
843 : 4 : NULL);
844 : : else
845 : 226 : return pending_diagnostic::fixup_location (loc, primary);
846 : : }
847 : :
848 : 76 : label_text describe_state_change (const evdesc::state_change &change) override
849 : : {
850 : 76 : if (change.m_new_state == m_sm.m_tainted_control_flow)
851 : 76 : return change.formatted_print
852 : 76 : ("use of attacker-controlled value for control flow");
853 : 0 : return taint_diagnostic::describe_state_change (change);
854 : : }
855 : :
856 : 76 : label_text describe_final_event (const evdesc::final_event &ev) final override
857 : : {
858 : 76 : if (mention_noreturn_attribute_p ())
859 : 66 : return ev.formatted_print
860 : : ("treating %qE as an assertion failure handler"
861 : : " due to %<__attribute__((__noreturn__))%>",
862 : 66 : m_assert_failure_fndecl);
863 : : else
864 : 10 : return ev.formatted_print
865 : : ("treating %qE as an assertion failure handler",
866 : 10 : m_assert_failure_fndecl);
867 : : }
868 : :
869 : : private:
870 : 76 : bool mention_noreturn_attribute_p () const
871 : : {
872 : 76 : if (fndecl_built_in_p (m_assert_failure_fndecl, BUILT_IN_UNREACHABLE))
873 : 10 : return false;
874 : : return true;
875 : : }
876 : :
877 : : tree m_assert_failure_fndecl;
878 : : };
879 : :
880 : : /* taint_state_machine's ctor. */
881 : :
882 : 3723 : taint_state_machine::taint_state_machine (logger *logger)
883 : : : state_machine ("taint", logger),
884 : 7446 : m_tainted (add_state ("tainted")),
885 : 3723 : m_has_lb (add_state ("has_lb")),
886 : 3723 : m_has_ub (add_state ("has_ub")),
887 : 3723 : m_stop (add_state ("stop")),
888 : 7446 : m_tainted_control_flow (add_state ("tainted-control-flow"))
889 : : {
890 : 3723 : }
891 : :
892 : : state_machine::state_t
893 : 1615258 : taint_state_machine::alt_get_inherited_state (const sm_state_map &map,
894 : : const svalue *sval,
895 : : const extrinsic_state &ext_state)
896 : : const
897 : : {
898 : 1615258 : switch (sval->get_kind ())
899 : : {
900 : : default:
901 : : break;
902 : 34994 : case SK_UNARYOP:
903 : 34994 : {
904 : 34994 : const unaryop_svalue *unaryop_sval
905 : 34994 : = as_a <const unaryop_svalue *> (sval);
906 : 34994 : enum tree_code op = unaryop_sval->get_op ();
907 : 34994 : const svalue *arg = unaryop_sval->get_arg ();
908 : 34994 : switch (op)
909 : : {
910 : 31617 : case NOP_EXPR:
911 : 31617 : {
912 : 31617 : state_t arg_state = map.get_state (arg, ext_state);
913 : 31617 : return arg_state;
914 : : }
915 : : default:
916 : : break;
917 : : }
918 : : }
919 : : break;
920 : 128469 : case SK_BINOP:
921 : 128469 : {
922 : 128469 : const binop_svalue *binop_sval = as_a <const binop_svalue *> (sval);
923 : 128469 : enum tree_code op = binop_sval->get_op ();
924 : 128469 : const svalue *arg0 = binop_sval->get_arg0 ();
925 : 128469 : const svalue *arg1 = binop_sval->get_arg1 ();
926 : 128469 : switch (op)
927 : : {
928 : : default:
929 : : break;
930 : :
931 : 119226 : case EQ_EXPR:
932 : 119226 : case GE_EXPR:
933 : 119226 : case LE_EXPR:
934 : 119226 : case NE_EXPR:
935 : 119226 : case GT_EXPR:
936 : 119226 : case LT_EXPR:
937 : 119226 : case UNORDERED_EXPR:
938 : 119226 : case ORDERED_EXPR:
939 : 119226 : case PLUS_EXPR:
940 : 119226 : case MINUS_EXPR:
941 : 119226 : case MULT_EXPR:
942 : 119226 : case POINTER_PLUS_EXPR:
943 : 119226 : case TRUNC_DIV_EXPR:
944 : 119226 : {
945 : 119226 : state_t arg0_state = map.get_state (arg0, ext_state);
946 : 119226 : state_t arg1_state = map.get_state (arg1, ext_state);
947 : 119226 : return combine_states (arg0_state, arg1_state);
948 : : }
949 : 608 : break;
950 : :
951 : 608 : case TRUNC_MOD_EXPR:
952 : 608 : {
953 : : /* The left-hand side of X % Y can be sanitized by
954 : : the operation. */
955 : 608 : return map.get_state (arg1, ext_state);
956 : : }
957 : : break;
958 : :
959 : : case BIT_AND_EXPR:
960 : : case RSHIFT_EXPR:
961 : : return NULL;
962 : : }
963 : : }
964 : : break;
965 : : }
966 : : return NULL;
967 : : }
968 : :
969 : : /* Return true iff FNDECL should be considered to be an assertion failure
970 : : handler by -Wanalyzer-tainted-assertion. */
971 : :
972 : : static bool
973 : 64738 : is_assertion_failure_handler_p (tree fndecl)
974 : : {
975 : : // i.e. "noreturn"
976 : 0 : if (TREE_THIS_VOLATILE (fndecl))
977 : 0 : return true;
978 : :
979 : : return false;
980 : : }
981 : :
982 : : /* Implementation of state_machine::on_stmt vfunc for taint_state_machine. */
983 : :
984 : : bool
985 : 318665 : taint_state_machine::on_stmt (sm_context *sm_ctxt,
986 : : const supernode *node,
987 : : const gimple *stmt) const
988 : : {
989 : 318665 : if (const gcall *call = dyn_cast <const gcall *> (stmt))
990 : 66431 : if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
991 : : {
992 : 64950 : if (is_named_call_p (callee_fndecl, "fread", call, 4))
993 : : {
994 : 212 : tree arg = gimple_call_arg (call, 0);
995 : :
996 : 212 : sm_ctxt->on_transition (node, stmt, arg, m_start, m_tainted);
997 : :
998 : : /* Dereference an ADDR_EXPR. */
999 : : // TODO: should the engine do this?
1000 : 212 : if (TREE_CODE (arg) == ADDR_EXPR)
1001 : 141 : sm_ctxt->on_transition (node, stmt, TREE_OPERAND (arg, 0),
1002 : 141 : m_start, m_tainted);
1003 : 212 : return true;
1004 : : }
1005 : :
1006 : : /* External function with "access" attribute. */
1007 : 64738 : if (sm_ctxt->unknown_side_effects_p ())
1008 : 14574 : check_for_tainted_size_arg (sm_ctxt, node, call, callee_fndecl);
1009 : :
1010 : 67859 : if (is_assertion_failure_handler_p (callee_fndecl)
1011 : 1640 : && sm_ctxt->get_global_state () == m_tainted_control_flow)
1012 : : {
1013 : 39 : sm_ctxt->warn (node, call, NULL_TREE,
1014 : 78 : make_unique<tainted_assertion> (*this, NULL_TREE,
1015 : : callee_fndecl));
1016 : : }
1017 : : }
1018 : : // TODO: ...etc; many other sources of untrusted data
1019 : :
1020 : 318453 : if (const gassign *assign = dyn_cast <const gassign *> (stmt))
1021 : : {
1022 : 184462 : enum tree_code op = gimple_assign_rhs_code (assign);
1023 : :
1024 : 184462 : switch (op)
1025 : : {
1026 : : default:
1027 : : break;
1028 : 382 : case TRUNC_DIV_EXPR:
1029 : 382 : case CEIL_DIV_EXPR:
1030 : 382 : case FLOOR_DIV_EXPR:
1031 : 382 : case ROUND_DIV_EXPR:
1032 : 382 : case TRUNC_MOD_EXPR:
1033 : 382 : case CEIL_MOD_EXPR:
1034 : 382 : case FLOOR_MOD_EXPR:
1035 : 382 : case ROUND_MOD_EXPR:
1036 : 382 : case RDIV_EXPR:
1037 : 382 : case EXACT_DIV_EXPR:
1038 : 382 : check_for_tainted_divisor (sm_ctxt, node, assign);
1039 : 382 : break;
1040 : : }
1041 : : }
1042 : :
1043 : 318453 : if (const gcond *cond = dyn_cast <const gcond *> (stmt))
1044 : : {
1045 : : /* Reset the state of "tainted-control-flow" before each
1046 : : control flow statement, so that only the last one before
1047 : : an assertion-failure-handler counts. */
1048 : 23293 : sm_ctxt->set_global_state (m_start);
1049 : 23293 : check_control_flow_arg_for_taint (sm_ctxt, cond, gimple_cond_lhs (cond));
1050 : 23293 : check_control_flow_arg_for_taint (sm_ctxt, cond, gimple_cond_rhs (cond));
1051 : : }
1052 : :
1053 : 318453 : if (const gswitch *switch_ = dyn_cast <const gswitch *> (stmt))
1054 : : {
1055 : : /* Reset the state of "tainted-control-flow" before each
1056 : : control flow statement, so that only the last one before
1057 : : an assertion-failure-handler counts. */
1058 : 996 : sm_ctxt->set_global_state (m_start);
1059 : 996 : check_control_flow_arg_for_taint (sm_ctxt, switch_,
1060 : : gimple_switch_index (switch_));
1061 : : }
1062 : :
1063 : : return false;
1064 : : }
1065 : :
1066 : : /* If EXPR is tainted, mark this execution path with the
1067 : : "tainted-control-flow" global state, in case we're about
1068 : : to call an assertion-failure-handler. */
1069 : :
1070 : : void
1071 : 47582 : taint_state_machine::check_control_flow_arg_for_taint (sm_context *sm_ctxt,
1072 : : const gimple *stmt,
1073 : : tree expr) const
1074 : : {
1075 : 47582 : const region_model *old_model = sm_ctxt->get_old_region_model ();
1076 : 47582 : const svalue *sval = old_model->get_rvalue (expr, NULL);
1077 : 47582 : state_t state = sm_ctxt->get_state (stmt, sval);
1078 : 47582 : enum bounds b;
1079 : 47582 : if (get_taint (state, TREE_TYPE (expr), &b))
1080 : 324 : sm_ctxt->set_global_state (m_tainted_control_flow);
1081 : 47582 : }
1082 : :
1083 : : /* Implementation of state_machine::on_condition vfunc for taint_state_machine.
1084 : : Potentially transition state 'tainted' to 'has_ub' or 'has_lb',
1085 : : and states 'has_ub' and 'has_lb' to 'stop'. */
1086 : :
1087 : : void
1088 : 35038 : taint_state_machine::on_condition (sm_context *sm_ctxt,
1089 : : const supernode *node,
1090 : : const gimple *stmt,
1091 : : const svalue *lhs,
1092 : : enum tree_code op,
1093 : : const svalue *rhs) const
1094 : : {
1095 : 35038 : if (stmt == NULL)
1096 : : return;
1097 : :
1098 : 35038 : if (lhs->get_kind () == SK_UNKNOWN
1099 : 35038 : || rhs->get_kind () == SK_UNKNOWN)
1100 : : {
1101 : : /* If we have a comparison against UNKNOWN, then
1102 : : we've presumably hit the svalue complexity limit,
1103 : : and we don't know what is being sanitized.
1104 : : Give up on any taint already found on this execution path. */
1105 : : // TODO: warn about this
1106 : 10355 : if (get_logger ())
1107 : 14 : get_logger ()->log ("comparison against UNKNOWN; removing all taint");
1108 : 10355 : sm_ctxt->clear_all_per_svalue_state ();
1109 : 10355 : return;
1110 : : }
1111 : :
1112 : : /* Strip away casts before considering LHS and RHS, to increase the
1113 : : chance of detecting places where sanitization of a value may have
1114 : : happened. */
1115 : 24683 : if (const svalue *inner = lhs->maybe_undo_cast ())
1116 : 877 : lhs = inner;
1117 : 24683 : if (const svalue *inner = rhs->maybe_undo_cast ())
1118 : 510 : rhs = inner;
1119 : :
1120 : : // TODO
1121 : 24683 : switch (op)
1122 : : {
1123 : : //case NE_EXPR:
1124 : : //case EQ_EXPR:
1125 : 3944 : case GE_EXPR:
1126 : 3944 : case GT_EXPR:
1127 : 3944 : {
1128 : : /* (LHS >= RHS) or (LHS > RHS)
1129 : : LHS gains a lower bound
1130 : : RHS gains an upper bound. */
1131 : 3944 : sm_ctxt->on_transition (node, stmt, lhs, m_tainted,
1132 : 3944 : m_has_lb);
1133 : 3944 : sm_ctxt->on_transition (node, stmt, lhs, m_has_ub,
1134 : 3944 : m_stop);
1135 : 3944 : sm_ctxt->on_transition (node, stmt, rhs, m_tainted,
1136 : 3944 : m_has_ub);
1137 : 3944 : sm_ctxt->on_transition (node, stmt, rhs, m_has_lb,
1138 : 3944 : m_stop);
1139 : : }
1140 : 3944 : break;
1141 : 3276 : case LE_EXPR:
1142 : 3276 : case LT_EXPR:
1143 : 3276 : {
1144 : : /* Detect where build_range_check has optimized
1145 : : (c>=low) && (c<=high)
1146 : : into
1147 : : (c-low>=0) && (c-low<=high-low)
1148 : : and thus into:
1149 : : (unsigned)(c - low) <= (unsigned)(high-low). */
1150 : 6552 : if (const binop_svalue *binop_sval
1151 : 3276 : = lhs->dyn_cast_binop_svalue ())
1152 : : {
1153 : 513 : const svalue *inner_lhs = binop_sval->get_arg0 ();
1154 : 513 : enum tree_code inner_op = binop_sval->get_op ();
1155 : 513 : const svalue *inner_rhs = binop_sval->get_arg1 ();
1156 : 513 : if (const svalue *before_cast = inner_lhs->maybe_undo_cast ())
1157 : 52 : inner_lhs = before_cast;
1158 : 513 : if (tree outer_rhs_cst = rhs->maybe_get_constant ())
1159 : 361 : if (tree inner_rhs_cst = inner_rhs->maybe_get_constant ())
1160 : 323 : if (inner_op == PLUS_EXPR
1161 : 313 : && TREE_CODE (inner_rhs_cst) == INTEGER_CST
1162 : 313 : && TREE_CODE (outer_rhs_cst) == INTEGER_CST
1163 : 313 : && TYPE_UNSIGNED (TREE_TYPE (inner_rhs_cst))
1164 : 404 : && TYPE_UNSIGNED (TREE_TYPE (outer_rhs_cst)))
1165 : : {
1166 : : /* We have
1167 : : (unsigned)(INNER_LHS + CST_A) </<= UNSIGNED_CST_B
1168 : : and thus an optimized test of INNER_LHS (before any
1169 : : cast to unsigned) against a range.
1170 : : Transition any of the tainted states to the stop state.
1171 : : We have to special-case this here rather than in
1172 : : region_model::on_condition since we can't apply
1173 : : both conditions simultaneously (we'd have a transition
1174 : : from the old state to has_lb, then a transition from
1175 : : the old state *again* to has_ub). */
1176 : 81 : state_t old_state
1177 : 81 : = sm_ctxt->get_state (stmt, inner_lhs);
1178 : 81 : if (old_state == m_tainted
1179 : 64 : || old_state == m_has_lb
1180 : 64 : || old_state == m_has_ub)
1181 : 17 : sm_ctxt->set_next_state (stmt, inner_lhs, m_stop);
1182 : 81 : return;
1183 : : }
1184 : : }
1185 : :
1186 : : /* (LHS <= RHS) or (LHS < RHS)
1187 : : LHS gains an upper bound
1188 : : RHS gains a lower bound. */
1189 : 3195 : sm_ctxt->on_transition (node, stmt, lhs, m_tainted,
1190 : 3195 : m_has_ub);
1191 : 3195 : sm_ctxt->on_transition (node, stmt, lhs, m_has_lb,
1192 : 3195 : m_stop);
1193 : 3195 : sm_ctxt->on_transition (node, stmt, rhs, m_tainted,
1194 : 3195 : m_has_lb);
1195 : 3195 : sm_ctxt->on_transition (node, stmt, rhs, m_has_ub,
1196 : 3195 : m_stop);
1197 : : }
1198 : 3195 : break;
1199 : : default:
1200 : : break;
1201 : : }
1202 : : }
1203 : :
1204 : : /* Implementation of state_machine::on_bounded_ranges vfunc for
1205 : : taint_state_machine, for handling switch statement cases.
1206 : : Potentially transition state 'tainted' to 'has_ub' or 'has_lb',
1207 : : and states 'has_ub' and 'has_lb' to 'stop'. */
1208 : :
1209 : : void
1210 : 7673 : taint_state_machine::on_bounded_ranges (sm_context *sm_ctxt,
1211 : : const supernode *,
1212 : : const gimple *stmt,
1213 : : const svalue &sval,
1214 : : const bounded_ranges &ranges) const
1215 : : {
1216 : 7673 : gcc_assert (!ranges.empty_p ());
1217 : 7673 : gcc_assert (ranges.get_count () > 0);
1218 : :
1219 : : /* We have one or more ranges; this could be a "default:", or one or
1220 : : more single or range cases.
1221 : :
1222 : : Look at the overall endpoints to see if the ranges impose any lower
1223 : : bounds or upper bounds beyond those of the underlying numeric type. */
1224 : :
1225 : 7673 : tree lowest_bound = ranges.get_range (0).m_lower;
1226 : 7673 : tree highest_bound = ranges.get_range (ranges.get_count () - 1).m_upper;
1227 : 7673 : gcc_assert (lowest_bound);
1228 : 7673 : gcc_assert (highest_bound);
1229 : :
1230 : 7673 : bool ranges_have_lb
1231 : 7673 : = (lowest_bound != TYPE_MIN_VALUE (TREE_TYPE (lowest_bound)));
1232 : 7673 : bool ranges_have_ub
1233 : 7673 : = (highest_bound != TYPE_MAX_VALUE (TREE_TYPE (highest_bound)));
1234 : :
1235 : 7673 : if (!ranges_have_lb && !ranges_have_ub)
1236 : : return;
1237 : :
1238 : : /* We have new bounds from the ranges; combine them with any
1239 : : existing bounds on SVAL. */
1240 : 6951 : state_t old_state = sm_ctxt->get_state (stmt, &sval);
1241 : 6951 : if (old_state == m_tainted)
1242 : : {
1243 : 149 : if (ranges_have_lb && ranges_have_ub)
1244 : 145 : sm_ctxt->set_next_state (stmt, &sval, m_stop);
1245 : 4 : else if (ranges_have_lb)
1246 : 4 : sm_ctxt->set_next_state (stmt, &sval, m_has_lb);
1247 : 0 : else if (ranges_have_ub)
1248 : 0 : sm_ctxt->set_next_state (stmt, &sval, m_has_ub);
1249 : : }
1250 : 6802 : else if (old_state == m_has_ub && ranges_have_lb)
1251 : 0 : sm_ctxt->set_next_state (stmt, &sval, m_stop);
1252 : 6802 : else if (old_state == m_has_lb && ranges_have_ub)
1253 : 0 : sm_ctxt->set_next_state (stmt, &sval, m_stop);
1254 : : }
1255 : :
1256 : : bool
1257 : 1206994 : taint_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
1258 : : {
1259 : 1206994 : if (s == m_has_lb || s == m_has_ub)
1260 : 976 : return false;
1261 : :
1262 : : return true;
1263 : : }
1264 : :
1265 : : /* If STATE is a tainted state, write the bounds to *OUT and return true.
1266 : : Otherwise return false.
1267 : : Use the signedness of TYPE to determine if "has_ub" is tainted. */
1268 : :
1269 : : bool
1270 : 86059 : taint_state_machine::get_taint (state_t state, tree type,
1271 : : enum bounds *out) const
1272 : : {
1273 : : /* Unsigned types have an implicit lower bound. */
1274 : 86059 : bool is_unsigned = false;
1275 : 86059 : if (type)
1276 : 86059 : if (INTEGRAL_TYPE_P (type))
1277 : 70030 : is_unsigned = TYPE_UNSIGNED (type);
1278 : :
1279 : : /* Can't use a switch as the states are non-const. */
1280 : 86059 : if (state == m_tainted)
1281 : : {
1282 : 536 : *out = is_unsigned ? BOUNDS_LOWER : BOUNDS_NONE;
1283 : 536 : return true;
1284 : : }
1285 : 85523 : else if (state == m_has_lb)
1286 : : {
1287 : 43 : *out = BOUNDS_LOWER;
1288 : 43 : return true;
1289 : : }
1290 : 85480 : else if (state == m_has_ub && !is_unsigned)
1291 : : {
1292 : : /* Missing lower bound. */
1293 : 18 : *out = BOUNDS_UPPER;
1294 : 18 : return true;
1295 : : }
1296 : : return false;
1297 : : }
1298 : :
1299 : : /* Find the most tainted state of S0 and S1. */
1300 : :
1301 : : state_machine::state_t
1302 : 119226 : taint_state_machine::combine_states (state_t s0, state_t s1) const
1303 : : {
1304 : 119226 : gcc_assert (s0);
1305 : 119226 : gcc_assert (s1);
1306 : 119226 : if (s0 == s1)
1307 : : return s0;
1308 : 775 : if (s0 == m_tainted || s1 == m_tainted)
1309 : : return m_tainted;
1310 : 328 : if (s0 == m_start)
1311 : : return s1;
1312 : 304 : if (s1 == m_start)
1313 : : return s0;
1314 : 60 : if (s0 == m_stop)
1315 : : return s1;
1316 : 40 : if (s1 == m_stop)
1317 : : return s0;
1318 : : /* The only remaining combinations are one of has_ub and has_lb
1319 : : (in either order). */
1320 : 20 : gcc_assert ((s0 == m_has_lb && s1 == m_has_ub)
1321 : : || (s0 == m_has_ub && s1 == m_has_lb));
1322 : : return m_tainted;
1323 : : }
1324 : :
1325 : : /* Check for calls to external functions marked with
1326 : : __attribute__((access)) with a size-index: complain about
1327 : : tainted values passed as a size to such a function. */
1328 : :
1329 : : void
1330 : 14574 : taint_state_machine::check_for_tainted_size_arg (sm_context *sm_ctxt,
1331 : : const supernode *node,
1332 : : const gcall *call,
1333 : : tree callee_fndecl) const
1334 : : {
1335 : 14574 : tree fntype = TREE_TYPE (callee_fndecl);
1336 : 14574 : if (!fntype)
1337 : 13058 : return;
1338 : :
1339 : 14574 : if (!TYPE_ATTRIBUTES (fntype))
1340 : : return;
1341 : :
1342 : : /* Initialize a map of attribute access specifications for arguments
1343 : : to the function call. */
1344 : 1516 : rdwr_map rdwr_idx;
1345 : 1516 : init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype));
1346 : :
1347 : 1516 : unsigned argno = 0;
1348 : :
1349 : 5675 : for (tree iter = TYPE_ARG_TYPES (fntype); iter;
1350 : 4159 : iter = TREE_CHAIN (iter), ++argno)
1351 : : {
1352 : 4159 : const attr_access* access = rdwr_idx.get (argno);
1353 : 4159 : if (!access)
1354 : 4013 : continue;
1355 : :
1356 : : /* Ignore any duplicate entry in the map for the size argument. */
1357 : 343 : if (access->ptrarg != argno)
1358 : 142 : continue;
1359 : :
1360 : 201 : if (access->sizarg == UINT_MAX)
1361 : 55 : continue;
1362 : :
1363 : 146 : tree size_arg = gimple_call_arg (call, access->sizarg);
1364 : :
1365 : 146 : state_t state = sm_ctxt->get_state (call, size_arg);
1366 : 146 : enum bounds b;
1367 : 146 : if (get_taint (state, TREE_TYPE (size_arg), &b))
1368 : : {
1369 : 5 : const char* const access_str =
1370 : 5 : TREE_STRING_POINTER (access->to_external_string ());
1371 : 5 : tree diag_size = sm_ctxt->get_diagnostic_tree (size_arg);
1372 : 5 : sm_ctxt->warn (node, call, size_arg,
1373 : : make_unique<tainted_access_attrib_size>
1374 : 10 : (*this, diag_size, b,
1375 : : callee_fndecl,
1376 : 5 : access->sizarg,
1377 : : access_str));
1378 : : }
1379 : : }
1380 : 1516 : }
1381 : :
1382 : : /* Complain if ASSIGN (a division operation) has a tainted divisor
1383 : : that could be zero. */
1384 : :
1385 : : void
1386 : 382 : taint_state_machine::check_for_tainted_divisor (sm_context *sm_ctxt,
1387 : : const supernode *node,
1388 : : const gassign *assign) const
1389 : : {
1390 : 382 : const region_model *old_model = sm_ctxt->get_old_region_model ();
1391 : 382 : if (!old_model)
1392 : 80 : return;
1393 : :
1394 : 382 : tree divisor_expr = gimple_assign_rhs2 (assign);;
1395 : :
1396 : : /* Until we track conditions on floating point values, we can't check to
1397 : : see if they've been checked against zero. */
1398 : 382 : if (!INTEGRAL_TYPE_P (TREE_TYPE (divisor_expr)))
1399 : : return;
1400 : :
1401 : 310 : const svalue *divisor_sval = old_model->get_rvalue (divisor_expr, NULL);
1402 : :
1403 : 310 : state_t state = sm_ctxt->get_state (assign, divisor_sval);
1404 : 310 : enum bounds b;
1405 : 310 : if (get_taint (state, TREE_TYPE (divisor_expr), &b))
1406 : : {
1407 : 35 : const svalue *zero_sval
1408 : : = old_model->get_manager ()->get_or_create_int_cst
1409 : 35 : (TREE_TYPE (divisor_expr), 0);
1410 : 35 : tristate ts
1411 : 35 : = old_model->eval_condition (divisor_sval, NE_EXPR, zero_sval);
1412 : 35 : if (ts.is_true ())
1413 : : /* The divisor is known to not equal 0: don't warn. */
1414 : 8 : return;
1415 : :
1416 : 27 : tree diag_divisor = sm_ctxt->get_diagnostic_tree (divisor_expr);
1417 : 27 : sm_ctxt->warn (node, assign, divisor_expr,
1418 : 54 : make_unique <tainted_divisor> (*this, diag_divisor, b));
1419 : 27 : sm_ctxt->set_next_state (assign, divisor_sval, m_stop);
1420 : : }
1421 : : }
1422 : :
1423 : : } // anonymous namespace
1424 : :
1425 : : /* Internal interface to this file. */
1426 : :
1427 : : state_machine *
1428 : 3723 : make_taint_state_machine (logger *logger)
1429 : : {
1430 : 3723 : return new taint_state_machine (logger);
1431 : : }
1432 : :
1433 : : /* A closed concrete range. */
1434 : :
1435 : : class concrete_range
1436 : : {
1437 : : public:
1438 : : /* Return true iff THIS is fully within OTHER
1439 : : i.e.
1440 : : - m_min must be >= OTHER.m_min
1441 : : - m_max must be <= OTHER.m_max. */
1442 : 154 : bool within_p (const concrete_range &other) const
1443 : : {
1444 : 154 : if (compare_constants (m_min, GE_EXPR, other.m_min).is_true ())
1445 : 59 : if (compare_constants (m_max, LE_EXPR, other.m_max).is_true ())
1446 : : return true;
1447 : : return false;
1448 : : }
1449 : :
1450 : : tree m_min;
1451 : : tree m_max;
1452 : : };
1453 : :
1454 : : /* Attempt to get a closed concrete range for SVAL based on types.
1455 : : If found, write to *OUT and return true.
1456 : : Otherwise return false. */
1457 : :
1458 : : static bool
1459 : 246 : get_possible_range (const svalue *sval, concrete_range *out)
1460 : : {
1461 : 246 : if (const svalue *inner = sval->maybe_undo_cast ())
1462 : : {
1463 : 92 : concrete_range inner_range;
1464 : 92 : if (!get_possible_range (inner, &inner_range))
1465 : 40 : return false;
1466 : :
1467 : 92 : if (sval->get_type ()
1468 : 92 : && inner->get_type ()
1469 : 92 : && INTEGRAL_TYPE_P (sval->get_type ())
1470 : 92 : && INTEGRAL_TYPE_P (inner->get_type ())
1471 : 92 : && TYPE_UNSIGNED (inner->get_type ())
1472 : 144 : && (TYPE_PRECISION (sval->get_type ())
1473 : 52 : > TYPE_PRECISION (inner->get_type ())))
1474 : : {
1475 : : /* We have a cast from an unsigned type to a wider integral type.
1476 : : Assuming this is zero-extension, we can inherit the range from
1477 : : the inner type. */
1478 : 40 : enum tree_code op = ((const unaryop_svalue *)sval)->get_op ();
1479 : 40 : out->m_min = fold_unary (op, sval->get_type (), inner_range.m_min);
1480 : 40 : out->m_max = fold_unary (op, sval->get_type (), inner_range.m_max);
1481 : 40 : return true;
1482 : : }
1483 : : }
1484 : :
1485 : 206 : if (sval->get_type ()
1486 : 206 : && INTEGRAL_TYPE_P (sval->get_type ()))
1487 : : {
1488 : 206 : out->m_min = TYPE_MIN_VALUE (sval->get_type ());
1489 : 206 : out->m_max = TYPE_MAX_VALUE (sval->get_type ());
1490 : 206 : return true;
1491 : : }
1492 : :
1493 : : return false;
1494 : : }
1495 : :
1496 : : /* Determine if it's possible for tainted array access ELEMENT_REG to
1497 : : actually be a problem.
1498 : :
1499 : : Check here for index being from e.g. unsigned char when the array
1500 : : contains >= 255 elements.
1501 : :
1502 : : Return true if out-of-bounds is possible, false if it's impossible
1503 : : (for suppressing false positives). */
1504 : :
1505 : : static bool
1506 : 154 : index_can_be_out_of_bounds_p (const element_region *element_reg)
1507 : : {
1508 : 154 : const svalue *index = element_reg->get_index ();
1509 : 154 : const region *array_reg = element_reg->get_parent_region ();
1510 : :
1511 : 154 : if (array_reg->get_type ()
1512 : 154 : && TREE_CODE (array_reg->get_type ()) == ARRAY_TYPE
1513 : 154 : && TYPE_DOMAIN (array_reg->get_type ())
1514 : 308 : && INTEGRAL_TYPE_P (TYPE_DOMAIN (array_reg->get_type ())))
1515 : : {
1516 : 154 : concrete_range valid_index_range;
1517 : 154 : valid_index_range.m_min
1518 : 154 : = TYPE_MIN_VALUE (TYPE_DOMAIN (array_reg->get_type ()));
1519 : 154 : valid_index_range.m_max
1520 : 154 : = TYPE_MAX_VALUE (TYPE_DOMAIN (array_reg->get_type ()));
1521 : :
1522 : 154 : concrete_range possible_index_range;
1523 : 154 : if (get_possible_range (index, &possible_index_range))
1524 : 154 : if (possible_index_range.within_p (valid_index_range))
1525 : 20 : return false;
1526 : : }
1527 : :
1528 : : return true;
1529 : : }
1530 : :
1531 : : /* Complain to CTXT if accessing REG leads could lead to arbitrary
1532 : : memory access under an attacker's control (due to taint). */
1533 : :
1534 : : void
1535 : 806651 : region_model::check_region_for_taint (const region *reg,
1536 : : enum access_direction,
1537 : : region_model_context *ctxt) const
1538 : : {
1539 : 806651 : gcc_assert (reg);
1540 : 806651 : gcc_assert (ctxt);
1541 : :
1542 : 806651 : LOG_SCOPE (ctxt->get_logger ());
1543 : :
1544 : 806651 : sm_state_map *smap;
1545 : 806651 : const state_machine *sm;
1546 : 806651 : unsigned sm_idx;
1547 : 806651 : if (!ctxt->get_taint_map (&smap, &sm, &sm_idx))
1548 : : return;
1549 : :
1550 : 775164 : gcc_assert (smap);
1551 : 775164 : gcc_assert (sm);
1552 : :
1553 : 775164 : const taint_state_machine &taint_sm = (const taint_state_machine &)*sm;
1554 : :
1555 : 775164 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
1556 : 775164 : if (!ext_state)
1557 : : return;
1558 : :
1559 : : const region *iter_region = reg;
1560 : 3799922 : while (iter_region)
1561 : : {
1562 : 3024758 : switch (iter_region->get_kind ())
1563 : : {
1564 : : default:
1565 : : break;
1566 : :
1567 : 10050 : case RK_ELEMENT:
1568 : 10050 : {
1569 : 10050 : const element_region *element_reg
1570 : : = (const element_region *)iter_region;
1571 : 10050 : const svalue *index = element_reg->get_index ();
1572 : 10050 : const state_machine::state_t
1573 : 10050 : state = smap->get_state (index, *ext_state);
1574 : 10050 : gcc_assert (state);
1575 : 10050 : enum bounds b;
1576 : 10050 : if (taint_sm.get_taint (state, index->get_type (), &b))
1577 : : {
1578 : 154 : if (index_can_be_out_of_bounds_p (element_reg))
1579 : : {
1580 : 134 : tree arg = get_representative_tree (index);
1581 : 134 : ctxt->warn (make_unique<tainted_array_index> (taint_sm,
1582 : : arg, b));
1583 : : }
1584 : 20 : else if (ctxt->get_logger ())
1585 : 0 : ctxt->get_logger ()->log ("rejecting tainted_array_index as"
1586 : : " out of bounds is not possible");
1587 : : }
1588 : : }
1589 : 10050 : break;
1590 : :
1591 : 13050 : case RK_OFFSET:
1592 : 13050 : {
1593 : 13050 : const offset_region *offset_reg
1594 : : = (const offset_region *)iter_region;
1595 : 13050 : const svalue *offset = offset_reg->get_byte_offset ();
1596 : 13050 : const state_machine::state_t
1597 : 13050 : state = smap->get_state (offset, *ext_state);
1598 : 13050 : gcc_assert (state);
1599 : : /* Handle implicit cast to sizetype. */
1600 : 13050 : tree effective_type = offset->get_type ();
1601 : 13050 : if (const svalue *cast = offset->maybe_undo_cast ())
1602 : 287 : if (cast->get_type ())
1603 : 13050 : effective_type = cast->get_type ();
1604 : 13050 : enum bounds b;
1605 : 13050 : if (taint_sm.get_taint (state, effective_type, &b))
1606 : : {
1607 : 45 : tree arg = get_representative_tree (offset);
1608 : 45 : ctxt->warn (make_unique<tainted_offset> (taint_sm, arg, b,
1609 : : offset));
1610 : : }
1611 : : }
1612 : 13050 : break;
1613 : :
1614 : 10900 : case RK_CAST:
1615 : 10900 : {
1616 : 10900 : const cast_region *cast_reg
1617 : 10900 : = as_a <const cast_region *> (iter_region);
1618 : 10900 : iter_region = cast_reg->get_original_region ();
1619 : 10900 : continue;
1620 : 10900 : }
1621 : :
1622 : 4624 : case RK_SIZED:
1623 : 4624 : {
1624 : 4624 : const sized_region *sized_reg
1625 : : = (const sized_region *)iter_region;
1626 : 4624 : const svalue *size_sval = sized_reg->get_byte_size_sval (m_mgr);
1627 : 4624 : const state_machine::state_t
1628 : 4624 : state = smap->get_state (size_sval, *ext_state);
1629 : 4624 : gcc_assert (state);
1630 : 4624 : enum bounds b;
1631 : 4624 : if (taint_sm.get_taint (state, size_sval->get_type (), &b))
1632 : : {
1633 : 3 : tree arg = get_representative_tree (size_sval);
1634 : 3 : ctxt->warn (make_unique<tainted_size> (taint_sm, arg, b));
1635 : : }
1636 : : }
1637 : 4624 : break;
1638 : : }
1639 : :
1640 : 3013858 : iter_region = iter_region->get_parent_region ();
1641 : : }
1642 : 806651 : }
1643 : :
1644 : : /* Complain to CTXT about a tainted allocation size if SIZE_IN_BYTES is
1645 : : under an attacker's control (due to taint), where the allocation
1646 : : is happening within MEM_SPACE. */
1647 : :
1648 : : void
1649 : 10336 : region_model::check_dynamic_size_for_taint (enum memory_space mem_space,
1650 : : const svalue *size_in_bytes,
1651 : : region_model_context *ctxt) const
1652 : : {
1653 : 10336 : gcc_assert (size_in_bytes);
1654 : 10336 : gcc_assert (ctxt);
1655 : :
1656 : 10336 : LOG_SCOPE (ctxt->get_logger ());
1657 : :
1658 : 10336 : sm_state_map *smap;
1659 : 10336 : const state_machine *sm;
1660 : 10336 : unsigned sm_idx;
1661 : 10336 : if (!ctxt->get_taint_map (&smap, &sm, &sm_idx))
1662 : : return;
1663 : :
1664 : 10297 : gcc_assert (smap);
1665 : 10297 : gcc_assert (sm);
1666 : :
1667 : 10297 : const taint_state_machine &taint_sm = (const taint_state_machine &)*sm;
1668 : :
1669 : 10297 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
1670 : 10297 : if (!ext_state)
1671 : : return;
1672 : :
1673 : 10297 : const state_machine::state_t
1674 : 10297 : state = smap->get_state (size_in_bytes, *ext_state);
1675 : 10297 : gcc_assert (state);
1676 : 10297 : enum bounds b;
1677 : 10297 : if (taint_sm.get_taint (state, size_in_bytes->get_type (), &b))
1678 : : {
1679 : 31 : tree arg = get_representative_tree (size_in_bytes);
1680 : 31 : ctxt->warn (make_unique<tainted_allocation_size>
1681 : 62 : (taint_sm, arg, b, mem_space));
1682 : : }
1683 : 10336 : }
1684 : :
1685 : : /* Mark SVAL as TAINTED. CTXT must be non-NULL. */
1686 : :
1687 : : void
1688 : 47 : region_model::mark_as_tainted (const svalue *sval,
1689 : : region_model_context *ctxt)
1690 : : {
1691 : 47 : gcc_assert (sval);
1692 : 47 : gcc_assert (ctxt);
1693 : :
1694 : 47 : sm_state_map *smap;
1695 : 47 : const state_machine *sm;
1696 : 47 : unsigned sm_idx;
1697 : 47 : if (!ctxt->get_taint_map (&smap, &sm, &sm_idx))
1698 : 0 : return;
1699 : :
1700 : 47 : gcc_assert (smap);
1701 : 47 : gcc_assert (sm);
1702 : :
1703 : 47 : const taint_state_machine &taint_sm = (const taint_state_machine &)*sm;
1704 : :
1705 : 47 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
1706 : 47 : if (!ext_state)
1707 : : return;
1708 : :
1709 : 47 : smap->set_state (this, sval, taint_sm.m_tainted, NULL, *ext_state);
1710 : : }
1711 : :
1712 : : /* Return true if SVAL could possibly be attacker-controlled. */
1713 : :
1714 : : bool
1715 : 36 : region_model_context::possibly_tainted_p (const svalue *sval)
1716 : : {
1717 : 36 : sm_state_map *smap;
1718 : 36 : const state_machine *sm;
1719 : 36 : unsigned sm_idx;
1720 : 36 : if (!get_taint_map (&smap, &sm, &sm_idx))
1721 : : return false;
1722 : :
1723 : 36 : const taint_state_machine &taint_sm = (const taint_state_machine &)*sm;
1724 : :
1725 : 36 : const extrinsic_state *ext_state = get_ext_state ();
1726 : 36 : if (!ext_state)
1727 : : return false;
1728 : :
1729 : 36 : const state_machine::state_t state = smap->get_state (sval, *ext_state);
1730 : 36 : gcc_assert (state);
1731 : :
1732 : 36 : return (state == taint_sm.m_tainted
1733 : 34 : || state == taint_sm.m_has_lb
1734 : 70 : || state == taint_sm.m_has_ub);
1735 : : }
1736 : :
1737 : : } // namespace ana
1738 : :
1739 : : #endif /* #if ENABLE_ANALYZER */
|