Line data Source code
1 : /* Handling for the known behavior of various functions specific to C++.
2 : Copyright (C) 2020-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 : #include "cgraph.h"
23 : #include "ipa-utils.h"
24 :
25 : #include "diagnostic.h"
26 :
27 : #include "analyzer/analyzer-logging.h"
28 : #include "analyzer/region-model.h"
29 : #include "analyzer/call-details.h"
30 :
31 : #if ENABLE_ANALYZER
32 :
33 : /* Return true if CALL is a non-allocating operator new or operator new []
34 : that contains no user-defined args, i.e. having any signature of:
35 :
36 : - void* operator new (std::size_t count, void* ptr);
37 : - void* operator new[] (std::size_t count, void* ptr);
38 :
39 : See https://en.cppreference.com/w/cpp/memory/new/operator_new. */
40 :
41 42981 : bool is_placement_new_p (const gcall &call)
42 : {
43 42981 : tree fndecl = gimple_call_fndecl (&call);
44 :
45 42981 : if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
46 : /* Give up on overloaded operator new. */
47 : return false;
48 :
49 42603 : if (!is_named_call_p (fndecl, "operator new", call, 2)
50 42603 : && !is_named_call_p (fndecl, "operator new []", call, 2))
51 : return false;
52 :
53 : /* We must distinguish between an allocating non-throwing new
54 : and a non-allocating new.
55 :
56 : The former might have one of the following signatures :
57 : void* operator new (std::size_t count, const std::nothrow_t& tag);
58 : void* operator new[] (std::size_t count, const std::nothrow_t& tag);
59 : Whereas a placement new would take a pointer. */
60 1155 : tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
61 1155 : return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
62 : }
63 :
64 : namespace ana {
65 :
66 : /* Implementations of specific functions. */
67 :
68 : /* Handler for __dynamic_cast. */
69 :
70 : /* A candidate TYPE subobject found on one inheritance path. */
71 :
72 : struct dyncast_subobject
73 : {
74 96 : dyncast_subobject () : binfo (NULL_TREE), accessible (false) {}
75 237 : dyncast_subobject (tree binfo, bool accessible)
76 237 : : binfo (binfo), accessible (accessible)
77 : {}
78 :
79 : tree binfo; /* the BINFO that represents our subobject.
80 : NULL_TREE if failed. */
81 : bool accessible; /* Every edge from the root was public. */
82 : };
83 :
84 : /* Recover the class type from a type_info argument of __dynamic_cast, expected
85 : to be &_ZTIxxx. The C++ FE sets TREE_TYPE on the tinfo decl's DECL_NAME
86 : identifier. __dynamic_cast is callable directly, so a runtime tinfo pointer
87 : (an SSA name) can exist. Return NULL_TREE on any shape mismatch. */
88 :
89 : static tree
90 198 : get_type_from_tinfo_arg (tree arg)
91 : {
92 198 : if (!arg || TREE_CODE (arg) != ADDR_EXPR)
93 : return NULL_TREE;
94 198 : tree tinfo_decl = TREE_OPERAND (arg, 0);
95 198 : if (!DECL_P (tinfo_decl) || !DECL_NAME (tinfo_decl))
96 : return NULL_TREE;
97 198 : tree type = TREE_TYPE (DECL_NAME (tinfo_decl));
98 198 : if (!type || !RECORD_OR_UNION_TYPE_P (type))
99 : return NULL_TREE;
100 198 : return TYPE_MAIN_VARIANT (type);
101 : }
102 :
103 : /* Find the sub-BINFO of BINFO that has type TARGET_TYPE and sits at the same
104 : address as BINFO itself (i.e. at relative offset 0) by descending through
105 : every base at that same (absolute) offset. */
106 :
107 : static tree
108 216 : lookup_binfo_at_same_offset (tree binfo, tree target_type)
109 : {
110 216 : if (types_same_for_odr (BINFO_TYPE (binfo), target_type))
111 : return binfo;
112 :
113 120 : tree offset = BINFO_OFFSET (binfo);
114 120 : tree base_binfo;
115 120 : for (unsigned i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
116 120 : if (tree_int_cst_equal (BINFO_OFFSET (base_binfo), offset))
117 120 : if (tree found = lookup_binfo_at_same_offset (base_binfo, target_type))
118 : return found;
119 : return NULL_TREE;
120 : }
121 :
122 : /* Look recursively for a BINFO that matches our DST_TYPE. This method might
123 : find multiple matches. If none is found, matches won't be changed.
124 :
125 : Even if one path is private, it is still ambiguous according to the
126 : definition, so that case still counts as having multiple matches. That
127 : means we ignore access specifiers when searching bases.
128 :
129 : Morally virtual matches of the same type under different virtual ancestors
130 : are distinct subobjects, i.e:
131 :
132 : class B0 {};
133 : class V1 : B0 {}; class V2 : B0 {};
134 : class B2 : virtual V1 {}; class B4 : virtual V2 {};
135 : class MD : B2, B3 {};
136 :
137 : Here, each B0 is a different subobject, so we must account for this case
138 : when checking virtual inheritance. We compare the BINFO_OFFSET of all the
139 : BINFOs that match (the offset is relative to our most-derived object) to
140 : decide if they belong to the same suboject. Note that if it is at the same
141 : BINFO_OFFSET and has the same TREE_TYPE, it must necessarily be the same
142 : subobject. */
143 :
144 : static void
145 678 : lookup_subobject_matches (const_tree target_type, const dyncast_subobject match,
146 : auto_vec<dyncast_subobject> &matches)
147 : {
148 678 : if (types_same_for_odr (BINFO_TYPE (match.binfo), target_type))
149 : {
150 : /* Check if we had already found this particular subobject. */
151 198 : tree match_offset = BINFO_OFFSET (match.binfo);
152 273 : for (auto &subobject : matches)
153 27 : if (tree_int_cst_equal (BINFO_OFFSET (subobject.binfo), match_offset))
154 : {
155 6 : subobject.accessible |= match.accessible;
156 6 : return; /* We are adding the same subobject, so skip it. */
157 : }
158 192 : matches.safe_push (match);
159 192 : return;
160 : }
161 : tree parent_binfo = match.binfo;
162 : tree base_binfo;
163 936 : for (unsigned i = 0; BINFO_BASE_ITERATE (parent_binfo, i, base_binfo); i++)
164 : {
165 456 : dyncast_subobject child = match;
166 456 : child.binfo = base_binfo;
167 : /* If BINFO_BASE_ACCESSES is not present, public access is implied. */
168 912 : child.accessible
169 912 : &= !BINFO_BASE_ACCESSES (parent_binfo)
170 456 : || BINFO_BASE_ACCESS (parent_binfo, i) == access_public_node;
171 : /* Check if the next binfo might be our DST_TYPE binfo recursively. */
172 456 : lookup_subobject_matches (target_type, child, matches);
173 : }
174 : }
175 :
176 : /* We implement the runtime check rules as per [expr.dynamic.cast]9.
177 : As a general overview, those rules state:
178 : [expr.dynamic.cast]/9.1: Does SRC_OBJ point to a public base subobject of
179 : a DST_TYPE object? And is there only one DST_TYPE object derived from
180 : SRC_OBJ?
181 : We expect the hierarchy to be something like:
182 : SRC_TYPE -> ... -> DST_TYPE -> ... -> MD_TYPE.
183 :
184 : [expr.dynamic.cast]/9.2: Otherwise, does SRC_OBJ point to a public base
185 : subobject of a MDTYPE object? And is DST_TYPE an unambiguous and public
186 : base of MDTYPE?
187 : We expect the hierarchy to be something like:
188 : MD_TYPE -> ... -> DST_TYPE
189 :
190 : [expr.dynamic.cast]/9.3: Otherwise, the runtime check fails. */
191 :
192 : static dyncast_subobject
193 96 : evaluate_dyncast (tree dst_type, tree md_binfo, tree src_binfo)
194 : {
195 96 : dyncast_subobject no_base_match;
196 :
197 : /* Start by assuming the path will be public. */
198 96 : auto_vec<dyncast_subobject> dst_matches;
199 96 : dyncast_subobject md_subobject = {md_binfo, /* accessible = */ true};
200 96 : lookup_subobject_matches (dst_type, md_subobject, dst_matches);
201 :
202 : /* Per [expr.dynamic.cast]/9.1:
203 : Only one object of DST_TYPE can be derived from this SRC_OBJ and
204 : The path from DST -> SRC must be public. */
205 96 : tree src_offset = BINFO_OFFSET (src_binfo);
206 96 : tree src_type = BINFO_TYPE (src_binfo);
207 :
208 96 : auto_vec<dyncast_subobject> clause1_matches;
209 351 : for (const auto &dst_subobj : dst_matches)
210 : {
211 93 : auto_vec<dyncast_subobject> src_matches;
212 93 : dyncast_subobject from_dst = {dst_subobj.binfo, /* accessible = */ true};
213 93 : lookup_subobject_matches (src_type, from_dst, src_matches);
214 : /* Only keep matches that derive from this src subobject. */
215 267 : for (const auto &src_subobj : src_matches)
216 60 : if (tree_int_cst_equal (BINFO_OFFSET (src_subobj.binfo), src_offset))
217 : /* Keep dst BINFO but save whether SRC is a public base of DST. */
218 48 : clause1_matches.safe_push ({dst_subobj.binfo, src_subobj.accessible});
219 93 : }
220 138 : if (clause1_matches.length () == 1 && clause1_matches[0].accessible)
221 30 : return clause1_matches[0]; /* No ambiguity, only one public match. */
222 :
223 : /* No match or the match we found was private. Try clause 2. */
224 :
225 : /* Otherwise, per [expr.dynamic.cast]/9.2:
226 : Require a public path from MD_OBJ -> SRC_OBJ and
227 : Require that DST_TYPE is an unambiguous and public base of MD_TYPE. */
228 105 : if (dst_matches.length () == 1 && dst_matches[0].accessible)
229 : {
230 33 : auto_vec<dyncast_subobject> src_matches;
231 33 : lookup_subobject_matches (src_type, md_subobject, src_matches);
232 : /* Find any public path from MD_OBJ -> SRC_OBJ. */
233 117 : for (const auto &src_subobj : src_matches)
234 33 : if (src_subobj.accessible)
235 15 : return dst_matches[0]; /* Found a public match. */
236 33 : }
237 51 : return no_base_match; /* No match or the match we found was private. */
238 96 : }
239 :
240 3501 : class kf_dynamic_cast : public known_function
241 : {
242 : public:
243 663 : bool matches_call_types_p (const call_details &cd) const final override
244 : {
245 : /* A call will look something like:
246 : Derived *d;
247 : d = __dynamic_cast ((Base*) b, &_ZTI1Base, &_ZTI1Derived, 8); */
248 1326 : return (cd.num_args () == 4 && POINTER_TYPE_P (cd.get_arg_type (0))
249 663 : && POINTER_TYPE_P (cd.get_arg_type (1))
250 663 : && POINTER_TYPE_P (cd.get_arg_type (2))
251 1326 : && INTEGRAL_TYPE_P (cd.get_arg_type (3)));
252 : }
253 99 : void impl_call_post (const call_details &cd) const final override
254 : {
255 99 : region_model *model = cd.get_model ();
256 99 : region_model_manager *mgr = cd.get_manager ();
257 :
258 99 : cd.set_any_lhs_with_defaults ();
259 :
260 99 : tree dst_ptr_type = cd.get_lhs_type ();
261 99 : if (!dst_ptr_type)
262 54 : return;
263 :
264 : /* Recover the class types from the tinfo args. */
265 99 : tree src_type = get_type_from_tinfo_arg (cd.get_arg_tree (1));
266 99 : tree dst_type = get_type_from_tinfo_arg (cd.get_arg_tree (2));
267 99 : if (!src_type || !dst_type)
268 : return;
269 :
270 : /* Read the vptr binding of the object; VPTR_OFF selects the
271 : sub-vtable within the vtable decl, so it identifies which subobject's
272 : vptr we read. */
273 99 : tree src_obj = cd.get_arg_tree (0);
274 99 : unsigned HOST_WIDE_INT vptr_off;
275 99 : tree vtable
276 99 : = model->get_vtable_from_obj (src_obj, src_type, mgr, nullptr, &vptr_off);
277 : /* The class the vtable belongs to is the dynamic (most-derived) type of
278 : the object. VTABLE is whatever decl the vptr slot happened to point at,
279 : so check it really is a vtable. */
280 99 : if (!vtable || !VAR_P (vtable) || !DECL_VIRTUAL_P (vtable))
281 : return;
282 96 : tree mdtype = DECL_CONTEXT (vtable);
283 96 : if (!mdtype || !RECORD_OR_UNION_TYPE_P (mdtype))
284 : return;
285 96 : tree md_binfo = TYPE_BINFO (mdtype);
286 96 : if (!md_binfo)
287 : return;
288 :
289 : /* Given the value stored to SRC_OBJ's vtpr field (&_ZTV* + offset), find
290 : which subobject of this hierarchy would have this value written into its
291 : vptr. */
292 96 : tree vtable_binfo
293 96 : = subbinfo_with_vtable_at_offset (md_binfo, vptr_off, vtable);
294 96 : if (!vtable_binfo)
295 : return;
296 : /* With a shared primary-base vtable the owning binfo may be an enclosing
297 : type. Consider:
298 : class A {};
299 : class B {};
300 : class C : B {};
301 : class D : A, C {};
302 : Here the vptr value stored in the B-subobject's slot is owned by the C
303 : binfo (C's sub-vtable group), and a lookup with B's vptr value returns
304 : the C binfo, not B's (BINFO_VTABLE is only set on the owner, cf.
305 : ipa-devirt.cc:61). In this case, the src subobject sits on its primary
306 : chain (relative offset 0), which lookup_binfo_at_same_offset finds. */
307 96 : tree src_binfo = lookup_binfo_at_same_offset (vtable_binfo, src_type);
308 96 : if (!src_binfo)
309 : return;
310 :
311 96 : dyncast_subobject dst_match
312 96 : = evaluate_dyncast (dst_type, md_binfo, src_binfo);
313 :
314 96 : if (!dst_match.binfo)
315 : { /* [expr.dynamic.cast]/9.3: Otherwise, the runtime check failed. */
316 51 : cd.maybe_set_lhs (mgr->get_or_create_null_ptr (dst_ptr_type));
317 51 : return;
318 : }
319 :
320 : /* Build a pointer to the dst subobject. Work in byte offsets relative to
321 : SRC_REG's base region; we never need a region for the mdtype object
322 : itself, only its start offset, recovered from where the src subobject
323 : sits within MDTYPE. */
324 45 : const region *src_reg = cd.deref_ptr_arg (0);
325 45 : region_offset off = src_reg->get_offset (mgr);
326 45 : if (!off.concrete_p ())
327 : return; /* Bail, leave lhs conjured. */
328 45 : byte_offset_t src_obj_start;
329 45 : if (!off.get_concrete_byte_offset (&src_obj_start))
330 : return;
331 :
332 45 : HOST_WIDE_INT src_off_in_md = tree_to_shwi (BINFO_OFFSET (src_binfo));
333 45 : HOST_WIDE_INT dst_off_in_md = tree_to_shwi (BINFO_OFFSET (dst_match.binfo));
334 45 : HOST_WIDE_INT md_start_in_base = src_obj_start.to_shwi () - src_off_in_md;
335 45 : if (md_start_in_base < 0)
336 : return; /* Layout disagreement between the store and the binfo data;
337 : bail rather than build a negative-offset region. */
338 45 : HOST_WIDE_INT dst_off_in_base = md_start_in_base + dst_off_in_md;
339 :
340 : /* BASE_REG is the outermost region, not necessarily the mdtype
341 : object (it might sit at a nonzero offset inside BASE_REG, e.g. as an
342 : array element or a member subobject). The store binds values by byte
343 : ranges within a base region, so a concrete offset_region aliases the
344 : FE's field-path accesses to the same bytes. */
345 45 : const region *base_reg = off.get_base_region ();
346 45 : const svalue *dst_off_sval
347 45 : = mgr->get_or_create_int_cst (size_type_node, dst_off_in_base);
348 45 : const region *dst_reg
349 45 : = mgr->get_offset_region (base_reg, dst_type, dst_off_sval);
350 45 : cd.maybe_set_lhs (mgr->get_ptr_svalue (dst_ptr_type, dst_reg));
351 : }
352 : };
353 :
354 : /* Handler for "operator new" and "operator new []". */
355 :
356 7002 : class kf_operator_new : public known_function
357 : {
358 : public:
359 4437 : bool matches_call_types_p (const call_details &cd) const final override
360 : {
361 4437 : return (cd.num_args () == 1
362 2130 : && cd.arg_is_size_p (0))
363 4437 : || (cd.num_args () == 2
364 2307 : && cd.arg_is_size_p (0)
365 2307 : && POINTER_TYPE_P (cd.get_arg_type (1)));
366 : }
367 :
368 : void
369 309 : check_any_preconditions (const call_details &cd) const final override
370 : {
371 309 : region_model_context *ctxt = cd.get_ctxt ();
372 309 : if (!ctxt)
373 : return;
374 309 : region_model *model = cd.get_model ();
375 309 : const gcall &call = cd.get_call_stmt ();
376 :
377 : /* If the call was actually a placement new, check that accessing
378 : the buffer lhs is placed into does not result in out-of-bounds. */
379 309 : if (is_placement_new_p (call))
380 : {
381 69 : if (const region *sized_reg = get_sized_region_for_placement_new (cd))
382 57 : model->check_region_for_write (sized_reg,
383 : nullptr,
384 : ctxt);
385 : }
386 : }
387 :
388 1578 : void impl_call_pre (const call_details &cd) const final override
389 : {
390 1578 : region_model *model = cd.get_model ();
391 1578 : region_model_manager *mgr = cd.get_manager ();
392 1578 : const svalue *size_sval = cd.get_arg_svalue (0);
393 1578 : region_model_context *ctxt = cd.get_ctxt ();
394 1578 : const gcall &call = cd.get_call_stmt ();
395 :
396 1578 : if (is_placement_new_p (call))
397 : {
398 12 : const region *ptr_reg = cd.deref_ptr_arg (1);
399 12 : if (ptr_reg && cd.get_lhs_type ())
400 0 : if (const region *sized_reg = get_sized_region_for_placement_new (cd))
401 : {
402 0 : const svalue *ptr_sval
403 0 : = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_reg);
404 0 : cd.maybe_set_lhs (ptr_sval);
405 : }
406 : }
407 : /* If the call is an allocating new, then create a heap allocated
408 : region. */
409 : else
410 : {
411 1566 : const region *new_reg
412 1566 : = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
413 1566 : if (cd.get_lhs_type ())
414 : {
415 1566 : const svalue *ptr_sval
416 1566 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
417 1566 : cd.maybe_set_lhs (ptr_sval);
418 : }
419 : }
420 1578 : }
421 :
422 1578 : void impl_call_post (const call_details &cd) const final override
423 : {
424 1578 : region_model *model = cd.get_model ();
425 1578 : region_model_manager *mgr = cd.get_manager ();
426 1578 : tree callee_fndecl = cd.get_fndecl_for_call ();
427 1578 : region_model_context *ctxt = cd.get_ctxt ();
428 :
429 : /* If the call is guaranteed to return nonnull
430 : then add a nonnull constraint to the allocated region. */
431 1578 : if (!TREE_NOTHROW (callee_fndecl)
432 600 : && flag_exceptions
433 2034 : && cd.get_lhs_type ())
434 : {
435 444 : const svalue *null_sval
436 444 : = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
437 444 : const svalue *result
438 444 : = model->get_store_value (cd.get_lhs_region (), ctxt);
439 444 : model->add_constraint (result, NE_EXPR, null_sval, ctxt);
440 : }
441 1578 : }
442 :
443 : private:
444 : const region *
445 69 : get_sized_region_for_placement_new (const call_details &cd) const
446 : {
447 69 : const region *ptr_reg = cd.deref_ptr_arg (1);
448 69 : if (ptr_reg && cd.get_lhs_type ())
449 : {
450 57 : region_model_manager *mgr = cd.get_manager ();
451 57 : const svalue *num_bytes_sval = cd.get_arg_svalue (0);
452 57 : return mgr->get_sized_region (ptr_reg,
453 : cd.get_lhs_type (),
454 57 : num_bytes_sval);
455 : }
456 : return nullptr;
457 : }
458 : };
459 :
460 : /* Handler for "operator delete" and for "operator delete []",
461 : both the sized and unsized variants
462 : (2 arguments and 1 argument respectively). */
463 :
464 7002 : class kf_operator_delete : public known_function
465 : {
466 : public:
467 2574 : bool matches_call_types_p (const call_details &cd) const final override
468 : {
469 2574 : return cd.num_args () == 1 or cd.num_args () == 2;
470 : }
471 :
472 494 : void impl_call_post (const call_details &cd) const final override
473 : {
474 494 : region_model *model = cd.get_model ();
475 494 : const svalue *ptr_sval = cd.get_arg_svalue (0);
476 494 : if (const region *freed_reg = ptr_sval->maybe_get_region ())
477 : {
478 : /* If the ptr points to an underlying heap region, delete it,
479 : poisoning pointers. */
480 274 : model->unbind_region_and_descendents (freed_reg,
481 : poison_kind::deleted);
482 : }
483 494 : }
484 :
485 : };
486 :
487 3501 : class kf_cxa_allocate_exception : public known_function
488 : {
489 : public:
490 1325 : bool matches_call_types_p (const call_details &cd) const final override
491 : {
492 1325 : return cd.num_args () == 1 && cd.arg_is_size_p (0);
493 : }
494 :
495 305 : void impl_call_pre (const call_details &cd) const final override
496 : {
497 305 : region_model *model = cd.get_model ();
498 305 : region_model_manager *mgr = cd.get_manager ();
499 305 : const svalue *size_sval = cd.get_arg_svalue (0);
500 305 : region_model_context *ctxt = cd.get_ctxt ();
501 :
502 : /* Create a heap allocated region. */
503 305 : const region *new_reg
504 305 : = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
505 305 : if (cd.get_lhs_type ())
506 : {
507 305 : const svalue *ptr_sval
508 305 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
509 305 : cd.maybe_set_lhs (ptr_sval);
510 : }
511 305 : }
512 : };
513 :
514 3501 : class kf_cxa_begin_catch : public known_function
515 : {
516 : public:
517 1404 : bool matches_call_types_p (const call_details &cd) const final override
518 : {
519 1404 : return (cd.num_args () == 1
520 1404 : && POINTER_TYPE_P (cd.get_arg_type (0)));
521 : }
522 :
523 337 : void impl_call_pre (const call_details &cd) const final override
524 : {
525 337 : region_model *model = cd.get_model ();
526 :
527 337 : auto node = model->pop_thrown_exception ();
528 337 : model->push_caught_exception (node);
529 337 : cd.maybe_set_lhs (node.m_exception_sval);
530 337 : }
531 : };
532 :
533 3501 : class kf_cxa_end_catch : public known_function
534 : {
535 : public:
536 1292 : bool matches_call_types_p (const call_details &cd) const final override
537 : {
538 1292 : return cd.num_args () == 0;
539 : }
540 :
541 221 : void impl_call_pre (const call_details &cd) const final override
542 : {
543 221 : region_model *model = cd.get_model ();
544 221 : model->pop_caught_exception ();
545 221 : }
546 : };
547 :
548 : /* A subclass of pending_diagnostic for complaining about an exception
549 : of an unexpected type being thrown (due to a call to
550 : __cxa_call_unexpected).
551 : See https://en.cppreference.com/w/cpp/language/except_spec */
552 :
553 : class throw_of_unexpected_type
554 : : public pending_diagnostic_subclass<throw_of_unexpected_type>
555 : {
556 : public:
557 2 : throw_of_unexpected_type (tree exception_type,
558 : tree thrown_from_fndecl)
559 2 : : m_exception_type (exception_type),
560 2 : m_thrown_from_fndecl (thrown_from_fndecl)
561 : {
562 2 : gcc_assert (m_exception_type);
563 2 : gcc_assert (m_thrown_from_fndecl);
564 2 : }
565 :
566 13 : const char *get_kind () const final override
567 : {
568 13 : return "throw_of_unexpected_type";
569 : }
570 :
571 2 : bool operator== (const throw_of_unexpected_type &other) const
572 : {
573 2 : return (m_exception_type == other.m_exception_type
574 2 : && m_thrown_from_fndecl == other.m_thrown_from_fndecl);
575 : }
576 :
577 4 : int get_controlling_option () const final override
578 : {
579 4 : return OPT_Wanalyzer_throw_of_unexpected_type;
580 : }
581 :
582 2 : bool emit (diagnostic_emission_context &ctxt) final override
583 : {
584 2 : auto_diagnostic_group d;
585 :
586 2 : bool warned
587 2 : = ctxt.warn ("throwing exception of unexpected type %qT from %qE",
588 : m_exception_type, m_thrown_from_fndecl);
589 2 : if (warned)
590 : {
591 2 : inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl),
592 : "%qE declared here", m_thrown_from_fndecl);
593 : // TODO: show specified types?
594 : }
595 4 : return warned;
596 2 : }
597 :
598 : bool
599 4 : describe_final_event (pretty_printer &pp,
600 : const evdesc::final_event &) final override
601 : {
602 4 : pp_printf (&pp,
603 : "exception of unexpected type %qT thrown from %qE",
604 : m_exception_type, m_thrown_from_fndecl);
605 4 : return true;
606 : }
607 :
608 : private:
609 : tree m_exception_type;
610 : tree m_thrown_from_fndecl;
611 : };
612 :
613 : /* See https://en.cppreference.com/w/cpp/language/except_spec */
614 :
615 3501 : class kf_cxa_call_unexpected : public known_function
616 : {
617 : public:
618 12 : bool matches_call_types_p (const call_details &cd) const final override
619 : {
620 12 : return (cd.num_args () == 1
621 12 : && POINTER_TYPE_P (cd.get_arg_type (0)));
622 : }
623 :
624 2 : void impl_call_pre (const call_details &cd) const final override
625 : {
626 2 : if (region_model_context *ctxt = cd.get_ctxt ())
627 : {
628 2 : region_model *model = cd.get_model ();
629 2 : tree thrown_from_fndecl = model->get_current_function ()->decl;
630 : /* We must have a thrown exception. */
631 2 : auto eh_node = model->get_current_thrown_exception ();
632 0 : gcc_assert (eh_node);
633 2 : tree exception_type = eh_node->maybe_get_type ();
634 2 : ctxt->warn
635 2 : (std::make_unique<throw_of_unexpected_type> (exception_type,
636 : thrown_from_fndecl));
637 2 : ctxt->terminate_path ();
638 : }
639 2 : }
640 : };
641 :
642 : /* Populate KFM with instances of known functions relating to C++. */
643 :
644 : void
645 3501 : register_known_functions_lang_cp (known_function_manager &kfm)
646 : {
647 3501 : kfm.add ("operator new", std::make_unique<kf_operator_new> ());
648 3501 : kfm.add ("operator new []", std::make_unique<kf_operator_new> ());
649 3501 : kfm.add ("operator delete", std::make_unique<kf_operator_delete> ());
650 3501 : kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ());
651 :
652 : /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s
653 : "Level II: C++ ABI"
654 : https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi */
655 3501 : kfm.add ("__cxa_allocate_exception",
656 3501 : std::make_unique<kf_cxa_allocate_exception> ());
657 : // We treat __cxa_throw and __cxa_rethrow as special cases
658 3501 : kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ());
659 3501 : kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ());
660 3501 : kfm.add ("__cxa_call_unexpected",
661 3501 : std::make_unique<kf_cxa_call_unexpected> ());
662 :
663 : /* Itanium C++ ABI's "The dynamic_cast Algorithm"
664 : https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dynamic_cast-algorithm
665 : */
666 3501 : kfm.add ("__dynamic_cast", std::make_unique<kf_dynamic_cast> ());
667 3501 : }
668 :
669 : } // namespace ana
670 :
671 : #endif /* #if ENABLE_ANALYZER */
|