1 /* Handling for the known behavior of various functions specific to C++.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
22 #define INCLUDE_MEMORY
23 #define INCLUDE_VECTOR
25 #include "coretypes.h"
28 #include "basic-block.h"
30 #include "analyzer/analyzer.h"
31 #include "analyzer/analyzer-logging.h"
32 #include "diagnostic.h"
33 #include "analyzer/region-model.h"
34 #include "analyzer/call-details.h"
35 #include "make-unique.h"
39 /* Return true if CALL is a non-allocating operator new or operator new []
40 that contains no user-defined args, i.e. having any signature of:
42 - void* operator new (std::size_t count, void* ptr);
43 - void* operator new[] (std::size_t count, void* ptr);
45 See https://en.cppreference.com/w/cpp/memory/new/operator_new. */
47 bool is_placement_new_p (const gcall
*call
)
50 tree fndecl
= gimple_call_fndecl (call
);
52 if (!fndecl
|| TREE_CODE (TREE_TYPE (fndecl
)) == METHOD_TYPE
)
53 /* Give up on overloaded operator new. */
56 if (!is_named_call_p (fndecl
, "operator new", call
, 2)
57 && !is_named_call_p (fndecl
, "operator new []", call
, 2))
60 /* We must distinguish between an allocating non-throwing new
61 and a non-allocating new.
63 The former might have one of the following signatures :
64 void* operator new (std::size_t count, const std::nothrow_t& tag);
65 void* operator new[] (std::size_t count, const std::nothrow_t& tag);
66 Whereas a placement new would take a pointer. */
67 tree arg1_type
= TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl
)));
68 return TREE_CODE (TREE_VALUE (arg1_type
)) == POINTER_TYPE
;
73 /* Implementations of specific functions. */
75 /* Handler for "operator new" and "operator new []". */
77 class kf_operator_new
: public known_function
80 bool matches_call_types_p (const call_details
&cd
) const final override
82 return (cd
.num_args () == 1
83 && cd
.arg_is_size_p (0))
84 || (cd
.num_args () == 2
85 && cd
.arg_is_size_p (0)
86 && POINTER_TYPE_P (cd
.get_arg_type (1)));
89 void impl_call_pre (const call_details
&cd
) const final override
91 region_model
*model
= cd
.get_model ();
92 region_model_manager
*mgr
= cd
.get_manager ();
93 const svalue
*size_sval
= cd
.get_arg_svalue (0);
94 region_model_context
*ctxt
= cd
.get_ctxt ();
95 const gcall
*call
= cd
.get_call_stmt ();
97 /* If the call was actually a placement new, check that accessing
98 the buffer lhs is placed into does not result in out-of-bounds. */
99 if (is_placement_new_p (call
))
101 const region
*ptr_reg
= cd
.deref_ptr_arg (1);
102 if (ptr_reg
&& cd
.get_lhs_type ())
104 const svalue
*num_bytes_sval
= cd
.get_arg_svalue (0);
105 const region
*sized_new_reg
106 = mgr
->get_sized_region (ptr_reg
,
109 model
->check_region_for_write (sized_new_reg
,
112 const svalue
*ptr_sval
113 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), sized_new_reg
);
114 cd
.maybe_set_lhs (ptr_sval
);
117 /* If the call is an allocating new, then create a heap allocated
121 const region
*new_reg
122 = model
->get_or_create_region_for_heap_alloc (size_sval
, ctxt
);
123 if (cd
.get_lhs_type ())
125 const svalue
*ptr_sval
126 = mgr
->get_ptr_svalue (cd
.get_lhs_type (), new_reg
);
127 cd
.maybe_set_lhs (ptr_sval
);
132 void impl_call_post (const call_details
&cd
) const final override
134 region_model
*model
= cd
.get_model ();
135 region_model_manager
*mgr
= cd
.get_manager ();
136 tree callee_fndecl
= cd
.get_fndecl_for_call ();
137 region_model_context
*ctxt
= cd
.get_ctxt ();
139 /* If the call is guaranteed to return nonnull
140 then add a nonnull constraint to the allocated region. */
141 if (!TREE_NOTHROW (callee_fndecl
) && flag_exceptions
)
143 const svalue
*null_sval
144 = mgr
->get_or_create_null_ptr (cd
.get_lhs_type ());
146 = model
->get_store_value (cd
.get_lhs_region (), ctxt
);
147 model
->add_constraint (result
, NE_EXPR
, null_sval
, ctxt
);
152 /* Handler for "operator delete" and for "operator delete []",
153 both the sized and unsized variants
154 (2 arguments and 1 argument respectively). */
156 class kf_operator_delete
: public known_function
159 bool matches_call_types_p (const call_details
&cd
) const final override
161 return cd
.num_args () == 1 or cd
.num_args () == 2;
164 void impl_call_post (const call_details
&cd
) const final override
166 region_model
*model
= cd
.get_model ();
167 const svalue
*ptr_sval
= cd
.get_arg_svalue (0);
168 if (const region
*freed_reg
= ptr_sval
->maybe_get_region ())
170 /* If the ptr points to an underlying heap region, delete it,
171 poisoning pointers. */
172 model
->unbind_region_and_descendents (freed_reg
,
173 POISON_KIND_DELETED
);
179 /* Populate KFM with instances of known functions relating to C++. */
182 register_known_functions_lang_cp (known_function_manager
&kfm
)
184 kfm
.add ("operator new", make_unique
<kf_operator_new
> ());
185 kfm
.add ("operator new []", make_unique
<kf_operator_new
> ());
186 kfm
.add ("operator delete", make_unique
<kf_operator_delete
> ());
187 kfm
.add ("operator delete []", make_unique
<kf_operator_delete
> ());
192 #endif /* #if ENABLE_ANALYZER */