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