Line data Source code
1 : /* Implementation of class ana::poisoned_value_diagnostic.
2 : Copyright (C) 2019-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "analyzer/common.h"
22 :
23 : #include "analyzer/region-model.h"
24 : #include "analyzer/feasible-graph.h"
25 : #include "diagnostics/sarif-sink.h"
26 :
27 : #if ENABLE_ANALYZER
28 :
29 : namespace ana {
30 :
31 : /* A subclass of pending_diagnostic for complaining about uses of
32 : poisoned values. */
33 :
34 : class poisoned_value_diagnostic
35 : : public pending_diagnostic_subclass<poisoned_value_diagnostic>
36 : {
37 : public:
38 2261 : poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
39 : const region *src_region,
40 : tree check_expr)
41 2261 : : m_expr (expr), m_pkind (pkind),
42 2261 : m_src_region (src_region),
43 2261 : m_check_expr (check_expr)
44 : {}
45 :
46 14459 : const char *get_kind () const final override { return "poisoned_value_diagnostic"; }
47 :
48 73 : bool use_of_uninit_p () const final override
49 : {
50 73 : return m_pkind == poison_kind::uninit;
51 : }
52 :
53 1368 : bool operator== (const poisoned_value_diagnostic &other) const
54 : {
55 1368 : return (m_expr == other.m_expr
56 1343 : && m_pkind == other.m_pkind
57 2711 : && m_src_region == other.m_src_region);
58 : }
59 :
60 1966 : int get_controlling_option () const final override
61 : {
62 1966 : switch (m_pkind)
63 : {
64 0 : default:
65 0 : gcc_unreachable ();
66 : case poison_kind::uninit:
67 : return OPT_Wanalyzer_use_of_uninitialized_value;
68 : case poison_kind::freed:
69 : case poison_kind::deleted:
70 : return OPT_Wanalyzer_use_after_free;
71 : case poison_kind::popped_stack:
72 : return OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame;
73 : }
74 : }
75 :
76 1389 : bool terminate_path_p () const final override { return true; }
77 :
78 577 : bool emit (diagnostic_emission_context &ctxt) final override
79 : {
80 577 : switch (m_pkind)
81 : {
82 0 : default:
83 0 : gcc_unreachable ();
84 550 : case poison_kind::uninit:
85 550 : {
86 550 : ctxt.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable". */
87 550 : return ctxt.warn ("use of uninitialized value %qE",
88 550 : m_expr);
89 : }
90 3 : break;
91 3 : case poison_kind::freed:
92 3 : {
93 3 : ctxt.add_cwe (416); /* "CWE-416: Use After Free". */
94 3 : return ctxt.warn ("use after %<free%> of %qE",
95 3 : m_expr);
96 : }
97 9 : break;
98 9 : case poison_kind::deleted:
99 9 : {
100 9 : ctxt.add_cwe (416); /* "CWE-416: Use After Free". */
101 9 : return ctxt.warn ("use after %<delete%> of %qE",
102 9 : m_expr);
103 : }
104 15 : break;
105 15 : case poison_kind::popped_stack:
106 15 : {
107 : /* TODO: which CWE? */
108 15 : return ctxt.warn
109 15 : ("dereferencing pointer %qE to within stale stack frame",
110 15 : m_expr);
111 : }
112 : break;
113 : }
114 : }
115 :
116 : bool
117 1154 : describe_final_event (pretty_printer &pp,
118 : const evdesc::final_event &) final override
119 : {
120 1154 : switch (m_pkind)
121 : {
122 0 : default:
123 0 : gcc_unreachable ();
124 1100 : case poison_kind::uninit:
125 1100 : {
126 1100 : pp_printf (&pp,
127 : "use of uninitialized value %qE here",
128 : m_expr);
129 1100 : return true;
130 : }
131 6 : case poison_kind::freed:
132 6 : {
133 6 : pp_printf (&pp,
134 : "use after %<free%> of %qE here",
135 : m_expr);
136 6 : return true;
137 : }
138 18 : case poison_kind::deleted:
139 18 : {
140 18 : pp_printf (&pp,
141 : "use after %<delete%> of %qE here",
142 : m_expr);
143 18 : return true;
144 : }
145 30 : case poison_kind::popped_stack:
146 30 : {
147 30 : pp_printf (&pp,
148 : "dereferencing pointer %qE to within stale stack frame",
149 : m_expr);
150 30 : return true;
151 : }
152 : }
153 : }
154 :
155 1154 : void mark_interesting_stuff (interesting_t *interest) final override
156 : {
157 1154 : if (m_src_region)
158 : {
159 1084 : interest->add_region_creation (m_src_region);
160 1084 : interest->add_read_region (m_src_region, "poisoned value");
161 : }
162 1154 : }
163 :
164 : /* Attempt to suppress false positives.
165 : Reject paths where the value of the underlying region isn't poisoned.
166 : This can happen due to state merging when exploring the exploded graph,
167 : where the more precise analysis during feasibility analysis finds that
168 : the region is in fact valid.
169 : To do this we need to get the value from the fgraph. Unfortunately
170 : we can't simply query the state of m_src_region (from the enode),
171 : since it might be a different region in the fnode state (e.g. with
172 : heap-allocated regions, the numbering could be different).
173 : Hence we access m_check_expr, if available. */
174 :
175 1325 : bool check_valid_fpath_p (const feasible_node &fnode)
176 : const final override
177 : {
178 1325 : if (!m_check_expr)
179 : return true;
180 1217 : const svalue *fsval = fnode.get_model ().get_rvalue (m_check_expr, nullptr);
181 : /* Check to see if the expr is also poisoned in FNODE (and in the
182 : same way). */
183 1217 : const poisoned_svalue * fspval = fsval->dyn_cast_poisoned_svalue ();
184 1217 : if (!fspval)
185 : return false;
186 1217 : if (fspval->get_poison_kind () != m_pkind)
187 : return false;
188 : return true;
189 : }
190 :
191 : void
192 8 : maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
193 : const final override
194 : {
195 8 : auto &props = result_obj.get_or_create_properties ();
196 : #define PROPERTY_PREFIX "gcc/analyzer/poisoned_value_diagnostic/"
197 8 : props.set (PROPERTY_PREFIX "expr", tree_to_json (m_expr));
198 8 : props.set_string (PROPERTY_PREFIX "kind", poison_kind_to_str (m_pkind));
199 8 : if (m_src_region)
200 8 : props.set (PROPERTY_PREFIX "src_region", m_src_region->to_json ());
201 8 : props.set (PROPERTY_PREFIX "check_expr", tree_to_json (m_check_expr));
202 : #undef PROPERTY_PREFIX
203 8 : }
204 :
205 : private:
206 : tree m_expr;
207 : enum poison_kind m_pkind;
208 : const region *m_src_region;
209 : tree m_check_expr;
210 : };
211 :
212 : std::unique_ptr<pending_diagnostic>
213 2261 : make_poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
214 : const region *src_region,
215 : tree check_expr)
216 : {
217 2261 : return std::make_unique<poisoned_value_diagnostic> (expr, pkind,
218 2261 : src_region, check_expr);
219 : }
220 :
221 : } // namespace ana
222 :
223 : #endif /* #if ENABLE_ANALYZER */
|