Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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