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