1 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3
4 #include <libarena/common.h>
5
6 #include <libarena/asan.h>
7 #include <libarena/buddy.h>
8
9 extern struct buddy __arena buddy;
10
11 struct segarr_entry {
12 u8 __arena *block;
13 size_t sz;
14 u8 poison;
15 };
16
17 #define SEGARRLEN (512)
18 static struct segarr_entry __arena segarr[SEGARRLEN];
19 static void __arena *ptrs[17];
20 size_t __arena alloc_sizes[] = { 3, 17, 1025, 129, 16350, 333, 9, 517 };
21 size_t __arena alloc_multiple_sizes[] = { 3, 17, 1025, 129, 16350, 333, 9, 517, 2099 };
22 size_t __arena alloc_free_sizes[] = { 3, 17, 64, 129, 256, 333, 512, 517 };
23 size_t __arena alignment_sizes[] = { 1, 3, 7, 8, 9, 15, 16, 17, 31,
24 32, 64, 100, 128, 255, 256, 512, 1000 };
25
26 SEC("syscall")
test_buddy_create(void)27 __weak int test_buddy_create(void)
28 {
29 const int iters = 10;
30 int ret, i;
31
32 for (i = zero; i < iters && can_loop; i++) {
33 ret = buddy_init(&buddy);
34 if (ret)
35 return ret;
36
37 ret = buddy_destroy(&buddy);
38 if (ret)
39 return ret;
40 }
41
42 return 0;
43 }
44
45 SEC("syscall")
test_buddy_alloc(void)46 __weak int test_buddy_alloc(void)
47 {
48 void __arena *mem;
49 int ret, i;
50
51 for (i = zero; i < 8 && can_loop; i++) {
52 ret = buddy_init(&buddy);
53 if (ret)
54 return ret;
55
56 mem = buddy_alloc(&buddy, alloc_sizes[i]);
57 if (!mem) {
58 buddy_destroy(&buddy);
59 return -ENOMEM;
60 }
61
62 buddy_destroy(&buddy);
63 }
64
65 return 0;
66 }
67
68 SEC("syscall")
test_buddy_alloc_free(void)69 __weak int test_buddy_alloc_free(void)
70 {
71 const int iters = 800;
72 void __arena *mem;
73 int ret, i;
74
75 ret = buddy_init(&buddy);
76 if (ret)
77 return ret;
78
79 for (i = zero; i < iters && can_loop; i++) {
80 mem = buddy_alloc(&buddy, alloc_free_sizes[(i * 5) % 8]);
81 if (!mem) {
82 buddy_destroy(&buddy);
83 return -ENOMEM;
84 }
85
86 buddy_free(&buddy, mem);
87 }
88
89 buddy_destroy(&buddy);
90
91 return 0;
92 }
93
94 SEC("syscall")
test_buddy_alloc_multiple(void)95 __weak int test_buddy_alloc_multiple(void)
96 {
97 int ret, j;
98 u32 i, idx;
99 u8 __arena *mem;
100 size_t sz;
101 u8 poison;
102
103 ret = buddy_init(&buddy);
104 if (ret)
105 return ret;
106
107 /*
108 * Cycle through each size, allocating an entry in the
109 * segarr. Continue for SEGARRLEN iterations. For every
110 * allocation write down the size, use the current index
111 * as a poison value, and log it with the pointer in the
112 * segarr entry. Use the poison value to poison the entire
113 * allocated memory according to the size given.
114 */
115 for (i = zero; i < SEGARRLEN && can_loop; i++) {
116 sz = alloc_multiple_sizes[i % 9];
117 poison = (u8)i;
118
119 mem = buddy_alloc(&buddy, sz);
120 if (!mem) {
121 buddy_destroy(&buddy);
122 arena_stdout("%s:%d", __func__, __LINE__);
123 return -ENOMEM;
124 }
125
126 segarr[i].block = mem;
127 segarr[i].sz = sz;
128 segarr[i].poison = poison;
129
130 for (j = zero; j < sz && can_loop; j++) {
131 mem[j] = poison;
132 if (mem[j] != poison) {
133 buddy_destroy(&buddy);
134 return -EINVAL;
135 }
136 }
137 }
138
139 /*
140 * Go to (i * 17) % SEGARRLEN, and free the block pointed to.
141 * Before freeing, check all bytes have the poisoned value
142 * corresponding to the element. If any values are unexpected,
143 * return an error. Skip some elements to test destroying the
144 * buddy allocator while data is still allocated.
145 */
146 for (i = 10; i < SEGARRLEN && can_loop; i++) {
147 idx = (i * 17) % SEGARRLEN;
148
149 mem = segarr[idx].block;
150 sz = segarr[idx].sz;
151 poison = segarr[idx].poison;
152
153 for (j = zero; j < sz && can_loop; j++) {
154 if (mem[j] != poison) {
155 buddy_destroy(&buddy);
156 arena_stdout("%s:%d %lx %u vs %u", __func__,
157 __LINE__, (uintptr_t)&mem[j],
158 mem[j], poison);
159 return -EINVAL;
160 }
161 }
162
163 buddy_free(&buddy, mem);
164 }
165
166 buddy_destroy(&buddy);
167
168 return 0;
169 }
170
171 SEC("syscall")
test_buddy_alignment(void)172 __weak int test_buddy_alignment(void)
173 {
174 int ret;
175 u32 i;
176
177 ret = buddy_init(&buddy);
178 if (ret)
179 return ret;
180
181 /* Allocate various sizes and check alignment */
182 for (i = zero; i < 17 && can_loop; i++) {
183 barrier_var(i);
184 ptrs[i] = buddy_alloc(&buddy, alignment_sizes[i]);
185 if (!ptrs[i]) {
186 arena_stdout("alignment test: alloc failed for size %lu",
187 alignment_sizes[i]);
188 buddy_destroy(&buddy);
189 return -ENOMEM;
190 }
191
192 /* Check 8-byte alignment */
193 if ((u64)ptrs[i] & 0x7) {
194 arena_stdout(
195 "alignment test: ptr %llx not 8-byte aligned (size %lu)",
196 (u64)ptrs[i], alignment_sizes[i]);
197 buddy_destroy(&buddy);
198 return -EINVAL;
199 }
200 }
201
202 /* Free all allocations */
203 for (i = zero; i < 17 && can_loop; i++) {
204 barrier_var(i);
205 buddy_free(&buddy, ptrs[i]);
206 }
207
208 buddy_destroy(&buddy);
209
210 return 0;
211 }
212
213 __weak char _license[] SEC("license") = "GPL";
214