The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
talloc_tests.c
Go to the documentation of this file.
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/** Tests for talloc and FreeRADIUS talloc utility functions
18 *
19 * @file src/lib/util/test/talloc_tests.c
20 *
21 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
22 */
23#include "acutest_common_init.h"
24#include "acutest_helpers.h"
25#include <freeradius-devel/util/talloc.h>
26#include <freeradius-devel/util/math.h>
27
28/*
29 * === Core talloc (samba) tests ===
30 */
31
32/*
33 * talloc - basic allocation and parent/child hierarchy
34 */
35static void test_talloc_basic(void)
36{
37 TALLOC_CTX *ctx;
38 int *p;
39 char *s;
40
41 ctx = talloc_init_const("test_basic");
42 TEST_ASSERT(ctx != NULL);
43
44 TEST_CASE("talloc allocates child of context");
45 p = talloc(ctx, int);
46 TEST_ASSERT(p != NULL);
47 TEST_CHECK(talloc_parent(p) == ctx);
48 *p = 42;
49 TEST_CHECK(*p == 42);
50
51 TEST_CASE("talloc_size allocates arbitrary size");
52 s = talloc_size(ctx, 100);
53 TEST_ASSERT(s != NULL);
54 TEST_CHECK(talloc_get_size(s) == 100);
55 TEST_CHECK(talloc_parent(s) == ctx);
56
57 talloc_free(ctx);
58}
59
60/*
61 * talloc_zero - zero-initialized allocation
62 */
63static void test_talloc_zero(void)
64{
65 TALLOC_CTX *ctx;
66 int *p;
67 uint8_t *buf;
68 unsigned int i;
69
70 ctx = talloc_init_const("test_zero");
71
72 TEST_CASE("talloc_zero initializes to zero");
73 p = talloc_zero(ctx, int);
74 TEST_ASSERT(p != NULL);
75 TEST_CHECK(*p == 0);
76
77 TEST_CASE("talloc_zero_size initializes to zero");
78 buf = talloc_zero_size(ctx, 64);
79 TEST_ASSERT(buf != NULL);
80 for (i = 0; i < 64; i++) {
81 TEST_CHECK(buf[i] == 0);
82 }
83
84 talloc_free(ctx);
85}
86
87/*
88 * talloc_array / talloc_zero_array / talloc_realloc
89 */
90static void test_talloc_array(void)
91{
92 TALLOC_CTX *ctx;
93 int *arr;
94 unsigned int i;
95
96 ctx = talloc_init_const("test_array");
97
98 TEST_CASE("talloc_array allocates typed array");
99 arr = talloc_array(ctx, int, 10);
100 TEST_ASSERT(arr != NULL);
101 TEST_CHECK(talloc_get_size(arr) == 10 * sizeof(int));
102 for (i = 0; i < 10; i++) arr[i] = (int)i;
103 for (i = 0; i < 10; i++) TEST_CHECK(arr[i] == (int)i);
104
105 TEST_CASE("talloc_zero_array initializes to zero");
106 arr = talloc_zero_array(ctx, int, 5);
107 TEST_ASSERT(arr != NULL);
108 for (i = 0; i < 5; i++) TEST_CHECK(arr[i] == 0);
109
110 TEST_CASE("talloc_realloc grows array preserving content");
111 arr = talloc_array(ctx, int, 4);
112 for (i = 0; i < 4; i++) arr[i] = (int)(i + 100);
113 arr = talloc_realloc(ctx, arr, int, 8);
114 TEST_ASSERT(arr != NULL);
115 for (i = 0; i < 4; i++) TEST_CHECK(arr[i] == (int)(i + 100));
116 TEST_CHECK(talloc_get_size(arr) == 8 * sizeof(int));
117
118 TEST_CASE("talloc_realloc shrinks array");
119 arr = talloc_realloc(ctx, arr, int, 2);
120 TEST_ASSERT(arr != NULL);
121 TEST_CHECK(arr[0] == 100);
122 TEST_CHECK(arr[1] == 101);
123 TEST_CHECK(talloc_get_size(arr) == 2 * sizeof(int));
124
125 talloc_free(ctx);
126}
127
128/*
129 * talloc_free - recursive free of children
130 */
131static void test_talloc_free(void)
132{
133 TALLOC_CTX *ctx;
134 int *child1, *child2;
135
136 TEST_CASE("talloc_free(NULL) returns -1");
137 TEST_CHECK(talloc_free(NULL) == -1);
138
139 TEST_CASE("talloc_free recursively frees children");
140 ctx = talloc_init_const("test_free");
141 child1 = talloc(ctx, int);
142 TEST_ASSERT(child1 != NULL);
143 child2 = talloc(child1, int);
144 TEST_ASSERT(child2 != NULL);
145 TEST_CHECK(talloc_parent(child2) == child1);
146 TEST_CHECK(talloc_free(ctx) == 0);
147}
148
149/*
150 * talloc_set_name_const / talloc_get_name / talloc_check_name
151 */
152static void test_talloc_naming(void)
153{
154 TALLOC_CTX *ctx;
155 void *p;
156
157 ctx = talloc_init_const("test_naming");
158
159 TEST_CASE("talloc_set_name_const / talloc_get_name roundtrip");
160 p = talloc_size(ctx, 10);
161 talloc_set_name_const(p, "my_chunk");
162 TEST_CHECK(strcmp(talloc_get_name(p), "my_chunk") == 0);
163
164 TEST_CASE("talloc_check_name succeeds on match");
165 TEST_CHECK(talloc_check_name(p, "my_chunk") == p);
166
167 TEST_CASE("talloc_check_name returns NULL on mismatch");
168 TEST_CHECK(talloc_check_name(p, "wrong_name") == NULL);
169
170 TEST_CASE("talloc_named_const sets name at creation");
171 p = talloc_named_const(ctx, 20, "named_chunk");
172 TEST_ASSERT(p != NULL);
173 TEST_CHECK(strcmp(talloc_get_name(p), "named_chunk") == 0);
174
175 talloc_free(ctx);
176}
177
178/*
179 * talloc_parent / talloc_parent_name
180 */
181static void test_talloc_parent(void)
182{
183 TALLOC_CTX *ctx;
184 int *child;
185
186 ctx = talloc_init_const("test_parent");
187
188 TEST_CASE("talloc_parent returns correct parent");
189 child = talloc(ctx, int);
190 TEST_CHECK(talloc_parent(child) == ctx);
191
192 TEST_CASE("talloc_parent_name returns parent's name");
193 TEST_CHECK(strcmp(talloc_parent_name(child), "test_parent") == 0);
194
195 TEST_CASE("talloc_parent of top-level returns NULL context");
196 /*
197 * Top-level contexts allocated with talloc_init have the
198 * null context as parent (or NULL if tracking is off).
199 */
200 {
201 void *parent = talloc_parent(ctx);
202 (void)parent; /* Just verify it doesn't crash */
203 }
204
205 talloc_free(ctx);
206}
207
208/*
209 * talloc_steal - reparent a chunk
210 */
211static void test_talloc_steal(void)
212{
213 TALLOC_CTX *ctx1, *ctx2;
214 int *p;
215 void *ret;
216
217 ctx1 = talloc_init_const("ctx1");
218 ctx2 = talloc_init_const("ctx2");
219
220 TEST_CASE("talloc_steal moves child to new parent");
221 p = talloc(ctx1, int);
222 *p = 99;
223 TEST_CHECK(talloc_parent(p) == ctx1);
224 ret = talloc_steal(ctx2, p);
225 TEST_CHECK(ret == p);
226 TEST_CHECK(talloc_parent(p) == ctx2);
227 TEST_CHECK(*p == 99);
228
229 TEST_CASE("talloc_steal NULL returns NULL");
230 TEST_CHECK(talloc_steal(ctx1, NULL) == NULL);
231
232 TEST_CASE("talloc_steal to NULL reparents to null context");
233 p = talloc(ctx1, int);
234 ret = talloc_steal(NULL, p);
235 TEST_CHECK(ret == p);
236 talloc_free(p); /* must free manually since no parent owns it */
237
238 talloc_free(ctx1);
239 talloc_free(ctx2);
240}
241
242/*
243 * talloc_move - steal and NULL out old pointer
244 */
245static void test_talloc_move(void)
246{
247 TALLOC_CTX *ctx1, *ctx2;
248 int *p, *moved;
249
250 ctx1 = talloc_init_const("ctx1");
251 ctx2 = talloc_init_const("ctx2");
252
253 TEST_CASE("talloc_move transfers ownership and NULLs source");
254 p = talloc(ctx1, int);
255 *p = 77;
256 moved = talloc_move(ctx2, &p);
257 TEST_ASSERT(moved != NULL);
258 TEST_CHECK(*moved == 77);
259 TEST_CHECK(p == NULL);
260 TEST_CHECK(talloc_parent(moved) == ctx2);
261
262 talloc_free(ctx1);
263 talloc_free(ctx2);
264}
265
266/*
267 * talloc_reference / talloc_unlink / talloc_reference_count
268 */
269static void test_talloc_reference(void)
270{
271 TALLOC_CTX *ctx1, *ctx2;
272 char *str;
273 void *ref;
274
275 ctx1 = talloc_init_const("ctx1");
276 ctx2 = talloc_init_const("ctx2");
277
278 TEST_CASE("talloc_reference creates a reference");
279 str = talloc_strdup(ctx1, "shared");
280 ref = talloc_reference(ctx2, str);
281 TEST_CHECK(ref != NULL);
282 TEST_CHECK(talloc_reference_count(str) == 1);
283
284 TEST_CASE("talloc_unlink removes the reference");
285 TEST_CHECK(talloc_unlink(ctx2, str) == 0);
286 TEST_CHECK(talloc_reference_count(str) == 0);
287
288 TEST_CASE("Multiple references increment count");
289 talloc_reference(ctx2, str);
290 talloc_reference(ctx2, str);
291 TEST_CHECK(talloc_reference_count(str) >= 2);
292
293 talloc_free(ctx1);
294 talloc_free(ctx2);
295}
296
297/*
298 * talloc_set_destructor - native talloc destructors
299 */
301
302static int native_destructor(int *ptr)
303{
304 TEST_ASSERT(*ptr == 1);
305 *ptr = 0;
307 return 0;
308}
309
311{
312 TALLOC_CTX *ctx;
313 int *p;
314
315 ctx = talloc_init_const("test_destructor");
316
317 TEST_CASE("Destructor called on free");
319 p = talloc(ctx, int);
320 *p = 1;
321 talloc_set_destructor(p, native_destructor);
322 talloc_free(p);
324
325 TEST_CASE("Destructor returning -1 prevents free");
326
327 /*
328 * We can't easily test this without risking a leak,
329 * so just verify the destructor fires on parent free.
330 */
332 p = talloc(ctx, int);
333 *p = 1;
334 talloc_set_destructor(p, native_destructor);
335 talloc_free(ctx);
337}
338
339/*
340 * talloc_strdup / talloc_strndup (base samba versions,
341 * which are remapped to talloc_typed_strdup/strndup by the header)
342 */
343static void test_talloc_strdup(void)
344{
345 TALLOC_CTX *ctx;
346 char *s;
347
348 ctx = talloc_init_const("test_strdup");
349
350 TEST_CASE("talloc_strdup duplicates string");
351 s = talloc_strdup(ctx, "hello");
352 TEST_ASSERT(s != NULL);
353 TEST_CHECK(strcmp(s, "hello") == 0);
354 TEST_CHECK(talloc_parent(s) == ctx);
355
356 TEST_CASE("talloc_strndup limits length");
357 s = talloc_strndup(ctx, "hello world", 5);
358 TEST_ASSERT(s != NULL);
359 TEST_CHECK(strcmp(s, "hello") == 0);
360
361 TEST_CASE("talloc_strdup of NULL returns NULL");
362 s = talloc_strdup(ctx, NULL);
363 TEST_CHECK(s == NULL);
364
365 talloc_free(ctx);
366}
367
368/*
369 * talloc_asprintf / talloc_asprintf_append / talloc_asprintf_append_buffer
370 */
371static void test_talloc_asprintf(void)
372{
373 TALLOC_CTX *ctx;
374 char *s;
375
376 ctx = talloc_init_const("test_asprintf");
377
378 TEST_CASE("talloc_asprintf formats string");
379 s = talloc_asprintf(ctx, "num=%d str=%s", 42, "foo");
380 TEST_ASSERT(s != NULL);
381 TEST_CHECK(strcmp(s, "num=42 str=foo") == 0);
382
383 TEST_CASE("talloc_asprintf_append appends at string end");
384 s = talloc_strdup(ctx, "hello");
385 s = talloc_asprintf_append(s, " %s", "world");
386 TEST_ASSERT(s != NULL);
387 TEST_CHECK(strcmp(s, "hello world") == 0);
388
389 TEST_CASE("talloc_asprintf_append_buffer appends at buffer end");
390 s = talloc_strdup(ctx, "abc");
391 s = talloc_asprintf_append_buffer(s, "def");
392 TEST_ASSERT(s != NULL);
393 TEST_CHECK(strcmp(s, "abcdef") == 0);
394
395 talloc_free(ctx);
396}
397
398/*
399 * talloc_pool - suballocations from a pool
400 */
401static void test_talloc_pool(void)
402{
403 TALLOC_CTX *pool;
404 int *a, *b, *c;
405
406 TEST_CASE("talloc_pool creates a pool context");
407 pool = talloc_pool(NULL, 1024);
408 TEST_ASSERT(pool != NULL);
409
410 TEST_CASE("Allocations from pool succeed");
411 a = talloc(pool, int);
412 b = talloc(pool, int);
413 c = talloc(pool, int);
414 TEST_ASSERT(a != NULL);
415 TEST_ASSERT(b != NULL);
416 TEST_ASSERT(c != NULL);
417 *a = 1; *b = 2; *c = 3;
418 TEST_CHECK(*a == 1);
419 TEST_CHECK(*b == 2);
420 TEST_CHECK(*c == 3);
421
422 TEST_CASE("Pool children have pool as parent");
423 TEST_CHECK(talloc_parent(a) == pool);
424 TEST_CHECK(talloc_parent(b) == pool);
425
426 talloc_free(pool);
427}
428
429/*
430 * talloc_free_children - free all children but keep parent
431 */
433{
434 TALLOC_CTX *ctx;
435 int *p;
436
437 ctx = talloc_init_const("test_free_children");
438
439 TEST_CASE("talloc_free_children frees all children");
440
441 p = talloc(ctx, int);
442 TEST_CHECK(p != NULL);
443 p = talloc(ctx, int);
444 TEST_CHECK(p != NULL);
445 p = talloc(ctx, int);
446 TEST_CHECK(p != NULL);
447
448 TEST_CHECK(talloc_total_blocks(ctx) == 4); /* ctx + 3 children */
449
450 talloc_free_children(ctx);
451 TEST_CHECK(talloc_total_blocks(ctx) == 1); /* just ctx */
452
453 TEST_CASE("Context is still usable after free_children");
454 p = talloc(ctx, int);
455 TEST_ASSERT(p != NULL);
456 *p = 123;
457 TEST_CHECK(*p == 123);
458
459 talloc_free(ctx);
460}
461
462/*
463 * talloc_total_size / talloc_total_blocks
464 */
465static void test_talloc_total_size(void)
466{
467 TALLOC_CTX *ctx;
468 size_t total_size, total_blocks;
469
470 ctx = talloc_init_const("test_total");
471
472 TEST_CASE("Empty context has size 0 and 1 block");
473 TEST_CHECK(talloc_total_size(ctx) == 0);
474 TEST_CHECK(talloc_total_blocks(ctx) == 1);
475
476 TEST_CASE("Allocations increase totals");
477 talloc_size(ctx, 100);
478 talloc_size(ctx, 200);
479 total_size = talloc_total_size(ctx);
480 total_blocks = talloc_total_blocks(ctx);
481 TEST_CHECK(total_size >= 300);
482 TEST_CHECK(total_blocks == 3); /* ctx + 2 children */
483
484 TEST_CASE("Nested children counted in totals");
485 {
486 void *child = talloc_size(ctx, 50);
487 talloc_size(child, 25);
488 }
489 TEST_CHECK(talloc_total_size(ctx) >= 375);
490 TEST_CHECK(talloc_total_blocks(ctx) == 5);
491
492 talloc_free(ctx);
493}
494
495/*
496 * talloc_get_size - get chunk data size
497 */
498static void test_talloc_get_size(void)
499{
500 TALLOC_CTX *ctx;
501 void *p;
502
503 ctx = talloc_init_const("test_get_size");
504
505 TEST_CASE("talloc_get_size returns requested size");
506 p = talloc_size(ctx, 42);
507 TEST_CHECK(talloc_get_size(p) == 42);
508
509 TEST_CASE("talloc_get_size of NULL returns 0");
510 TEST_CHECK(talloc_get_size(NULL) == 0);
511
512 TEST_CASE("talloc_get_size after realloc returns new size");
513 p = talloc_realloc_size(ctx, p, 100);
514 TEST_CHECK(talloc_get_size(p) == 100);
515
516 talloc_free(ctx);
517}
518
519/*
520 * talloc_find_parent_byname - walk parent chain looking for name
521 */
523{
524 TALLOC_CTX *root, *mid;
525 int *leaf;
526 void *found;
527
528 root = talloc_named_const(NULL, 0, "root_ctx");
529 mid = talloc_named_const(root, 0, "mid_ctx");
530 leaf = talloc(mid, int);
531
532 TEST_CASE("Find existing parent by name");
533 found = talloc_find_parent_byname(leaf, "root_ctx");
534 TEST_CHECK(found == root);
535
536 found = talloc_find_parent_byname(leaf, "mid_ctx");
537 TEST_CHECK(found == mid);
538
539 TEST_CASE("Return NULL for non-existent parent name");
540 found = talloc_find_parent_byname(leaf, "nonexistent");
541 TEST_CHECK(found == NULL);
542
543 talloc_free(root);
544}
545
546/*
547 * talloc_is_parent - check if ptr is an ancestor
548 */
549static void test_talloc_is_parent(void)
550{
551 TALLOC_CTX *root, *mid;
552 int *leaf;
553
554 root = talloc_init_const("root");
555 mid = talloc(root, char);
556 leaf = talloc(mid, int);
557
558 TEST_CASE("Direct parent is parent");
559 TEST_CHECK(talloc_is_parent(leaf, mid) == 1);
560
561 TEST_CASE("Grandparent is parent");
562 TEST_CHECK(talloc_is_parent(leaf, root) == 1);
563
564 TEST_CASE("Non-parent is not parent");
565 {
566 TALLOC_CTX *other = talloc_init_const("other");
567 TEST_CHECK(talloc_is_parent(leaf, other) == 0);
568 talloc_free(other);
569 }
570
571 talloc_free(root);
572}
573
574/*
575 * talloc_reparent - reparent with explicit old parent
576 */
577static void test_talloc_reparent(void)
578{
579 TALLOC_CTX *old_parent, *new_parent;
580 int *p;
581 void *ret;
582
583 old_parent = talloc_init_const("old");
584 new_parent = talloc_init_const("new");
585
586 TEST_CASE("talloc_reparent moves from old to new parent");
587 p = talloc(old_parent, int);
588 *p = 55;
589 ret = talloc_reparent(old_parent, new_parent, p);
590 TEST_CHECK(ret == p);
591 TEST_CHECK(talloc_parent(p) == new_parent);
592 TEST_CHECK(*p == 55);
593
594 TEST_CASE("talloc_reparent NULL returns NULL");
595 ret = talloc_reparent(old_parent, new_parent, NULL);
596 TEST_CHECK(ret == NULL);
597
598 talloc_free(old_parent);
599 talloc_free(new_parent);
600}
601
602/*
603 * talloc_memdup - duplicate memory
604 */
605static void test_talloc_memdup(void)
606{
607 TALLOC_CTX *ctx;
608 uint8_t src[] = {0x01, 0x02, 0x03, 0x04};
609 uint8_t *dup;
610
611 ctx = talloc_init_const("test_memdup");
612
613 TEST_CASE("talloc_memdup duplicates bytes");
614 dup = talloc_memdup(ctx, src, sizeof(src));
615 TEST_ASSERT(dup != NULL);
616 TEST_CHECK(memcmp(dup, src, sizeof(src)) == 0);
617 TEST_CHECK(talloc_get_size(dup) == sizeof(src));
618 TEST_CHECK(talloc_parent(dup) == ctx);
619
620 talloc_free(ctx);
621}
622
623/*
624 * talloc hierarchy depth - deep nesting
625 */
627{
628 TALLOC_CTX *root;
629 void *current;
630 int i;
631
632 root = talloc_init_const("deep_root");
633 current = root;
634
635 TEST_CASE("Deep hierarchy (100 levels) works");
636 for (i = 0; i < 100; i++) {
637 current = talloc_size(current, 16);
638 TEST_ASSERT(current != NULL);
639 }
640
641 TEST_CASE("Total blocks counts all levels");
642 TEST_CHECK(talloc_total_blocks(root) == 101); /* root + 100 children */
643
644 TEST_CASE("Free cleans up entire hierarchy");
645 TEST_CHECK(talloc_free(root) == 0);
646}
647
648/*
649 * talloc_strndup_append / talloc_strndup_append_buffer
650 */
652{
653 TALLOC_CTX *ctx;
654 char *s;
655
656 ctx = talloc_init_const("test_strndup_append");
657
658 TEST_CASE("talloc_strndup_append appends limited length");
659 s = talloc_strdup(ctx, "hello");
660 s = talloc_strndup_append(s, " world!!", 6);
661 TEST_ASSERT(s != NULL);
662 TEST_CHECK(strcmp(s, "hello world") == 0);
663
664 TEST_CASE("talloc_strndup_append_buffer appends at buffer end");
665 s = talloc_strdup(ctx, "abc");
666 s = talloc_strndup_append_buffer(s, "defgh", 3);
667 TEST_ASSERT(s != NULL);
668 TEST_CHECK(strcmp(s, "abcdef") == 0);
669
670 talloc_free(ctx);
671}
672
673/*
674 * talloc_increase_ref_count
675 */
677{
678 TALLOC_CTX *ctx;
679 char *str;
680 int ret;
681
682 ctx = talloc_init_const("test_ref_count");
683
684 TEST_CASE("increase_ref_count succeeds");
685 str = talloc_strdup(ctx, "refcounted");
686 ret = talloc_increase_ref_count(str);
687 TEST_CHECK(ret == 0);
688 TEST_CHECK(talloc_reference_count(str) == 1);
689
690 TEST_CASE("Multiple increases stack");
691 ret = talloc_increase_ref_count(str);
692 TEST_CHECK(ret == 0);
693 TEST_CHECK(talloc_reference_count(str) == 2);
694
695 /*
696 * And then decrease it to be safe.
697 */
698 ret = talloc_decrease_ref_count(str);
699 TEST_CHECK(ret == 2); // WAS 2
700 TEST_CHECK(talloc_reference_count(str) == 1); // NOW 1
701
702 ret = talloc_decrease_ref_count(str);
703 TEST_CHECK(ret == 1); // WAS 1, now 0 and freed
704
705 ret = talloc_total_blocks(ctx);
706 TEST_CHECK(ret == 1); // just ctx itself
707
708 ret = talloc_free(ctx);
709 TEST_CHECK(ret == 0);
710}
711
712/*
713 * === FreeRADIUS extension tests ===
714 */
715
716/*
717 * talloc_typed_strdup - should duplicate string and set type to char
718 */
720{
721 TALLOC_CTX *ctx;
722 char *dup;
723
724 ctx = talloc_init_const("test");
725
726 TEST_CASE("Duplicate a simple string");
727 dup = talloc_typed_strdup(ctx, "hello");
728 TEST_ASSERT(dup != NULL);
729 TEST_CHECK(strcmp(dup, "hello") == 0);
730 TEST_CHECK(talloc_get_size(dup) == 6);
731 TEST_CHECK(talloc_parent(dup) == ctx);
732
733 TEST_CASE("Duplicate an empty string");
734 dup = talloc_typed_strdup(ctx, "");
735 TEST_ASSERT(dup != NULL);
736 TEST_CHECK(dup[0] == '\0');
737 TEST_CHECK(talloc_get_size(dup) == 1);
738
739 TEST_CASE("Duplicate NULL returns NULL");
740 dup = talloc_typed_strdup(ctx, NULL);
741 TEST_CHECK(dup == NULL);
742
743 talloc_free(ctx);
744}
745
746/*
747 * talloc_typed_strndup - length-limited duplication
748 */
750{
751 TALLOC_CTX *ctx;
752 char *dup;
753
754 ctx = talloc_init_const("test");
755
756 TEST_CASE("Duplicate partial string");
757 dup = talloc_typed_strndup(ctx, "hello world", 5);
758 TEST_ASSERT(dup != NULL);
759 TEST_CHECK(strcmp(dup, "hello") == 0);
760 TEST_CHECK(talloc_get_size(dup) == 6);
761
762 TEST_CASE("Duplicate zero length");
763 dup = talloc_typed_strndup(ctx, "hello", 0);
764 TEST_ASSERT(dup != NULL);
765 TEST_CHECK(dup[0] == '\0');
766
767 talloc_free(ctx);
768}
769
770/*
771 * talloc_typed_asprintf - formatted string with correct type
772 */
774{
775 TALLOC_CTX *ctx;
776 char *str;
777
778 ctx = talloc_init_const("test");
779
780 TEST_CASE("Simple format string");
781 str = talloc_typed_asprintf(ctx, "hello %s %d", "world", 42);
782 TEST_ASSERT(str != NULL);
783 TEST_CHECK(strcmp(str, "hello world 42") == 0);
784
785 TEST_CASE("Empty format string");
786 str = talloc_typed_asprintf(ctx, "%s", "");
787 TEST_ASSERT(str != NULL);
788 TEST_CHECK(str[0] == '\0');
789
790 talloc_free(ctx);
791}
792
793/*
794 * talloc_bstrndup - binary safe string duplication
795 */
796static void test_talloc_bstrndup(void)
797{
798 TALLOC_CTX *ctx;
799 char *dup;
800
801 ctx = talloc_init_const("test");
802
803 TEST_CASE("Duplicate string with embedded NUL");
804 dup = talloc_bstrndup(ctx, "hel\0lo", 6);
805 TEST_ASSERT(dup != NULL);
806 TEST_CHECK(memcmp(dup, "hel\0lo", 6) == 0);
807 TEST_CHECK(dup[6] == '\0'); /* Should be NUL terminated */
808 TEST_CHECK(talloc_array_length(dup) == 7);
809
810 TEST_CASE("Duplicate zero-length string");
811 dup = talloc_bstrndup(ctx, "", 0);
812 TEST_ASSERT(dup != NULL);
813 TEST_CHECK(dup[0] == '\0');
814 TEST_CHECK(talloc_array_length(dup) == 1);
815
816 talloc_free(ctx);
817}
818
819/*
820 * talloc_bstr_append - append binary-safe strings
821 */
822static void test_talloc_bstr_append(void)
823{
824 TALLOC_CTX *ctx;
825 char *str, *result;
826
827 ctx = talloc_init_const("test");
828
829 TEST_CASE("Append to existing string");
830 str = talloc_bstrndup(ctx, "hello", 5);
831 result = talloc_bstr_append(ctx, str, " world", 6);
832 TEST_ASSERT(result != NULL);
833 TEST_CHECK(strcmp(result, "hello world") == 0);
834
835 TEST_CASE("Append to NULL creates new string");
836 result = talloc_bstr_append(ctx, NULL, "new", 3);
837 TEST_ASSERT(result != NULL);
838 TEST_CHECK(strcmp(result, "new") == 0);
839
840 talloc_free(ctx);
841}
842
843/*
844 * talloc_bstr_realloc - reallocate a bstr to a new length
845 */
847{
848 TALLOC_CTX *ctx;
849 char *str, *result;
850
851 ctx = talloc_init_const("test");
852
853 TEST_CASE("Realloc NULL allocates new buffer");
854 result = talloc_bstr_realloc(ctx, NULL, 10);
855 TEST_ASSERT(result != NULL);
856 TEST_CHECK(result[0] == '\0');
857 TEST_CHECK(talloc_array_length(result) == 11);
858
859 TEST_CASE("Realloc existing buffer to smaller size");
860 str = talloc_bstrndup(ctx, "hello world", 11);
861 result = talloc_bstr_realloc(ctx, str, 5);
862 TEST_ASSERT(result != NULL);
863 TEST_CHECK(result[5] == '\0');
864 TEST_CHECK(talloc_array_length(result) == 6);
865
866 talloc_free(ctx);
867}
868
869/*
870 * talloc_memcmp_bstr - compare char arrays
871 */
872static void test_talloc_memcmp_bstr(void)
873{
874 TALLOC_CTX *ctx;
875 char *a, *b;
876
877 ctx = talloc_init_const("test");
878
879 TEST_CASE("Equal strings");
880 a = talloc_bstrndup(ctx, "abc", 3);
881 b = talloc_bstrndup(ctx, "abc", 3);
882 TEST_CHECK(talloc_memcmp_bstr(a, b) == 0);
883
884 TEST_CASE("Different lengths");
885 talloc_free(a);
886 a = talloc_bstrndup(ctx, "abcd", 4);
888
889 talloc_free(ctx);
890}
891
892/*
893 * talloc_typed_memdup - duplicate uint8_t buffer
894 */
896{
897 TALLOC_CTX *ctx;
898 uint8_t data[] = {0xde, 0xad, 0xbe, 0xef};
899 uint8_t *dup;
900
901 ctx = talloc_init_const("test");
902
903 TEST_CASE("Duplicate binary data");
904 dup = talloc_typed_memdup(ctx, data, sizeof(data));
905 TEST_ASSERT(dup != NULL);
906 TEST_CHECK(memcmp(dup, data, sizeof(data)) == 0);
907 TEST_CHECK(talloc_get_size(dup) == sizeof(data));
908 TEST_CHECK(talloc_parent(dup) == ctx);
909
910 talloc_free(ctx);
911}
912
913/*
914 * talloc_array_null_terminate
915 */
917{
918 TALLOC_CTX *ctx;
919 void **array, **result;
920
921 ctx = talloc_init_const("test");
922
923 TEST_CASE("NULL terminate an array");
924 array = talloc_array(ctx, void *, 3);
925 array[0] = (void *)1;
926 array[1] = (void *)2;
927 array[2] = (void *)3;
928 result = talloc_array_null_terminate(array);
929 TEST_ASSERT(result != NULL);
930 TEST_CHECK(talloc_array_length(result) == 4);
931 TEST_CHECK(result[3] == NULL);
932 TEST_CHECK(result[0] == (void *)1);
933
934 TEST_CASE("NULL terminate NULL returns NULL");
936
937 talloc_free(ctx);
938}
939
940/*
941 * talloc_destructor_add / talloc_destructor_disarm
942 */
944static void const *fr_destructor_uctx;
945
946static int test_fr_destructor_func(UNUSED void *fire_ctx, void *uctx)
947{
949 fr_destructor_uctx = uctx;
950 return 0;
951}
952
954{
955 TALLOC_CTX *ctx, *fire;
957
958 ctx = talloc_init_const("test");
959
960 TEST_CASE("Destructor fires when fire_ctx is freed");
962 fire = talloc(ctx, int);
963 d = talloc_destructor_add(fire, NULL, test_fr_destructor_func, (void *)0x42);
964 TEST_CHECK(d != NULL);
965 talloc_free(fire);
967 TEST_CHECK(fr_destructor_uctx == (void *)0x42);
968
969 TEST_CASE("Manual disarm via talloc_destructor_disarm");
971 fire = talloc(ctx, int);
972 d = talloc_destructor_add(fire, NULL, test_fr_destructor_func, NULL);
973 TEST_ASSERT(d != NULL);
975 talloc_free(fire);
977
978 TEST_CASE("NULL fire_ctx returns NULL");
979 d = talloc_destructor_add(NULL, NULL, test_fr_destructor_func, NULL);
980 TEST_CHECK(d == NULL);
981
982 talloc_free(ctx);
983}
984
985/*
986 * talloc_link_ctx - link parent and child lifetimes
987 */
988static void test_talloc_link_ctx(void)
989{
990 TALLOC_CTX *parent, *child;
991 int ret;
992
993 TEST_CASE("Child freed when parent freed");
994 parent = talloc_init_const("parent");
995 child = talloc_init_const("child");
996 ret = talloc_link_ctx(parent, child);
997 TEST_CHECK(ret == 0);
999}
1000
1001/*
1002 * talloc_hdr_size - calculate talloc chunk header size
1003 */
1004static void test_talloc_hdr_size(void)
1005{
1006 ssize_t hdr;
1007
1008 TEST_CASE("Header size is positive and reasonable");
1009 hdr = talloc_hdr_size();
1010 TEST_CHECK(hdr > 0);
1011 TEST_CHECK(hdr < 1024);
1012 TEST_CHECK(hdr > (ssize_t)sizeof(void *));
1013
1014 TEST_CASE("Repeated calls return same value");
1015 TEST_CHECK(talloc_hdr_size() == hdr);
1016}
1017
1018/*
1019 * talloc_realloc_zero - realloc that zeros new memory
1020 */
1022{
1023 TALLOC_CTX *ctx;
1024 uint8_t *arr;
1025 unsigned int i;
1026
1027 ctx = talloc_init_const("test");
1028
1029 TEST_CASE("Initial allocation");
1030 arr = talloc_array(ctx, uint8_t, 4);
1031 TEST_ASSERT(arr != NULL);
1032 memset(arr, 0xff, 4);
1033
1034 TEST_CASE("Realloc larger zeros new portion");
1035 arr = talloc_realloc_zero(ctx, arr, uint8_t, 16);
1036 TEST_ASSERT(arr != NULL);
1037
1038 /* Original bytes preserved */
1039 for (i = 0; i < 4; i++) {
1040 TEST_CHECK(arr[i] == 0xff);
1041 }
1042
1043 /* New bytes zeroed */
1044 for (i = 4; i < 16; i++) {
1045 TEST_CHECK(arr[i] == 0);
1046 }
1047
1048 talloc_free(ctx);
1049}
1050
1051/*
1052 * talloc_decrease_ref_count
1053 */
1055{
1056 TALLOC_CTX *ctx, *ref_ctx;
1057 char *str;
1058 int ret;
1059
1060 ctx = talloc_init_const("test");
1061 ref_ctx = talloc_init_const("ref");
1062
1063 TEST_CASE("Decrease ref count with no references frees memory");
1064 str = talloc_strdup(ctx, "test");
1065 ret = talloc_decrease_ref_count(str);
1066 TEST_CHECK(ret == 0);
1067
1068 TEST_CASE("NULL ptr returns 0");
1069 ret = talloc_decrease_ref_count(NULL);
1070 TEST_CHECK(ret == 0);
1071
1072 TEST_CASE("With a reference, unlinks instead of freeing");
1073 str = talloc_strdup(ctx, "referenced");
1074 talloc_reference(ref_ctx, str);
1075 ret = talloc_decrease_ref_count(str);
1076 TEST_CHECK(ret == 1); /* One reference remains */
1077
1078 talloc_free(ctx);
1079 talloc_free(ref_ctx);
1080}
1081
1082/*
1083 * talloc_aligned_array - page-aligned allocation
1084 */
1086{
1087 TALLOC_CTX *ctx, *array;
1088 void *start;
1089 size_t page_size = (size_t)getpagesize();
1090
1091 ctx = talloc_init_const("test");
1092
1093 TEST_CASE("Aligned array returns non-NULL");
1094 array = talloc_aligned_array(ctx, &start, page_size, page_size);
1095 TEST_CHECK(array != NULL);
1096 TEST_CHECK(start != NULL);
1097
1098 TEST_CASE("Start address is page-aligned");
1099 TEST_CHECK(((uintptr_t)start % page_size) == 0);
1100
1101 talloc_free(ctx);
1102}
1103
1104/*
1105 * talloc_pool_used - bytes used, live bytes, and overflow of a talloc pool
1106 */
1107static size_t pool_chunk_footprint(size_t size)
1108{
1109 ssize_t hdr = talloc_hdr_size();
1110
1111 /*
1112 * The talloc header is a fixed struct of around a hundred
1113 * bytes. The upper bounds on hdr and size keep the sum below
1114 * from wrapping. Coverity analyses this function apart from
1115 * its callers, and needs the bound on size to see that the
1116 * sum cannot wrap.
1117 */
1118 if ((hdr <= 0) || (hdr > 4096) || (size > (SIZE_MAX / 2))) {
1119 TEST_ASSERT_(false, "talloc_hdr_size() returned %zd for size %zu", hdr, size);
1120 return 0;
1121 }
1122
1123 return ROUND_UP_POW2((size_t)hdr + size, 16);
1124}
1125
1126static void test_talloc_pool_used(void)
1127{
1128 TALLOC_CTX *pool;
1129 void *a, *b, *c, *big;
1130 size_t used, live;
1131 bool overflowed;
1132
1133 TEST_CASE("An empty pool uses nothing");
1134 pool = talloc_pool(NULL, 4096);
1135 TEST_ASSERT(pool != NULL);
1136 used = talloc_pool_used(&overflowed, &live, pool, 4096);
1137 TEST_CHECK(used == 0);
1138 TEST_CHECK(live == 0);
1139 TEST_CHECK(!overflowed);
1140
1141 TEST_CASE("With no frees, used and live are the sum of the chunk footprints");
1142 a = talloc_size(pool, 10);
1143 b = talloc_size(pool, 100);
1144 c = talloc_size(pool, 20);
1145 TEST_ASSERT(a && b && c);
1146 used = talloc_pool_used(&overflowed, &live, pool, 4096);
1148 TEST_MSG("used %zu", used);
1149 TEST_CHECK(live == used);
1150 TEST_MSG("live %zu", live);
1151 TEST_CHECK(!overflowed);
1152
1153 TEST_CASE("Freeing a chunk below the highest leaves a hole: used stays, live drops");
1154 talloc_free(b);
1155 used = talloc_pool_used(&overflowed, &live, pool, 4096);
1157 TEST_MSG("used %zu", used);
1159 TEST_MSG("live %zu", live);
1160 TEST_CHECK(!overflowed);
1161
1162 TEST_CASE("A chunk too large for the pool overflows, and is not live in the pool");
1163 big = talloc_size(pool, 8192);
1164 TEST_ASSERT(big != NULL);
1165 used = talloc_pool_used(&overflowed, &live, pool, 4096);
1166 TEST_CHECK(overflowed);
1168 TEST_MSG("used %zu", used);
1170 TEST_MSG("live %zu", live);
1171
1172 talloc_free(pool);
1173}
1174
1176 /* Core talloc (samba) tests */
1177 { "talloc_basic", test_talloc_basic },
1178 { "talloc_zero", test_talloc_zero },
1179 { "talloc_array", test_talloc_array },
1180 { "talloc_free", test_talloc_free },
1181 { "talloc_naming", test_talloc_naming },
1182 { "talloc_parent", test_talloc_parent },
1183 { "talloc_steal", test_talloc_steal },
1184 { "talloc_move", test_talloc_move },
1185 { "talloc_reference", test_talloc_reference },
1186 { "talloc_set_destructor", test_talloc_set_destructor },
1187 { "talloc_strdup", test_talloc_strdup },
1188 { "talloc_asprintf", test_talloc_asprintf },
1189 { "talloc_pool", test_talloc_pool },
1190 { "talloc_free_children", test_talloc_free_children },
1191 { "talloc_total_size", test_talloc_total_size },
1192 { "talloc_get_size", test_talloc_get_size },
1193 { "talloc_find_parent_byname", test_talloc_find_parent_byname },
1194 { "talloc_is_parent", test_talloc_is_parent },
1195 { "talloc_reparent", test_talloc_reparent },
1196 { "talloc_memdup", test_talloc_memdup },
1197 { "talloc_deep_hierarchy", test_talloc_deep_hierarchy },
1198 { "talloc_strndup_append", test_talloc_strndup_append },
1199 { "talloc_increase_ref_count", test_talloc_increase_ref_count },
1200
1201 /* FreeRADIUS extension tests */
1202 { "talloc_typed_strdup", test_talloc_typed_strdup },
1203 { "talloc_typed_strndup", test_talloc_typed_strndup },
1204 { "talloc_typed_asprintf", test_talloc_typed_asprintf },
1205 { "talloc_bstrndup", test_talloc_bstrndup },
1206 { "talloc_bstr_append", test_talloc_bstr_append },
1207 { "talloc_bstr_realloc", test_talloc_bstr_realloc },
1208 { "talloc_memcmp_bstr", test_talloc_memcmp_bstr },
1209 { "talloc_typed_memdup", test_talloc_typed_memdup },
1210 { "talloc_array_null_terminate", test_talloc_array_null_terminate },
1211 { "talloc_destructor_add", test_talloc_destructor_add },
1212 { "talloc_link_ctx", test_talloc_link_ctx },
1213 { "talloc_hdr_size", test_talloc_hdr_size },
1214 { "talloc_realloc_zero", test_talloc_realloc_zero },
1215 { "talloc_decrease_ref_count", test_talloc_decrease_ref_count },
1216 { "talloc_aligned_array", test_talloc_aligned_array },
1217 { "talloc_pool_used", test_talloc_pool_used },
1218
1220};
#define TEST_CHECK(cond)
Definition acutest.h:101
#define TEST_ASSERT_(cond,...)
Definition acutest.h:120
#define TEST_CASE(name)
Definition acutest.h:203
#define TEST_ASSERT(cond)
Definition acutest.h:125
#define TEST_TERMINATOR
Definition acutest.h:77
#define TEST_MSG(...)
Definition acutest.h:234
Common initialisation for acutest binaries.
#define UNUSED
Definition build.h:415
talloc_free(hp)
#define ROUND_UP_POW2(_num, _mul)
Round up - Only works if _mul is a power of 2 but avoids division.
Definition math.h:205
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
static size_t used
void ** talloc_array_null_terminate(void **array)
Add a NULL pointer to an array of pointers.
Definition talloc.c:926
fr_talloc_destructor_t * talloc_destructor_add(TALLOC_CTX *fire_ctx, TALLOC_CTX *disarm_ctx, fr_talloc_free_func_t func, void const *uctx)
Add an additional destructor to a talloc chunk.
Definition talloc.c:97
ssize_t talloc_hdr_size(void)
Calculate the size of the talloc chunk header.
Definition talloc.c:272
char * talloc_bstr_realloc(TALLOC_CTX *ctx, char *in, size_t inlen)
Trim a bstr (char) buffer.
Definition talloc.c:757
size_t talloc_pool_used(bool *overflowed, size_t *live, TALLOC_CTX const *pool, size_t pool_size)
Measure how many bytes of a talloc pool are used and live, and whether the pool overflowed.
Definition talloc.c:461
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:621
TALLOC_CTX * talloc_aligned_array(TALLOC_CTX *ctx, void **start, size_t alignment, size_t size)
Return a page aligned talloc memory array.
Definition talloc.c:197
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition talloc.c:168
uint8_t * talloc_typed_memdup(TALLOC_CTX *ctx, uint8_t const *in, size_t inlen)
Call talloc_memdup, setting the type on the new chunk correctly.
Definition talloc.c:521
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:693
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:546
int talloc_decrease_ref_count(void const *ptr)
Decrease the reference count on a ptr.
Definition talloc.c:893
char * talloc_typed_strndup(TALLOC_CTX *ctx, char const *p, size_t len)
Call talloc_strndup, setting the type on the new chunk correctly.
Definition talloc.c:597
char * talloc_bstr_append(TALLOC_CTX *ctx, char *to, char const *from, size_t from_len)
Append a bstr to a bstr.
Definition talloc.c:721
void talloc_destructor_disarm(fr_talloc_destructor_t *d)
Disarm a destructor and free all memory allocated in the trigger ctxs.
Definition talloc.c:139
int talloc_memcmp_bstr(char const *a, char const *b)
Compares two talloced char arrays with memcmp.
Definition talloc.c:869
#define talloc_strndup(_ctx, _str, _len)
Definition talloc.h:150
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:127
#define talloc_asprintf
Definition talloc.h:151
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
#define talloc_realloc_zero(_ctx, _ptr, _type, _count)
Definition talloc.h:219
Structure to record a destructor operation on a specific talloc chunk.
Definition talloc.h:93
static int native_destructor_count
static void test_talloc_bstrndup(void)
static void test_talloc_steal(void)
TEST_LIST
static void test_talloc_free_children(void)
static void test_talloc_get_size(void)
static int test_fr_destructor_func(UNUSED void *fire_ctx, void *uctx)
static int native_destructor(int *ptr)
static void test_talloc_decrease_ref_count(void)
static void test_talloc_hdr_size(void)
static void test_talloc_zero(void)
static void test_talloc_typed_memdup(void)
static void test_talloc_is_parent(void)
static void test_talloc_pool_used(void)
static void test_talloc_move(void)
static void test_talloc_parent(void)
static void test_talloc_typed_strndup(void)
static void const * fr_destructor_uctx
static void test_talloc_destructor_add(void)
static void test_talloc_bstr_append(void)
static void test_talloc_reparent(void)
static void test_talloc_deep_hierarchy(void)
static void test_talloc_asprintf(void)
static void test_talloc_reference(void)
static void test_talloc_typed_asprintf(void)
static void test_talloc_naming(void)
static void test_talloc_find_parent_byname(void)
static void test_talloc_typed_strdup(void)
static void test_talloc_basic(void)
static void test_talloc_link_ctx(void)
static void test_talloc_aligned_array(void)
static void test_talloc_free(void)
static void test_talloc_array(void)
static void test_talloc_strdup(void)
static void test_talloc_set_destructor(void)
static void test_talloc_array_null_terminate(void)
static void test_talloc_increase_ref_count(void)
static void test_talloc_strndup_append(void)
static void test_talloc_bstr_realloc(void)
static int fr_destructor_called
static void test_talloc_realloc_zero(void)
static void test_talloc_memcmp_bstr(void)
static void test_talloc_pool(void)
static void test_talloc_memdup(void)
static size_t pool_chunk_footprint(size_t size)
static void test_talloc_total_size(void)
static fr_slen_t parent
Definition pair.h:864
static fr_slen_t data
Definition value.h:1366