Branch data Line data Source code
1 : : /* Handling for the known behavior of various functions specific to C++.
2 : : Copyright (C) 2020-2025 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 "diagnostic.h"
24 : :
25 : : #include "analyzer/analyzer-logging.h"
26 : : #include "analyzer/region-model.h"
27 : : #include "analyzer/call-details.h"
28 : :
29 : : #if ENABLE_ANALYZER
30 : :
31 : : /* Return true if CALL is a non-allocating operator new or operator new []
32 : : that contains no user-defined args, i.e. having any signature of:
33 : :
34 : : - void* operator new (std::size_t count, void* ptr);
35 : : - void* operator new[] (std::size_t count, void* ptr);
36 : :
37 : : See https://en.cppreference.com/w/cpp/memory/new/operator_new. */
38 : :
39 : 50171 : bool is_placement_new_p (const gcall &call)
40 : : {
41 : 50171 : tree fndecl = gimple_call_fndecl (&call);
42 : :
43 : 50171 : if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
44 : : /* Give up on overloaded operator new. */
45 : : return false;
46 : :
47 : 48759 : if (!is_named_call_p (fndecl, "operator new", call, 2)
48 : 48759 : && !is_named_call_p (fndecl, "operator new []", call, 2))
49 : : return false;
50 : :
51 : : /* We must distinguish between an allocating non-throwing new
52 : : and a non-allocating new.
53 : :
54 : : The former might have one of the following signatures :
55 : : void* operator new (std::size_t count, const std::nothrow_t& tag);
56 : : void* operator new[] (std::size_t count, const std::nothrow_t& tag);
57 : : Whereas a placement new would take a pointer. */
58 : 708 : tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
59 : 708 : return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
60 : : }
61 : :
62 : : namespace ana {
63 : :
64 : : /* Implementations of specific functions. */
65 : :
66 : : /* Handler for "operator new" and "operator new []". */
67 : :
68 : 6626 : class kf_operator_new : public known_function
69 : : {
70 : : public:
71 : 3090 : bool matches_call_types_p (const call_details &cd) const final override
72 : : {
73 : 3090 : return (cd.num_args () == 1
74 : 1512 : && cd.arg_is_size_p (0))
75 : 3090 : || (cd.num_args () == 2
76 : 1578 : && cd.arg_is_size_p (0)
77 : 1578 : && POINTER_TYPE_P (cd.get_arg_type (1)));
78 : : }
79 : :
80 : 1095 : void impl_call_pre (const call_details &cd) const final override
81 : : {
82 : 1095 : region_model *model = cd.get_model ();
83 : 1095 : region_model_manager *mgr = cd.get_manager ();
84 : 1095 : const svalue *size_sval = cd.get_arg_svalue (0);
85 : 1095 : region_model_context *ctxt = cd.get_ctxt ();
86 : 1095 : const gcall &call = cd.get_call_stmt ();
87 : :
88 : : /* If the call was actually a placement new, check that accessing
89 : : the buffer lhs is placed into does not result in out-of-bounds. */
90 : 1095 : if (is_placement_new_p (call))
91 : : {
92 : 117 : const region *ptr_reg = cd.deref_ptr_arg (1);
93 : 117 : if (ptr_reg && cd.get_lhs_type ())
94 : : {
95 : 117 : const svalue *num_bytes_sval = cd.get_arg_svalue (0);
96 : 117 : const region *sized_new_reg
97 : 117 : = mgr->get_sized_region (ptr_reg,
98 : : cd.get_lhs_type (),
99 : : num_bytes_sval);
100 : 117 : model->check_region_for_write (sized_new_reg,
101 : : nullptr,
102 : : ctxt);
103 : 117 : const svalue *ptr_sval
104 : 117 : = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_new_reg);
105 : 117 : cd.maybe_set_lhs (ptr_sval);
106 : : }
107 : : }
108 : : /* If the call is an allocating new, then create a heap allocated
109 : : region. */
110 : : else
111 : : {
112 : 978 : const region *new_reg
113 : 978 : = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
114 : 978 : if (cd.get_lhs_type ())
115 : : {
116 : 978 : const svalue *ptr_sval
117 : 978 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
118 : 978 : cd.maybe_set_lhs (ptr_sval);
119 : : }
120 : : }
121 : 1095 : }
122 : :
123 : 1095 : void impl_call_post (const call_details &cd) const final override
124 : : {
125 : 1095 : region_model *model = cd.get_model ();
126 : 1095 : region_model_manager *mgr = cd.get_manager ();
127 : 1095 : tree callee_fndecl = cd.get_fndecl_for_call ();
128 : 1095 : region_model_context *ctxt = cd.get_ctxt ();
129 : :
130 : : /* If the call is guaranteed to return nonnull
131 : : then add a nonnull constraint to the allocated region. */
132 : 1095 : if (!TREE_NOTHROW (callee_fndecl) && flag_exceptions)
133 : : {
134 : 291 : const svalue *null_sval
135 : 291 : = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
136 : 291 : const svalue *result
137 : 291 : = model->get_store_value (cd.get_lhs_region (), ctxt);
138 : 291 : model->add_constraint (result, NE_EXPR, null_sval, ctxt);
139 : : }
140 : 1095 : }
141 : : };
142 : :
143 : : /* Handler for "operator delete" and for "operator delete []",
144 : : both the sized and unsized variants
145 : : (2 arguments and 1 argument respectively). */
146 : :
147 : 6626 : class kf_operator_delete : public known_function
148 : : {
149 : : public:
150 : 1081 : bool matches_call_types_p (const call_details &cd) const final override
151 : : {
152 : 1081 : return cd.num_args () == 1 or cd.num_args () == 2;
153 : : }
154 : :
155 : 233 : void impl_call_post (const call_details &cd) const final override
156 : : {
157 : 233 : region_model *model = cd.get_model ();
158 : 233 : const svalue *ptr_sval = cd.get_arg_svalue (0);
159 : 233 : if (const region *freed_reg = ptr_sval->maybe_get_region ())
160 : : {
161 : : /* If the ptr points to an underlying heap region, delete it,
162 : : poisoning pointers. */
163 : 186 : model->unbind_region_and_descendents (freed_reg,
164 : : poison_kind::deleted);
165 : : }
166 : 233 : }
167 : :
168 : : };
169 : :
170 : 3313 : class kf_cxa_allocate_exception : public known_function
171 : : {
172 : : public:
173 : 822 : bool matches_call_types_p (const call_details &cd) const final override
174 : : {
175 : 822 : return cd.num_args () == 1 && cd.arg_is_size_p (0);
176 : : }
177 : :
178 : 181 : void impl_call_pre (const call_details &cd) const final override
179 : : {
180 : 181 : region_model *model = cd.get_model ();
181 : 181 : region_model_manager *mgr = cd.get_manager ();
182 : 181 : const svalue *size_sval = cd.get_arg_svalue (0);
183 : 181 : region_model_context *ctxt = cd.get_ctxt ();
184 : :
185 : : /* Create a heap allocated region. */
186 : 181 : const region *new_reg
187 : 181 : = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
188 : 181 : if (cd.get_lhs_type ())
189 : : {
190 : 181 : const svalue *ptr_sval
191 : 181 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
192 : 181 : cd.maybe_set_lhs (ptr_sval);
193 : : }
194 : 181 : }
195 : : };
196 : :
197 : 3313 : class kf_cxa_begin_catch : public known_function
198 : : {
199 : : public:
200 : 936 : bool matches_call_types_p (const call_details &cd) const final override
201 : : {
202 : 936 : return (cd.num_args () == 1
203 : 936 : && POINTER_TYPE_P (cd.get_arg_type (0)));
204 : : }
205 : :
206 : 174 : void impl_call_pre (const call_details &cd) const final override
207 : : {
208 : 174 : region_model *model = cd.get_model ();
209 : :
210 : 174 : auto node = model->pop_thrown_exception ();
211 : 174 : model->push_caught_exception (node);
212 : 174 : cd.maybe_set_lhs (node.m_exception_sval);
213 : 174 : }
214 : : };
215 : :
216 : 3313 : class kf_cxa_end_catch : public known_function
217 : : {
218 : : public:
219 : 1140 : bool matches_call_types_p (const call_details &cd) const final override
220 : : {
221 : 1140 : return cd.num_args () == 0;
222 : : }
223 : :
224 : 204 : void impl_call_pre (const call_details &cd) const final override
225 : : {
226 : 204 : region_model *model = cd.get_model ();
227 : 204 : model->pop_caught_exception ();
228 : 204 : }
229 : : };
230 : :
231 : : /* A subclass of pending_diagnostic for complaining about an exception
232 : : of an unexpected type being thrown (due to a call to
233 : : __cxa_call_unexpected).
234 : : See https://en.cppreference.com/w/cpp/language/except_spec */
235 : :
236 : : class throw_of_unexpected_type
237 : : : public pending_diagnostic_subclass<throw_of_unexpected_type>
238 : : {
239 : : public:
240 : 3 : throw_of_unexpected_type (tree exception_type,
241 : : tree thrown_from_fndecl)
242 : 3 : : m_exception_type (exception_type),
243 : 3 : m_thrown_from_fndecl (thrown_from_fndecl)
244 : : {
245 : 3 : gcc_assert (m_exception_type);
246 : 3 : gcc_assert (m_thrown_from_fndecl);
247 : 3 : }
248 : :
249 : 12 : const char *get_kind () const final override
250 : : {
251 : 12 : return "throw_of_unexpected_type";
252 : : }
253 : :
254 : 3 : bool operator== (const throw_of_unexpected_type &other) const
255 : : {
256 : 3 : return (m_exception_type == other.m_exception_type
257 : 3 : && m_thrown_from_fndecl == other.m_thrown_from_fndecl);
258 : : }
259 : :
260 : 6 : int get_controlling_option () const final override
261 : : {
262 : 6 : return OPT_Wanalyzer_throw_of_unexpected_type;
263 : : }
264 : :
265 : 3 : bool emit (diagnostic_emission_context &ctxt) final override
266 : : {
267 : 3 : auto_diagnostic_group d;
268 : :
269 : 3 : bool warned
270 : 3 : = ctxt.warn ("throwing exception of unexpected type %qT from %qE",
271 : : m_exception_type, m_thrown_from_fndecl);
272 : 3 : if (warned)
273 : : {
274 : 3 : inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl),
275 : : "%qE declared here", m_thrown_from_fndecl);
276 : : // TODO: show specified types?
277 : : }
278 : 6 : return warned;
279 : 3 : }
280 : :
281 : : bool
282 : 6 : describe_final_event (pretty_printer &pp,
283 : : const evdesc::final_event &) final override
284 : : {
285 : 6 : pp_printf (&pp,
286 : : "exception of unexpected type %qT thrown from %qE",
287 : : m_exception_type, m_thrown_from_fndecl);
288 : 6 : return true;
289 : : }
290 : :
291 : : private:
292 : : tree m_exception_type;
293 : : tree m_thrown_from_fndecl;
294 : : };
295 : :
296 : : /* See https://en.cppreference.com/w/cpp/language/except_spec */
297 : :
298 : 3313 : class kf_cxa_call_unexpected : public known_function
299 : : {
300 : : public:
301 : 15 : bool matches_call_types_p (const call_details &cd) const final override
302 : : {
303 : 15 : return (cd.num_args () == 1
304 : 15 : && POINTER_TYPE_P (cd.get_arg_type (0)));
305 : : }
306 : :
307 : 3 : void impl_call_pre (const call_details &cd) const final override
308 : : {
309 : 3 : if (region_model_context *ctxt = cd.get_ctxt ())
310 : : {
311 : 3 : region_model *model = cd.get_model ();
312 : 3 : tree thrown_from_fndecl = model->get_current_function ()->decl;
313 : : /* We must have a thrown exception. */
314 : 3 : auto eh_node = model->get_current_thrown_exception ();
315 : 0 : gcc_assert (eh_node);
316 : 3 : tree exception_type = eh_node->maybe_get_type ();
317 : 3 : ctxt->warn
318 : 3 : (std::make_unique<throw_of_unexpected_type> (exception_type,
319 : : thrown_from_fndecl));
320 : 3 : ctxt->terminate_path ();
321 : : }
322 : 3 : }
323 : : };
324 : :
325 : : /* Populate KFM with instances of known functions relating to C++. */
326 : :
327 : : void
328 : 3313 : register_known_functions_lang_cp (known_function_manager &kfm)
329 : : {
330 : 3313 : kfm.add ("operator new", std::make_unique<kf_operator_new> ());
331 : 3313 : kfm.add ("operator new []", std::make_unique<kf_operator_new> ());
332 : 3313 : kfm.add ("operator delete", std::make_unique<kf_operator_delete> ());
333 : 3313 : kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ());
334 : :
335 : : /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s
336 : : "Level II: C++ ABI"
337 : : https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi */
338 : 3313 : kfm.add ("__cxa_allocate_exception",
339 : 3313 : std::make_unique<kf_cxa_allocate_exception> ());
340 : : // We treat __cxa_throw and __cxa_rethrow as special cases
341 : 3313 : kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ());
342 : 3313 : kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ());
343 : 3313 : kfm.add ("__cxa_call_unexpected",
344 : 3313 : std::make_unique<kf_cxa_call_unexpected> ());
345 : 3313 : }
346 : :
347 : : } // namespace ana
348 : :
349 : : #endif /* #if ENABLE_ANALYZER */
|