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