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