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