/src/freeradius-server/src/lib/util/talloc.c
Line | Count | Source |
1 | | /* |
2 | | * This library is free software; you can redistribute it and/or |
3 | | * modify it under the terms of the GNU Lesser General Public |
4 | | * License as published by the Free Software Foundation; either |
5 | | * version 2.1 of the License, or (at your option) any later version. |
6 | | * |
7 | | * This library is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
10 | | * Lesser General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU Lesser General Public |
13 | | * License along with this library; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** Functions which we wish were included in the standard talloc distribution |
18 | | * |
19 | | * @file src/lib/util/talloc.c |
20 | | * |
21 | | * @copyright 2017 The FreeRADIUS server project |
22 | | * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
23 | | */ |
24 | | RCSID("$Id: 9780726b4675f93cc2f0d270819ec5825ee44210 $") |
25 | | |
26 | | #include <freeradius-devel/util/atexit.h> |
27 | | #include <freeradius-devel/util/debug.h> |
28 | | #include <freeradius-devel/util/dlist.h> |
29 | | #include <freeradius-devel/util/syserror.h> |
30 | | |
31 | | #ifndef TALLOC_EXTENSIONS |
32 | | |
33 | | static TALLOC_CTX *global_ctx; |
34 | | static _Thread_local TALLOC_CTX *thread_local_ctx; |
35 | | |
36 | | /** A wrapper that can be passed to tree or hash alloc functions that take a #fr_free_t |
37 | | */ |
38 | | void talloc_free_data(void *data) |
39 | 0 | { |
40 | 0 | talloc_free(data); |
41 | 0 | } |
42 | | |
43 | | /** Retrieve the current talloc NULL ctx |
44 | | * |
45 | | * Talloc doesn't provide a function to retrieve the top level memory tracking context. |
46 | | * This function does that... |
47 | | * |
48 | | * @return the current talloc NULL context or NULL if memory tracking is not enabled. |
49 | | */ |
50 | | void *talloc_null_ctx(void) |
51 | 0 | { |
52 | 0 | TALLOC_CTX *null_ctx; |
53 | 0 | bool *tmp; |
54 | |
|
55 | 0 | tmp = talloc(NULL, bool); |
56 | 0 | null_ctx = talloc_parent(tmp); |
57 | 0 | talloc_free(tmp); |
58 | |
|
59 | 0 | return null_ctx; |
60 | 0 | } |
61 | | |
62 | | /** Called with the fire_ctx is freed |
63 | | * |
64 | | */ |
65 | | static int _talloc_destructor_fire(fr_talloc_destructor_t *d) |
66 | 20 | { |
67 | 20 | if (d->ds) { |
68 | 20 | talloc_set_destructor(d->ds, NULL); /* Disarm the disarmer */ |
69 | 20 | TALLOC_FREE(d->ds); /* Free the disarm trigger ctx */ |
70 | 20 | } |
71 | | |
72 | 20 | return d->func(d->fire, d->uctx); |
73 | 20 | } |
74 | | |
75 | | /** Called when the disarm_ctx ctx is freed |
76 | | * |
77 | | */ |
78 | | static int _talloc_destructor_disarm(fr_talloc_destructor_disarm_t *ds) |
79 | 0 | { |
80 | 0 | talloc_set_destructor(ds->d, NULL); /* Disarm the destructor */ |
81 | 0 | return talloc_free(ds->d); /* Free memory allocated to the destructor */ |
82 | 0 | } |
83 | | |
84 | | /** Add an additional destructor to a talloc chunk |
85 | | * |
86 | | * @param[in] fire_ctx When this ctx is freed the destructor function |
87 | | * will be called. |
88 | | * @param[in] disarm_ctx When this ctx is freed the destructor will be |
89 | | * disarmed. May be NULL. #talloc_destructor_disarm |
90 | | * may be used to disarm the destructor too. |
91 | | * @param[in] func to call when the fire_ctx is freed. |
92 | | * @param[in] uctx data to pass to the above function. |
93 | | * @return |
94 | | * - A handle to access the destructor on success. |
95 | | * - NULL on failure. |
96 | | */ |
97 | | fr_talloc_destructor_t *talloc_destructor_add(TALLOC_CTX *fire_ctx, TALLOC_CTX *disarm_ctx, |
98 | | fr_talloc_free_func_t func, void const *uctx) |
99 | 42 | { |
100 | 42 | fr_talloc_destructor_t *d; |
101 | | |
102 | 42 | if (!fire_ctx) { |
103 | 0 | fr_strerror_const("No firing ctx provided when setting destructor"); |
104 | 0 | return NULL; |
105 | 0 | } |
106 | | |
107 | 42 | d = talloc_zero(fire_ctx, fr_talloc_destructor_t); |
108 | 42 | if (!d) { |
109 | 0 | oom: |
110 | 0 | fr_strerror_const("Out of Memory"); |
111 | 0 | return NULL; |
112 | 0 | } |
113 | | |
114 | 42 | d->fire = fire_ctx; |
115 | 42 | d->func = func; |
116 | 42 | memcpy(&d->uctx, &uctx, sizeof(d->uctx)); |
117 | | |
118 | 42 | if (disarm_ctx) { |
119 | 42 | fr_talloc_destructor_disarm_t *ds; |
120 | | |
121 | 42 | ds = talloc(disarm_ctx, fr_talloc_destructor_disarm_t); |
122 | 42 | if (!ds) { |
123 | 0 | talloc_free(d); |
124 | 0 | goto oom; |
125 | 0 | } |
126 | 42 | ds->d = d; |
127 | 42 | d->ds = ds; |
128 | 42 | talloc_set_destructor(ds, _talloc_destructor_disarm); |
129 | 42 | } |
130 | | |
131 | 42 | talloc_set_destructor(d, _talloc_destructor_fire); |
132 | | |
133 | 42 | return d; |
134 | 42 | } |
135 | | |
136 | | /** Disarm a destructor and free all memory allocated in the trigger ctxs |
137 | | * |
138 | | */ |
139 | | void talloc_destructor_disarm(fr_talloc_destructor_t *d) |
140 | 0 | { |
141 | 0 | if (d->ds) { |
142 | 0 | talloc_set_destructor(d->ds, NULL); /* Disarm the disarmer */ |
143 | 0 | TALLOC_FREE(d->ds); /* Free the disarmer ctx */ |
144 | 0 | } |
145 | |
|
146 | 0 | talloc_set_destructor(d, NULL); /* Disarm the destructor */ |
147 | 0 | talloc_free(d); /* Free the destructor ctx */ |
148 | 0 | } |
149 | | |
150 | | static int _talloc_link_ctx_free(UNUSED void *parent, void *child) |
151 | 20 | { |
152 | 20 | talloc_free(child); |
153 | | |
154 | 20 | return 0; |
155 | 20 | } |
156 | | |
157 | | /** Link two different parent and child contexts, so the child is freed before the parent |
158 | | * |
159 | | * @note This is not thread safe. Do not free parent before threads are joined, do not call from a |
160 | | * child thread. |
161 | | * |
162 | | * @param parent who's fate the child should share. |
163 | | * @param child bound to parent's lifecycle. |
164 | | * @return |
165 | | * - 0 on success. |
166 | | * - -1 on failure. |
167 | | */ |
168 | | int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child) |
169 | 42 | { |
170 | 42 | if (!talloc_destructor_add(parent, child, _talloc_link_ctx_free, child)) return -1; |
171 | | |
172 | 42 | return 0; |
173 | 42 | } |
174 | | |
175 | | /** Return a page aligned talloc memory array |
176 | | * |
177 | | * Because we can't intercept talloc's malloc() calls, we need to do some tricks |
178 | | * in order to get the first allocation in the array page aligned, and to limit |
179 | | * the size of the array to a multiple of the page size. |
180 | | * |
181 | | * The reason for wanting a page aligned talloc array, is it allows us to |
182 | | * mprotect() the pages that belong to the array. |
183 | | * |
184 | | * Talloc chunks appear to be allocated within the protected region, so this should |
185 | | * catch frees too. |
186 | | * |
187 | | * @param[in] ctx to allocate array memory in. |
188 | | * @param[out] start The first aligned address in the array. |
189 | | * @param[in] alignment What alignment the memory chunk should have. |
190 | | * @param[in] size How big to make the array. Will be corrected to a multiple |
191 | | * of the page size. The actual array size will be size |
192 | | * rounded to a multiple of the (page_size), + page_size |
193 | | * @return |
194 | | * - A talloc chunk on success. |
195 | | * - NULL on failure. |
196 | | */ |
197 | | TALLOC_CTX *talloc_aligned_array(TALLOC_CTX *ctx, void **start, size_t alignment, size_t size) |
198 | 0 | { |
199 | 0 | size_t rounded; |
200 | 0 | size_t array_size; |
201 | 0 | void *next; |
202 | 0 | TALLOC_CTX *array; |
203 | |
|
204 | 0 | rounded = ROUND_UP(size, alignment); /* Round up to a multiple of the page size */ |
205 | 0 | if (rounded == 0) rounded = alignment; |
206 | |
|
207 | 0 | array_size = rounded + alignment; |
208 | 0 | array = talloc_array(ctx, uint8_t, array_size); /* Over allocate */ |
209 | 0 | if (!array) { |
210 | 0 | fr_strerror_const("Out of memory"); |
211 | 0 | return NULL; |
212 | 0 | } |
213 | | |
214 | 0 | next = (void *)ROUND_UP((uintptr_t)array, alignment); /* Round up address to the next multiple */ |
215 | 0 | *start = next; |
216 | |
|
217 | 0 | return array; |
218 | 0 | } |
219 | | |
220 | | /** How big the chunk header is |
221 | | * |
222 | | * Non-zero portion will always fit in a uint8_t, so we don't need to worry about atomicity. |
223 | | */ |
224 | | static size_t t_hdr_size; |
225 | | |
226 | | /** Calculate the size of talloc chunk headers, once, on startup |
227 | | * |
228 | | */ |
229 | | static void _talloc_hdr_size(void) |
230 | | { |
231 | | TALLOC_CTX *pool; |
232 | | void *chunk; |
233 | | |
234 | | pool = talloc_pool(NULL, 1024); /* Allocate 1k of memory, this will always be bigger than the chunk header */ |
235 | | if (unlikely(pool == NULL)) { |
236 | | oom: |
237 | | fr_strerror_const("Out of memory"); |
238 | | return; |
239 | | } |
240 | | chunk = talloc_size(pool, 1); /* Get the starting address */ |
241 | | if (unlikely(chunk == NULL)) goto oom; |
242 | | |
243 | | /* |
244 | | * Sanity check... make sure the chunk is within the pool |
245 | | * if it's not, we're in trouble. |
246 | | */ |
247 | | if (!fr_cond_assert((chunk > pool) && ((uintptr_t)chunk < ((uintptr_t)pool + 1024)))) { |
248 | | fr_strerror_const("Initial allocation outside of pool memory"); |
249 | | return; |
250 | | } |
251 | | |
252 | | /* |
253 | | * Talloc returns the address after the chunk header, so |
254 | | * the address of the pool is <malloc address> + <hdr_size>. |
255 | | * |
256 | | * If we allocate a chunk inside the pool, then the address |
257 | | * of the chunk will be <malloc address> + <hdr_size> + <hdr_size>. |
258 | | * If we subtrace the chunk from the pool address, we get |
259 | | * the size of the talloc header. |
260 | | */ |
261 | | t_hdr_size = (uintptr_t)chunk - (uintptr_t)pool; |
262 | | |
263 | | talloc_free(pool); /* Free the memory we used */ |
264 | | } |
265 | | |
266 | | /** Calculate the size of the talloc chunk header |
267 | | * |
268 | | * @return |
269 | | * - >0 the size of the talloc chunk header. |
270 | | * - -1 on error. |
271 | | */ |
272 | | ssize_t talloc_hdr_size(void) |
273 | 0 | { |
274 | 0 | static pthread_once_t once_control = PTHREAD_ONCE_INIT; |
275 | |
|
276 | 0 | if (t_hdr_size > 0) return t_hdr_size; /* We've already calculated it */ |
277 | | |
278 | 0 | if (pthread_once(&once_control, _talloc_hdr_size) != 0) { |
279 | 0 | fr_strerror_printf("pthread_once failed: %s", fr_syserror(errno)); |
280 | 0 | return -1; |
281 | 0 | } |
282 | 0 | if (t_hdr_size == 0) return -1; /* Callback should set error */ |
283 | | |
284 | 0 | return t_hdr_size; |
285 | 0 | } |
286 | | |
287 | | /** Return a page aligned talloc memory pool |
288 | | * |
289 | | * Because we can't intercept talloc's malloc() calls, we need to do some tricks |
290 | | * in order to get the first allocation in the pool page aligned, and to limit |
291 | | * the size of the pool to a multiple of the page size. |
292 | | * |
293 | | * The reason for wanting a page aligned talloc pool, is it allows us to |
294 | | * mprotect() the pages that belong to the pool. |
295 | | * |
296 | | * Talloc chunks appear to be allocated within the protected region, so this should |
297 | | * catch frees too. |
298 | | * |
299 | | * @param[in] ctx to allocate pool memory in. |
300 | | * @param[out] start A page aligned address within the pool. This can be passed |
301 | | * to mprotect(). |
302 | | * @param[out] end_len how many bytes to protect. |
303 | | * @param[in] headers how much room we should allocate for talloc headers. |
304 | | * This value should usually be >= 1. |
305 | | * @param[in] size How big to make the pool. Will be corrected to a multiple |
306 | | * of the page size. The actual pool size will be size |
307 | | * rounded to a multiple of the (page_size), + page_size |
308 | | */ |
309 | | TALLOC_CTX *talloc_page_aligned_pool(TALLOC_CTX *ctx, void **start, size_t *end_len, unsigned int headers, size_t size) |
310 | 0 | { |
311 | 0 | size_t rounded, alloced, page_size = (size_t)getpagesize(); |
312 | 0 | ssize_t hdr_size; |
313 | 0 | void *next; |
314 | 0 | TALLOC_CTX *pool; |
315 | |
|
316 | 0 | hdr_size = talloc_hdr_size(); |
317 | 0 | if (hdr_size < 0) return NULL; |
318 | | |
319 | 0 | size += (hdr_size * headers); /* Add more space for the chunks headers of the pool's children */ |
320 | 0 | size += hdr_size; /* Add one more header to the pool for the padding allocation */ |
321 | | |
322 | | /* |
323 | | * Allocate enough space for the padding header the other headers |
324 | | * and the actual data, and sure it's a multiple of the page_size. |
325 | | * |
326 | | * |<--- page_size --->|<-- page_size -->| |
327 | | * |<-- hdr_size -->|<-- size -->| |
328 | | */ |
329 | 0 | if (size % page_size == 0) { |
330 | 0 | rounded = size; |
331 | 0 | } else { |
332 | 0 | rounded = ROUND_UP(size, page_size); |
333 | 0 | } |
334 | | |
335 | | /* |
336 | | * Add another page, so that we can align the first allocation in |
337 | | * the pool to a page boundary. We have no idea what chunk malloc |
338 | | * will give to talloc, and what the first address after adding the |
339 | | * pools header will be |
340 | | */ |
341 | 0 | alloced = rounded + page_size; |
342 | 0 | pool = talloc_pool(ctx, alloced); |
343 | 0 | if (!pool) { |
344 | 0 | fr_strerror_const("Out of memory"); |
345 | 0 | return NULL; |
346 | 0 | } |
347 | | |
348 | | /* |
349 | | * If we didn't get lucky, and the first address in the pool is |
350 | | * not the start of a page, we need to allocate some padding to |
351 | | * get the first allocation in the pool to be on or after the |
352 | | * start of the next page. |
353 | | */ |
354 | 0 | if ((uintptr_t)pool % page_size != 0) { |
355 | 0 | size_t pad_size; |
356 | 0 | void *padding; |
357 | |
|
358 | 0 | next = (void *)ROUND_UP((uintptr_t)pool, page_size); /* Round up address to the next page */ |
359 | | |
360 | | /* |
361 | | * We don't care if the first address if on a page |
362 | | * boundary, just that it comes after one. |
363 | | */ |
364 | 0 | pad_size = ((uintptr_t)next - (uintptr_t)pool); |
365 | 0 | if (pad_size > (size_t) hdr_size) { |
366 | 0 | pad_size -= hdr_size; /* Save ~111 bytes by not over-padding */ |
367 | 0 | } else { |
368 | 0 | pad_size = 0; /* Allocate as few bytes as possible */ |
369 | 0 | } |
370 | |
|
371 | 0 | padding = talloc_size(pool, pad_size); |
372 | 0 | if (!fr_cond_assert(((uintptr_t)padding + (uintptr_t)pad_size) >= (uintptr_t)next)) { |
373 | 0 | fr_strerror_const("Failed padding pool memory"); |
374 | 0 | return NULL; |
375 | 0 | } |
376 | 0 | } else { |
377 | 0 | next = pool; |
378 | 0 | } |
379 | | |
380 | 0 | *start = next; /* This is the address we feed into mprotect */ |
381 | 0 | *end_len = rounded; /* This is how much memory we protect */ |
382 | | |
383 | | /* |
384 | | * Start address must be page aligned |
385 | | */ |
386 | 0 | fr_assert(((uintptr_t)*start % page_size) == 0); |
387 | | |
388 | | /* |
389 | | * For our sanity, length must be a multiple of the page size. |
390 | | * mprotect will silently protect an entire additional page |
391 | | * if length is not a multiple of page size. |
392 | | */ |
393 | 0 | fr_assert(((uintptr_t)*end_len % page_size) == 0); |
394 | | |
395 | | /* |
396 | | * We can't protect pages before the pool |
397 | | */ |
398 | 0 | fr_assert(*start >= pool); |
399 | | |
400 | | /* |
401 | | * We can't protect pages after the pool |
402 | | */ |
403 | 0 | fr_assert(((uintptr_t)*start + *end_len) <= ((uintptr_t)pool + alloced)); |
404 | |
|
405 | 0 | return pool; |
406 | 0 | } |
407 | | |
408 | | /** Version of talloc_realloc which zeroes out freshly allocated memory |
409 | | * |
410 | | * @param[in] ctx talloc ctx to allocate in. |
411 | | * @param[in] ptr the pointer to the old memory. |
412 | | * @param[in] elem_size the size of each element in the array. |
413 | | * @param[in] count the number of elements in the array. |
414 | | * @param[in] name the name of the new chunk. |
415 | | * @return |
416 | | * - A pointer to the new memory. |
417 | | * - NULL on error. |
418 | | */ |
419 | | void *_talloc_realloc_zero(const void *ctx, void *ptr, size_t elem_size, unsigned count, const char *name) |
420 | 0 | { |
421 | 0 | size_t old_size = talloc_get_size(ptr); |
422 | 0 | size_t new_size; |
423 | |
|
424 | 0 | void *new = _talloc_realloc_array(ctx, ptr, elem_size, count, name); |
425 | 0 | if (!new) return NULL; |
426 | | |
427 | 0 | new_size = talloc_array_length((uint8_t *) new); |
428 | 0 | if (new_size > old_size) { |
429 | 0 | memset((uint8_t *)new + old_size, 0, new_size - old_size); |
430 | 0 | } |
431 | |
|
432 | 0 | return new; |
433 | 0 | } |
434 | | |
435 | | /** Call talloc_memdup, setting the type on the new chunk correctly |
436 | | * |
437 | | * @param[in] ctx The talloc context to hang the result off. |
438 | | * @param[in] in The data you want to duplicate. |
439 | | * @param[in] inlen the length of the data to be duplicated. |
440 | | * @return |
441 | | * - Duplicated data. |
442 | | * - NULL on error. |
443 | | * |
444 | | * @hidecallergraph |
445 | | */ |
446 | | uint8_t *talloc_typed_memdup(TALLOC_CTX *ctx, uint8_t const *in, size_t inlen) |
447 | 93 | { |
448 | 93 | uint8_t *n; |
449 | | |
450 | 93 | n = talloc_memdup(ctx, in, inlen); |
451 | 93 | if (unlikely(!n)) return NULL; |
452 | 93 | talloc_set_type(n, uint8_t); |
453 | | |
454 | 93 | return n; |
455 | 93 | } |
456 | | |
457 | | /** Call talloc_strdup, setting the type on the new chunk correctly |
458 | | * |
459 | | * For some bizarre reason the talloc string functions don't set the |
460 | | * memory chunk type to char, which causes all kinds of issues with |
461 | | * verifying fr_pair_ts. |
462 | | * |
463 | | * @param[in] ctx The talloc context to hang the result off. |
464 | | * @param[in] p The string you want to duplicate. |
465 | | * @return |
466 | | * - Duplicated string. |
467 | | * - NULL on error. |
468 | | * |
469 | | * @hidecallergraph |
470 | | */ |
471 | | char *talloc_typed_strdup(TALLOC_CTX *ctx, char const *p) |
472 | 18.6M | { |
473 | 18.6M | char *n; |
474 | | |
475 | 18.6M | #undef talloc_strdup |
476 | 18.6M | n = talloc_strdup(ctx, p); |
477 | 18.6M | if (unlikely(!n)) return NULL; |
478 | 18.6M | talloc_set_type(n, char); |
479 | | |
480 | 18.6M | return n; |
481 | 18.6M | } |
482 | | |
483 | | /** Call talloc_strndup, setting the type on the new chunk correctly |
484 | | * |
485 | | * This function is similar to talloc_typed_strdup but gets the chunk |
486 | | * length using talloc functions. |
487 | | * |
488 | | * @param[in] ctx The talloc context to hang the result off. |
489 | | * @param[in] p The string you want to duplicate. |
490 | | * @return |
491 | | * - Duplicated string. |
492 | | * - NULL on error. |
493 | | * |
494 | | * @hidecallergraph |
495 | | */ |
496 | | char *talloc_typed_strdup_buffer(TALLOC_CTX *ctx, char const *p) |
497 | 0 | { |
498 | 0 | char *n; |
499 | |
|
500 | 0 | n = talloc_strndup(ctx, p, talloc_strlen(p)); |
501 | 0 | if (unlikely(!n)) return NULL; |
502 | 0 | talloc_set_type(n, char); |
503 | |
|
504 | 0 | return n; |
505 | 0 | } |
506 | | |
507 | | /** Call talloc_strndup, setting the type on the new chunk correctly |
508 | | * |
509 | | * For some bizarre reason the talloc string functions don't set the |
510 | | * memory chunk type to char, which causes all kinds of issues with |
511 | | * verifying fr_pair_ts. |
512 | | * |
513 | | * @param[in] ctx The talloc context to hang the result off. |
514 | | * @param[in] p The string you want to duplicate. |
515 | | * @param[in] len The length of the string |
516 | | * @return |
517 | | * - Duplicated string. |
518 | | * - NULL on error. |
519 | | * |
520 | | * @hidecallergraph |
521 | | */ |
522 | | char *talloc_typed_strndup(TALLOC_CTX *ctx, char const *p, size_t len) |
523 | 7.37k | { |
524 | 7.37k | char *n; |
525 | | |
526 | 7.37k | #undef talloc_strndup |
527 | 7.37k | n = talloc_strndup(ctx, p, len); |
528 | 7.37k | if (unlikely(!n)) return NULL; |
529 | 7.37k | talloc_set_type(n, char); |
530 | | |
531 | 7.37k | return n; |
532 | 7.37k | } |
533 | | |
534 | | /** Call talloc vasprintf, setting the type on the new chunk correctly |
535 | | * |
536 | | * For some bizarre reason the talloc string functions don't set the |
537 | | * memory chunk type to char, which causes all kinds of issues with |
538 | | * verifying fr_pair_ts. |
539 | | * |
540 | | * @param[in] ctx The talloc context to hang the result off. |
541 | | * @param[in] fmt The format string. |
542 | | * @return |
543 | | * - Formatted string. |
544 | | * - NULL on error. |
545 | | */ |
546 | | char *talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt, ...) |
547 | 975 | { |
548 | 975 | char *n; |
549 | 975 | va_list ap; |
550 | | |
551 | 975 | va_start(ap, fmt); |
552 | 975 | n = talloc_vasprintf(ctx, fmt, ap); |
553 | 975 | va_end(ap); |
554 | 975 | if (unlikely(!n)) return NULL; |
555 | 975 | talloc_set_type(n, char); |
556 | | |
557 | 975 | return n; |
558 | 975 | } |
559 | | |
560 | | /** Call talloc vasprintf, setting the type on the new chunk correctly |
561 | | * |
562 | | * For some bizarre reason the talloc string functions don't set the |
563 | | * memory chunk type to char, which causes all kinds of issues with |
564 | | * verifying fr_pair_ts. |
565 | | * |
566 | | * @param[in] ctx The talloc context to hang the result off. |
567 | | * @param[in] fmt The format string. |
568 | | * @param[in] ap varadic arguments. |
569 | | * @return |
570 | | * - Formatted string. |
571 | | * - NULL on error. |
572 | | */ |
573 | | char *talloc_typed_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap) |
574 | 0 | { |
575 | 0 | char *n; |
576 | |
|
577 | 0 | n = talloc_vasprintf(ctx, fmt, ap); |
578 | 0 | if (unlikely(!n)) return NULL; |
579 | 0 | talloc_set_type(n, char); |
580 | |
|
581 | 0 | return n; |
582 | 0 | } |
583 | | |
584 | | /** Binary safe strdup function |
585 | | * |
586 | | * @param[in] ctx the talloc context to allocate new buffer in. |
587 | | * @param[in] in String to dup, may contain embedded '\0'. |
588 | | * @return duped string. |
589 | | */ |
590 | | char *talloc_bstrdup(TALLOC_CTX *ctx, char const *in) |
591 | 0 | { |
592 | 0 | char *p; |
593 | 0 | size_t inlen = talloc_array_length(in); |
594 | |
|
595 | 0 | if (inlen == 0) inlen = 1; |
596 | |
|
597 | 0 | p = talloc_array(ctx, char, inlen); |
598 | 0 | if (unlikely(!p)) return NULL; |
599 | | |
600 | | /* |
601 | | * C99 (7.21.1/2) - Length zero results in noop |
602 | | * |
603 | | * But ubsan still flags this, grrr. |
604 | | */ |
605 | 0 | if (inlen > 0) memcpy(p, in, inlen - 1); |
606 | 0 | p[inlen - 1] = '\0'; |
607 | |
|
608 | 0 | return p; |
609 | 0 | } |
610 | | |
611 | | /** Binary safe strndup function |
612 | | * |
613 | | * @param[in] ctx he talloc context to allocate new buffer in. |
614 | | * @param[in] in String to dup, may contain embedded '\0'. |
615 | | * @param[in] inlen Number of bytes to dup. |
616 | | * @return duped string. |
617 | | */ |
618 | | char *talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen) |
619 | 45.9k | { |
620 | 45.9k | char *p; |
621 | | |
622 | 45.9k | p = talloc_array(ctx, char, inlen + 1); |
623 | 45.9k | if (unlikely(!p)) return NULL; |
624 | | |
625 | | /* |
626 | | * C99 (7.21.1/2) - Length zero results in noop |
627 | | * |
628 | | * But ubsan still flags this, grrr. |
629 | | */ |
630 | 45.9k | if (inlen > 0) memcpy(p, in, inlen); |
631 | 45.9k | p[inlen] = '\0'; |
632 | | |
633 | 45.9k | return p; |
634 | 45.9k | } |
635 | | |
636 | | /** Append a bstr to a bstr |
637 | | * |
638 | | * @param[in] ctx to allocated. |
639 | | * @param[in] to string to append to. |
640 | | * @param[in] from string to append from. |
641 | | * @param[in] from_len Length of from. |
642 | | * @return |
643 | | * - Realloced buffer containing both to and from. |
644 | | * - NULL on failure. To will still be valid. |
645 | | */ |
646 | | char *talloc_bstr_append(TALLOC_CTX *ctx, char *to, char const *from, size_t from_len) |
647 | 0 | { |
648 | 0 | char *n; |
649 | 0 | size_t to_len = 0; |
650 | |
|
651 | 0 | if (to) { |
652 | 0 | to_len = talloc_array_length(to); |
653 | 0 | if (to[to_len - 1] == '\0') to_len--; /* Inlen should be length of input string */ |
654 | 0 | } |
655 | |
|
656 | 0 | n = talloc_realloc_size(ctx, to, to_len + from_len + 1); |
657 | 0 | if (!n) { |
658 | 0 | fr_strerror_printf("Extending bstr buffer to %zu bytes failed", to_len + from_len + 1); |
659 | 0 | return NULL; |
660 | 0 | } |
661 | | |
662 | 0 | memcpy(n + to_len, from, from_len); |
663 | 0 | n[to_len + from_len] = '\0'; |
664 | 0 | talloc_set_type(n, char); |
665 | |
|
666 | 0 | return n; |
667 | 0 | } |
668 | | |
669 | | /** Trim a bstr (char) buffer |
670 | | * |
671 | | * Reallocs to inlen + 1 and '\0' terminates the string buffer. |
672 | | * |
673 | | * @param[in] ctx to realloc buffer into. |
674 | | * @param[in] in string to trim. Will be invalid after |
675 | | * this function returns. If NULL a new zero terminated |
676 | | * buffer of inlen bytes will be allocated. |
677 | | * @param[in] inlen Length to trim string to. |
678 | | * @return |
679 | | * - The realloced string on success. in then points to invalid memory. |
680 | | * - NULL on failure. In will still be valid. |
681 | | */ |
682 | | char *talloc_bstr_realloc(TALLOC_CTX *ctx, char *in, size_t inlen) |
683 | 0 | { |
684 | 0 | char *n; |
685 | |
|
686 | 0 | if (!in) { |
687 | 0 | n = talloc_array(ctx, char, inlen + 1); |
688 | 0 | if (!n) return NULL; |
689 | 0 | n[0] = '\0'; |
690 | 0 | return n; |
691 | 0 | } |
692 | | |
693 | 0 | n = talloc_realloc_size(ctx, in, inlen + 1); |
694 | 0 | if (unlikely(!n)) return NULL; |
695 | | |
696 | 0 | n[inlen] = '\0'; |
697 | 0 | talloc_set_type(n, char); |
698 | |
|
699 | 0 | return n; |
700 | 0 | } |
701 | | |
702 | | /** Concatenate to + ... |
703 | | * |
704 | | * @param[in] ctx to allocate realloced buffer in. |
705 | | * @param[in] to talloc string buffer to append to. |
706 | | * @param[in] argc how many variadic arguments were passed. |
707 | | * @param[in] ... talloc string buffer(s) to append. |
708 | | * Arguments can be NULL to simplify |
709 | | * calling logic. |
710 | | * @return |
711 | | * - NULL if to or from are NULL or if the realloc fails. |
712 | | * Note: You'll still need to free to if this function |
713 | | * returns NULL. |
714 | | * - The concatenation of to + from. After this function |
715 | | * returns to may point to invalid memory and should |
716 | | * not be used. |
717 | | */ |
718 | | char *talloc_buffer_append_variadic_buffer(TALLOC_CTX *ctx, char *to, int argc, ...) |
719 | 0 | { |
720 | 0 | va_list ap_val, ap_len; |
721 | 0 | int i; |
722 | |
|
723 | 0 | size_t to_len, total_len = 0; |
724 | 0 | char *out, *p; |
725 | |
|
726 | 0 | if (!to) return NULL; |
727 | | |
728 | 0 | va_start(ap_val, argc); |
729 | 0 | va_copy(ap_len, ap_val); |
730 | |
|
731 | 0 | total_len += to_len = talloc_strlen(to); |
732 | | |
733 | | /* |
734 | | * Figure out how much we need to realloc |
735 | | */ |
736 | 0 | for (i = 0; i < argc; i++) { |
737 | 0 | char *arg; |
738 | |
|
739 | 0 | arg = va_arg(ap_len, char *); |
740 | 0 | if (!arg) continue; |
741 | | |
742 | 0 | total_len += (talloc_strlen(arg)); |
743 | 0 | } |
744 | | |
745 | | /* |
746 | | * It's a noop... |
747 | | */ |
748 | 0 | if (total_len == to_len) { |
749 | 0 | va_end(ap_val); |
750 | 0 | va_end(ap_len); |
751 | 0 | return to; |
752 | 0 | } |
753 | | |
754 | 0 | out = talloc_realloc(ctx, to, char, total_len + 1); |
755 | 0 | if (!out) goto finish; |
756 | | |
757 | 0 | p = out + to_len; |
758 | | |
759 | | /* |
760 | | * Copy the args in |
761 | | */ |
762 | 0 | for (i = 0; i < argc; i++) { |
763 | 0 | char *arg; |
764 | 0 | size_t len; |
765 | |
|
766 | 0 | arg = va_arg(ap_val, char *); |
767 | 0 | if (!arg) continue; |
768 | | |
769 | 0 | len = talloc_strlen(arg); |
770 | |
|
771 | 0 | memcpy(p, arg, len); |
772 | 0 | p += len; |
773 | 0 | } |
774 | 0 | *p = '\0'; |
775 | |
|
776 | 0 | finish: |
777 | 0 | va_end(ap_val); |
778 | 0 | va_end(ap_len); |
779 | |
|
780 | 0 | return out; |
781 | 0 | } |
782 | | |
783 | | /** Compares two talloced char arrays with memcmp |
784 | | * |
785 | | * Talloc arrays carry their length as part of the structure, so can be passed to a generic |
786 | | * comparison function. |
787 | | * |
788 | | * @param a Pointer to first array. |
789 | | * @param b Pointer to second array. |
790 | | * @return |
791 | | * - 0 if the arrays match. |
792 | | * - a positive or negative integer otherwise. |
793 | | */ |
794 | | int talloc_memcmp_bstr(char const *a, char const *b) |
795 | 0 | { |
796 | 0 | size_t a_len, b_len; |
797 | |
|
798 | 0 | a_len = talloc_array_length(a); |
799 | 0 | b_len = talloc_array_length(b); |
800 | |
|
801 | 0 | if (a_len > b_len) return +1; |
802 | 0 | if (a_len < b_len) return -1; |
803 | | |
804 | 0 | return memcmp(a, b, a_len); |
805 | 0 | } |
806 | | |
807 | | /** Decrease the reference count on a ptr |
808 | | * |
809 | | * Ptr will be freed if count reaches zero. |
810 | | * |
811 | | * This is equivalent to talloc 1.0 behaviour of talloc_free. |
812 | | * |
813 | | * @param ptr to decrement ref count for. |
814 | | * @return |
815 | | * - 0 The memory was freed. |
816 | | * - >0 How many references remain. |
817 | | */ |
818 | | int talloc_decrease_ref_count(void const *ptr) |
819 | 38 | { |
820 | 38 | size_t ref_count; |
821 | 38 | void *to_free; |
822 | | |
823 | 38 | if (!ptr) return 0; |
824 | | |
825 | 38 | memcpy(&to_free, &ptr, sizeof(to_free)); |
826 | | |
827 | 38 | ref_count = talloc_reference_count(to_free); |
828 | 38 | if (ref_count == 0) { |
829 | 38 | talloc_free(to_free); |
830 | 38 | } else { |
831 | 0 | talloc_unlink(talloc_parent(ptr), to_free); |
832 | 0 | if (talloc_reference_count(to_free) == 0) talloc_free(to_free); |
833 | 0 | } |
834 | | |
835 | 38 | return ref_count; |
836 | 38 | } |
837 | | |
838 | | /** Add a NULL pointer to an array of pointers |
839 | | * |
840 | | * This is needed by some 3rd party libraries which take NULL terminated |
841 | | * arrays for arguments. |
842 | | * |
843 | | * If allocation fails, NULL will be returned and the original array |
844 | | * will not be touched. |
845 | | * |
846 | | * @param[in] array to null terminate. Will be invalidated (realloced). |
847 | | * @return |
848 | | * - NULL if array is NULL, or if reallocation fails. |
849 | | * - A realloced version of array with an additional NULL element. |
850 | | */ |
851 | | void **talloc_array_null_terminate(void **array) |
852 | 0 | { |
853 | 0 | size_t len; |
854 | 0 | TALLOC_CTX *ctx; |
855 | 0 | void **new; |
856 | 0 | size_t size; |
857 | |
|
858 | 0 | if (!array) return NULL; |
859 | | |
860 | 0 | len = talloc_array_length(array); |
861 | 0 | ctx = talloc_parent(array); |
862 | 0 | size = talloc_get_size(array) / talloc_array_length(array); |
863 | |
|
864 | 0 | new = _talloc_realloc_array(ctx, array, size, len + 1, talloc_get_name(array)); |
865 | 0 | if (!new) return NULL; |
866 | | |
867 | 0 | new[len] = NULL; |
868 | |
|
869 | 0 | return new; |
870 | 0 | } |
871 | | |
872 | | /** Callback to free the autofree ctx on global exit |
873 | | * |
874 | | */ |
875 | | static int _autofree_on_exit(void *af) |
876 | 0 | { |
877 | 0 | talloc_set_destructor(af, NULL); |
878 | 0 | return talloc_free(af); |
879 | 0 | } |
880 | | |
881 | | /** Ensures in the autofree ctx is manually freed, things don't explode atexit |
882 | | * |
883 | | */ |
884 | | static int _autofree_global_destructor(TALLOC_CTX *af) |
885 | 20 | { |
886 | 20 | return fr_atexit_global_disarm(true, _autofree_on_exit, af); |
887 | 20 | } |
888 | | |
889 | | TALLOC_CTX *talloc_autofree_context_global(void) |
890 | 20 | { |
891 | 20 | TALLOC_CTX *af = global_ctx; |
892 | | |
893 | 20 | if (!af) { |
894 | 20 | af = talloc_init_const("global_autofree_context"); |
895 | 20 | if (unlikely(!af)) return NULL; |
896 | | |
897 | 20 | talloc_set_destructor(af, _autofree_global_destructor); |
898 | | |
899 | 20 | fr_atexit_global(_autofree_on_exit, af); |
900 | 20 | global_ctx = af; |
901 | 20 | } |
902 | | |
903 | 20 | return af; |
904 | 20 | } |
905 | | |
906 | | /** Ensures in the autofree ctx is manually freed, things don't explode atexit |
907 | | * |
908 | | */ |
909 | | static int _autofree_thread_local_destructor(TALLOC_CTX *af) |
910 | 0 | { |
911 | 0 | return fr_atexit_thread_local_disarm(true, _autofree_on_exit, af); |
912 | 0 | } |
913 | | |
914 | | /** Get a thread-safe autofreed ctx that will be freed when the thread or process exits |
915 | | * |
916 | | * @note This should be used in place of talloc_autofree_context (which is now deprecated) |
917 | | * @note Will not be thread-safe if NULL tracking is enabled. |
918 | | * |
919 | | * @return A talloc ctx which will be freed when the thread or process exits. |
920 | | */ |
921 | | TALLOC_CTX *talloc_autofree_context_thread_local(void) |
922 | 0 | { |
923 | 0 | TALLOC_CTX *af = thread_local_ctx; |
924 | |
|
925 | 0 | if (!af) { |
926 | 0 | af = talloc_init_const("thread_local_autofree_context"); |
927 | 0 | if (unlikely(!af)) return NULL; |
928 | | |
929 | 0 | talloc_set_destructor(af, _autofree_thread_local_destructor); |
930 | |
|
931 | 0 | fr_atexit_thread_local(thread_local_ctx, _autofree_on_exit, af); |
932 | 0 | } |
933 | | |
934 | 0 | return af; |
935 | 0 | } |
936 | | #endif |