Branch data Line data Source code
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>.
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 "config.h"
22 : : #define INCLUDE_VECTOR
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "tree.h"
26 : : #include "function.h"
27 : : #include "basic-block.h"
28 : : #include "gimple.h"
29 : : #include "analyzer/analyzer.h"
30 : : #include "analyzer/analyzer-logging.h"
31 : : #include "diagnostic.h"
32 : : #include "analyzer/region-model.h"
33 : : #include "analyzer/call-details.h"
34 : : #include "make-unique.h"
35 : :
36 : : #if ENABLE_ANALYZER
37 : :
38 : : /* Return true if CALL is a non-allocating operator new or operator new []
39 : : that contains no user-defined args, i.e. having any signature of:
40 : :
41 : : - void* operator new (std::size_t count, void* ptr);
42 : : - void* operator new[] (std::size_t count, void* ptr);
43 : :
44 : : See https://en.cppreference.com/w/cpp/memory/new/operator_new. */
45 : :
46 : 48159 : bool is_placement_new_p (const gcall *call)
47 : : {
48 : 48159 : gcc_assert (call);
49 : 48159 : tree fndecl = gimple_call_fndecl (call);
50 : :
51 : 48159 : if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
52 : : /* Give up on overloaded operator new. */
53 : : return false;
54 : :
55 : 47264 : if (!is_named_call_p (fndecl, "operator new", call, 2)
56 : 47264 : && !is_named_call_p (fndecl, "operator new []", call, 2))
57 : : return false;
58 : :
59 : : /* We must distinguish between an allocating non-throwing new
60 : : and a non-allocating new.
61 : :
62 : : The former might have one of the following signatures :
63 : : void* operator new (std::size_t count, const std::nothrow_t& tag);
64 : : void* operator new[] (std::size_t count, const std::nothrow_t& tag);
65 : : Whereas a placement new would take a pointer. */
66 : 708 : tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
67 : 708 : return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
68 : : }
69 : :
70 : : namespace ana {
71 : :
72 : : /* Implementations of specific functions. */
73 : :
74 : : /* Handler for "operator new" and "operator new []". */
75 : :
76 : 6394 : class kf_operator_new : public known_function
77 : : {
78 : : public:
79 : 3090 : bool matches_call_types_p (const call_details &cd) const final override
80 : : {
81 : 3090 : return (cd.num_args () == 1
82 : 1512 : && cd.arg_is_size_p (0))
83 : 3090 : || (cd.num_args () == 2
84 : 1578 : && cd.arg_is_size_p (0)
85 : 1578 : && POINTER_TYPE_P (cd.get_arg_type (1)));
86 : : }
87 : :
88 : 1097 : void impl_call_pre (const call_details &cd) const final override
89 : : {
90 : 1097 : region_model *model = cd.get_model ();
91 : 1097 : region_model_manager *mgr = cd.get_manager ();
92 : 1097 : const svalue *size_sval = cd.get_arg_svalue (0);
93 : 1097 : region_model_context *ctxt = cd.get_ctxt ();
94 : 1097 : const gcall *call = cd.get_call_stmt ();
95 : :
96 : : /* If the call was actually a placement new, check that accessing
97 : : the buffer lhs is placed into does not result in out-of-bounds. */
98 : 1097 : if (is_placement_new_p (call))
99 : : {
100 : 117 : const region *ptr_reg = cd.deref_ptr_arg (1);
101 : 117 : if (ptr_reg && cd.get_lhs_type ())
102 : : {
103 : 117 : const svalue *num_bytes_sval = cd.get_arg_svalue (0);
104 : 117 : const region *sized_new_reg
105 : 117 : = mgr->get_sized_region (ptr_reg,
106 : : cd.get_lhs_type (),
107 : : num_bytes_sval);
108 : 117 : model->check_region_for_write (sized_new_reg,
109 : : nullptr,
110 : : ctxt);
111 : 117 : const svalue *ptr_sval
112 : 117 : = mgr->get_ptr_svalue (cd.get_lhs_type (), sized_new_reg);
113 : 117 : cd.maybe_set_lhs (ptr_sval);
114 : : }
115 : : }
116 : : /* If the call is an allocating new, then create a heap allocated
117 : : region. */
118 : : else
119 : : {
120 : 980 : const region *new_reg
121 : 980 : = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
122 : 980 : if (cd.get_lhs_type ())
123 : : {
124 : 980 : const svalue *ptr_sval
125 : 980 : = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
126 : 980 : cd.maybe_set_lhs (ptr_sval);
127 : : }
128 : : }
129 : 1097 : }
130 : :
131 : 1097 : void impl_call_post (const call_details &cd) const final override
132 : : {
133 : 1097 : region_model *model = cd.get_model ();
134 : 1097 : region_model_manager *mgr = cd.get_manager ();
135 : 1097 : tree callee_fndecl = cd.get_fndecl_for_call ();
136 : 1097 : region_model_context *ctxt = cd.get_ctxt ();
137 : :
138 : : /* If the call is guaranteed to return nonnull
139 : : then add a nonnull constraint to the allocated region. */
140 : 1097 : if (!TREE_NOTHROW (callee_fndecl) && flag_exceptions)
141 : : {
142 : 293 : const svalue *null_sval
143 : 293 : = mgr->get_or_create_null_ptr (cd.get_lhs_type ());
144 : 293 : const svalue *result
145 : 293 : = model->get_store_value (cd.get_lhs_region (), ctxt);
146 : 293 : model->add_constraint (result, NE_EXPR, null_sval, ctxt);
147 : : }
148 : 1097 : }
149 : : };
150 : :
151 : : /* Handler for "operator delete" and for "operator delete []",
152 : : both the sized and unsized variants
153 : : (2 arguments and 1 argument respectively). */
154 : :
155 : 6394 : class kf_operator_delete : public known_function
156 : : {
157 : : public:
158 : 812 : bool matches_call_types_p (const call_details &cd) const final override
159 : : {
160 : 812 : return cd.num_args () == 1 or cd.num_args () == 2;
161 : : }
162 : :
163 : 181 : void impl_call_post (const call_details &cd) const final override
164 : : {
165 : 181 : region_model *model = cd.get_model ();
166 : 181 : const svalue *ptr_sval = cd.get_arg_svalue (0);
167 : 181 : if (const region *freed_reg = ptr_sval->maybe_get_region ())
168 : : {
169 : : /* If the ptr points to an underlying heap region, delete it,
170 : : poisoning pointers. */
171 : 162 : model->unbind_region_and_descendents (freed_reg,
172 : : POISON_KIND_DELETED);
173 : : }
174 : 181 : }
175 : :
176 : : };
177 : :
178 : : /* Populate KFM with instances of known functions relating to C++. */
179 : :
180 : : void
181 : 3197 : register_known_functions_lang_cp (known_function_manager &kfm)
182 : : {
183 : 3197 : kfm.add ("operator new", make_unique<kf_operator_new> ());
184 : 3197 : kfm.add ("operator new []", make_unique<kf_operator_new> ());
185 : 3197 : kfm.add ("operator delete", make_unique<kf_operator_delete> ());
186 : 3197 : kfm.add ("operator delete []", make_unique<kf_operator_delete> ());
187 : 3197 : }
188 : :
189 : : } // namespace ana
190 : :
191 : : #endif /* #if ENABLE_ANALYZER */
|