]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cbdata.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / cbdata.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 45 Callback Data Registry */
10
11 #include "squid.h"
12 #include "cbdata.h"
13 #include "Generic.h"
14 #include "mem/Pool.h"
15 #include "mgr/Registration.h"
16 #include "Store.h"
17
18 #include <climits>
19 #include <cstddef>
20
21 #if WITH_VALGRIND
22 #include <map>
23 #endif
24
25 static int cbdataCount = 0;
26
27 /**
28 * Manage a set of registered callback data pointers.
29 * One of the easiest ways to make Squid coredump is to issue a
30 * callback to for some data structure which has previously been
31 * freed. With this class, we register (add) callback data
32 * pointers, lock them just before registering the callback function,
33 * validate them before issuing the callback, and then free them
34 * when finished.
35 */
36 class cbdata
37 {
38 #if !WITH_VALGRIND
39 public:
40 void *operator new(size_t, void *where) {return where;}
41 /**
42 * Only ever invoked when placement new throws
43 * an exception. Used to prevent an incorrect free.
44 */
45 void operator delete(void *, void *) {}
46 #else
47 MEMPROXY_CLASS(cbdata);
48 #endif
49
50 /* TODO: examine making cbdata templated on this - so we get type
51 * safe access to data - RBC 20030902 */
52 public:
53 cbdata() :
54 valid(0),
55 locks(0),
56 type(CBDATA_UNKNOWN),
57 cookie(0),
58 data(nullptr)
59 {}
60 ~cbdata();
61
62 static cbdata *FromUserData(const void *);
63
64 int valid;
65 int32_t locks;
66 cbdata_type type;
67
68 /* cookie used while debugging */
69 long cookie;
70 void check(int) const {assert(cookie == ((long)this ^ Cookie));}
71 static const long Cookie;
72
73 #if !WITH_VALGRIND
74 size_t dataSize() const { return sizeof(data);}
75 #endif
76 /* MUST be the last per-instance member */
77 void *data;
78 };
79
80 static_assert(std::is_standard_layout<cbdata>::value, "the behavior of offsetof(cbdata) is defined");
81
82 const long cbdata::Cookie((long)0xDEADBEEF);
83
84 struct CBDataIndex {
85 Mem::Allocator *pool;
86 }
87 *cbdata_index = nullptr;
88
89 int cbdata_types = 0;
90
91 #if WITH_VALGRIND
92 static std::map<const void *, cbdata *> cbdata_htable;
93 #endif
94
95 cbdata::~cbdata()
96 {
97 #if WITH_VALGRIND
98 void *p = data;
99 #else
100 void *p = this;
101 #endif
102 cbdata_index[type].pool->freeOne(p);
103 }
104
105 cbdata *
106 cbdata::FromUserData(const void *p) {
107 #if WITH_VALGRIND
108 return cbdata_htable.at(p);
109 #else
110 const auto t = static_cast<const char *>(p) - offsetof(cbdata, data);
111 return reinterpret_cast<cbdata *>(const_cast<char *>(t));
112 #endif
113 }
114
115 cbdata_type
116 cbdataInternalAddType(cbdata_type type, const char *name, int size)
117 {
118 if (type)
119 return type;
120
121 type = (cbdata_type)(cbdata_types + 1);
122
123 char *label;
124 assert (type == cbdata_types + 1);
125
126 cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
127 memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
128 cbdata_types = type;
129
130 label = (char *)xmalloc(strlen(name) + 20);
131
132 snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
133
134 #if !WITH_VALGRIND
135 size += offsetof(cbdata, data);
136 #endif
137
138 cbdata_index[type].pool = memPoolCreate(label, size);
139
140 return type;
141 }
142
143 void *
144 cbdataInternalAlloc(cbdata_type type)
145 {
146 cbdata *c;
147 void *p;
148 assert(type > 0 && type <= cbdata_types);
149 /* placement new: the pool alloc gives us cbdata + user type memory space
150 * and we init it with cbdata at the start of it
151 */
152 #if WITH_VALGRIND
153 c = new cbdata;
154 p = cbdata_index[type].pool->alloc();
155 c->data = p;
156 cbdata_htable.emplace(p,c);
157 #else
158 c = new (cbdata_index[type].pool->alloc()) cbdata;
159 p = (void *)&c->data;
160 #endif
161
162 c->type = type;
163 c->valid = 1;
164 c->locks = 0;
165 c->cookie = (long) c ^ cbdata::Cookie;
166 ++cbdataCount;
167 debugs(45, 9, "Allocating " << p);
168 return p;
169 }
170
171 static void
172 cbdataRealFree(cbdata *c)
173 {
174 #if WITH_VALGRIND
175 void *p = c->data;
176 #else
177 void *p = (void *)&c->data;
178 #endif
179
180 --cbdataCount;
181 debugs(45, 9, "Freeing " << p);
182
183 #if WITH_VALGRIND
184 cbdata_htable.erase(p);
185 delete c;
186 #else
187 /* This is ugly. But: operator delete doesn't get
188 * the type parameter, so we can't use that
189 * to free the memory.
190 * So, we free it ourselves.
191 * Note that this means a non-placement
192 * new would be a seriously bad idea.
193 * Lastly, if we where a templated class,
194 * we could use the normal delete operator
195 * and it would Just Work. RBC 20030902
196 */
197 c->cbdata::~cbdata();
198 #endif
199 }
200
201 void *
202 cbdataInternalFree(void *p)
203 {
204 auto *c = cbdata::FromUserData(p);
205
206 c->check(__LINE__);
207 assert(c->valid);
208 c->valid = 0;
209
210 if (c->locks) {
211 debugs(45, 9, p << " has " << c->locks << " locks, not freeing");
212 return nullptr;
213 }
214
215 cbdataRealFree(c);
216 return nullptr;
217 }
218
219 void
220 cbdataInternalLock(const void *p)
221 {
222 if (p == nullptr)
223 return;
224
225 auto *c = cbdata::FromUserData(p);
226
227 debugs(45, 9, p << "=" << (c ? c->locks + 1 : -1));
228
229 c->check(__LINE__);
230
231 assert(c->locks < INT_MAX);
232
233 ++ c->locks;
234 }
235
236 void
237 cbdataInternalUnlock(const void *p)
238 {
239 if (p == nullptr)
240 return;
241
242 auto *c = cbdata::FromUserData(p);
243
244 debugs(45, 9, p << "=" << (c ? c->locks - 1 : -1));
245
246 c->check(__LINE__);
247
248 assert(c != nullptr);
249
250 assert(c->locks > 0);
251
252 -- c->locks;
253
254 if (c->locks)
255 return;
256
257 if (c->valid)
258 return;
259
260 cbdataRealFree(c);
261 }
262
263 int
264 cbdataReferenceValid(const void *p)
265 {
266 if (p == nullptr)
267 return 1; /* A NULL pointer cannot become invalid */
268
269 debugs(45, 9, p);
270
271 const auto c = cbdata::FromUserData(p);
272
273 c->check(__LINE__);
274
275 assert(c->locks > 0);
276
277 return c->valid;
278 }
279
280 int
281 cbdataInternalReferenceDoneValid(void **pp, void **tp)
282 {
283 void *p = (void *) *pp;
284 int valid = cbdataReferenceValid(p);
285 *pp = nullptr;
286
287 cbdataInternalUnlock(p);
288
289 if (valid) {
290 *tp = p;
291 return 1;
292 } else {
293 *tp = nullptr;
294 return 0;
295 }
296 }
297
298 CallbackData &
299 CallbackData::operator =(const CallbackData &other)
300 {
301 if (data_ != other.data_) { // assignment to self and no-op assignments
302 auto old = data_;
303 data_ = cbdataReference(other.data_);
304 cbdataReferenceDone(old);
305 }
306 return *this;
307 }
308
309 CBDATA_CLASS_INIT(generic_cbdata);
310