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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#define INCLUDE_MEMORY
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
46bool is_placement_new_p (const gcall *call)
47{
48 gcc_assert (call);
49 tree fndecl = gimple_call_fndecl (gs: call);
50
51 if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
52 /* Give up on overloaded operator new. */
53 return false;
54
55 if (!is_named_call_p (fndecl, funcname: "operator new", call, num_args: 2)
56 && !is_named_call_p (fndecl, funcname: "operator new []", call, num_args: 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 tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
67 return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
68}
69
70namespace ana {
71
72/* Implementations of specific functions. */
73
74/* Handler for "operator new" and "operator new []". */
75
76class kf_operator_new : public known_function
77{
78public:
79 bool matches_call_types_p (const call_details &cd) const final override
80 {
81 return (cd.num_args () == 1
82 && cd.arg_is_size_p (idx: 0))
83 || (cd.num_args () == 2
84 && cd.arg_is_size_p (idx: 0)
85 && POINTER_TYPE_P (cd.get_arg_type (1)));
86 }
87
88 void impl_call_pre (const call_details &cd) const final override
89 {
90 region_model *model = cd.get_model ();
91 region_model_manager *mgr = cd.get_manager ();
92 const svalue *size_sval = cd.get_arg_svalue (idx: 0);
93 region_model_context *ctxt = cd.get_ctxt ();
94 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 if (is_placement_new_p (call))
99 {
100 const region *ptr_reg = cd.deref_ptr_arg (idx: 1);
101 if (ptr_reg && cd.get_lhs_type ())
102 {
103 const svalue *num_bytes_sval = cd.get_arg_svalue (idx: 0);
104 const region *sized_new_reg
105 = mgr->get_sized_region (parent: ptr_reg,
106 type: cd.get_lhs_type (),
107 byte_size_sval: num_bytes_sval);
108 model->check_region_for_write (dest_reg: sized_new_reg,
109 sval_hint: nullptr,
110 ctxt);
111 const svalue *ptr_sval
112 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: sized_new_reg);
113 cd.maybe_set_lhs (result: ptr_sval);
114 }
115 }
116 /* If the call is an allocating new, then create a heap allocated
117 region. */
118 else
119 {
120 const region *new_reg
121 = model->get_or_create_region_for_heap_alloc (size_in_bytes: size_sval, ctxt);
122 if (cd.get_lhs_type ())
123 {
124 const svalue *ptr_sval
125 = mgr->get_ptr_svalue (ptr_type: cd.get_lhs_type (), pointee: new_reg);
126 cd.maybe_set_lhs (result: ptr_sval);
127 }
128 }
129 }
130
131 void impl_call_post (const call_details &cd) const final override
132 {
133 region_model *model = cd.get_model ();
134 region_model_manager *mgr = cd.get_manager ();
135 tree callee_fndecl = cd.get_fndecl_for_call ();
136 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 if (!TREE_NOTHROW (callee_fndecl) && flag_exceptions)
141 {
142 const svalue *null_sval
143 = mgr->get_or_create_null_ptr (pointer_type: cd.get_lhs_type ());
144 const svalue *result
145 = model->get_store_value (reg: cd.get_lhs_region (), ctxt);
146 model->add_constraint (lhs: result, op: NE_EXPR, rhs: null_sval, ctxt);
147 }
148 }
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
155class kf_operator_delete : public known_function
156{
157public:
158 bool matches_call_types_p (const call_details &cd) const final override
159 {
160 return cd.num_args () == 1 or cd.num_args () == 2;
161 }
162
163 void impl_call_post (const call_details &cd) const final override
164 {
165 region_model *model = cd.get_model ();
166 const svalue *ptr_sval = cd.get_arg_svalue (idx: 0);
167 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 model->unbind_region_and_descendents (reg: freed_reg,
172 pkind: POISON_KIND_DELETED);
173 }
174 }
175
176};
177
178/* Populate KFM with instances of known functions relating to C++. */
179
180void
181register_known_functions_lang_cp (known_function_manager &kfm)
182{
183 kfm.add (name: "operator new", kf: make_unique<kf_operator_new> ());
184 kfm.add (name: "operator new []", kf: make_unique<kf_operator_new> ());
185 kfm.add (name: "operator delete", kf: make_unique<kf_operator_delete> ());
186 kfm.add (name: "operator delete []", kf: make_unique<kf_operator_delete> ());
187}
188
189} // namespace ana
190
191#endif /* #if ENABLE_ANALYZER */
192

source code of gcc/analyzer/kf-lang-cp.cc