mempool test: test populate none policy
[librseq.git] / tests / mempool_test.c
CommitLineData
d273fd4b
MD
1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: 2024 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
3/*
4 * rseq memory pool test.
5 */
6
7#ifndef _GNU_SOURCE
8#define _GNU_SOURCE
9#endif
10#include <assert.h>
11#include <sched.h>
12#include <signal.h>
13#include <stdio.h>
14#include <string.h>
15#include <sys/time.h>
16#include <inttypes.h>
17#include <stdlib.h>
c15b99f6
OD
18#include <sys/wait.h>
19#include <unistd.h>
d273fd4b
MD
20
21#include <rseq/mempool.h>
c3fad074 22#include "../src/rseq-utils.h"
d273fd4b
MD
23
24#include "list.h"
25#include "tap.h"
26
455e090e
MD
27#if RSEQ_BITS_PER_LONG == 64
28# define POISON_VALUE 0xABCDABCDABCDABCDULL
29#else
30# define POISON_VALUE 0xABCDABCDUL
31#endif
32
d273fd4b 33struct test_data {
5e000535 34 uintptr_t value[2];
d273fd4b
MD
35 struct test_data __rseq_percpu *backref;
36 struct list_head node;
37};
38
53569d03
MD
39static void test_mempool_fill(enum rseq_mempool_populate_policy policy,
40 unsigned long max_nr_ranges, size_t stride)
d273fd4b
MD
41{
42 struct test_data __rseq_percpu *ptr;
84a5a73a 43 struct test_data *iter, *tmp;
0ba2a93e
MD
44 struct rseq_mempool *mempool;
45 struct rseq_mempool_attr *attr;
d273fd4b
MD
46 uint64_t count = 0;
47 LIST_HEAD(list);
5e000535 48 int ret, i, size_order;
e62b6d54
MD
49 struct test_data init_value = {
50 .value = {
51 123,
52 456,
53 },
54 .backref = NULL,
55 .node = {},
56 };
d273fd4b 57
0ba2a93e 58 attr = rseq_mempool_attr_create();
a8ad787a 59 ok(attr, "Create pool attribute");
0ba2a93e 60 ret = rseq_mempool_attr_set_robust(attr);
d273fd4b 61 ok(ret == 0, "Setting mempool robust attribute");
cb475906
MD
62 ret = rseq_mempool_attr_set_percpu(attr, stride, CPU_SETSIZE);
63 ok(ret == 0, "Setting mempool percpu type");
dcb59d50
MD
64 ret = rseq_mempool_attr_set_max_nr_ranges(attr, max_nr_ranges);
65 ok(ret == 0, "Setting mempool max_nr_ranges=%lu", max_nr_ranges);
455e090e
MD
66 ret = rseq_mempool_attr_set_poison(attr, POISON_VALUE);
67 ok(ret == 0, "Setting mempool poison");
53569d03
MD
68 ret = rseq_mempool_attr_set_populate_policy(attr, policy);
69 ok(ret == 0, "Setting mempool populate policy to %s",
70 policy == RSEQ_MEMPOOL_POPULATE_NONE ? "NONE" : "ALL");
0ba2a93e 71 mempool = rseq_mempool_create("test_data",
cb475906 72 sizeof(struct test_data), attr);
f2981623 73 ok(mempool, "Create mempool of size %zu", stride);
0ba2a93e 74 rseq_mempool_attr_destroy(attr);
d273fd4b
MD
75
76 for (;;) {
77 struct test_data *cpuptr;
78
15da5c27 79 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_zmalloc(mempool);
d273fd4b
MD
80 if (!ptr)
81 break;
82 /* Link items in cpu 0. */
06e0b1c0 83 cpuptr = rseq_percpu_ptr(ptr, 0, stride);
d273fd4b
MD
84 cpuptr->backref = ptr;
85 /* Randomize items in list. */
86 if (count & 1)
87 list_add(&cpuptr->node, &list);
88 else
89 list_add_tail(&cpuptr->node, &list);
90 count++;
91 }
92
5e000535
MD
93 size_order = rseq_get_count_order_ulong(sizeof(struct test_data));
94 ok(count * (1U << size_order) == stride * max_nr_ranges,
dcb59d50 95 "Allocated %" PRIu64 " objects in pool", count);
d273fd4b
MD
96
97 list_for_each_entry(iter, &list, node) {
98 ptr = iter->backref;
99 for (i = 0; i < CPU_SETSIZE; i++) {
06e0b1c0 100 struct test_data *cpuptr = rseq_percpu_ptr(ptr, i, stride);
d273fd4b 101
5e000535 102 if (cpuptr->value[0] != 0)
d273fd4b 103 abort();
5e000535 104 cpuptr->value[0]++;
d273fd4b
MD
105 }
106 }
d273fd4b
MD
107 ok(1, "Check for pool content corruption");
108
84a5a73a 109 list_for_each_entry_safe(iter, tmp, &list, node) {
d273fd4b 110 ptr = iter->backref;
15da5c27 111 rseq_mempool_percpu_free(ptr, stride);
d273fd4b 112 }
c6a3de0e
MD
113 ok(1, "Free all objects");
114
115 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_zmalloc(mempool);
116 if (!ptr)
117 abort();
118 ok(1, "Allocate one object");
119
120 rseq_mempool_percpu_free(ptr, stride);
121 ok(1, "Free one object");
122
e62b6d54
MD
123 ptr = (struct test_data __rseq_percpu *)
124 rseq_mempool_percpu_malloc_init(mempool,
125 &init_value, sizeof(struct test_data));
126 if (!ptr)
127 abort();
128 ok(1, "Allocate one initialized object");
129
130 ok(ptr->value[0] == 123 && ptr->value[1] == 456, "Validate initial values");
131
132 rseq_mempool_percpu_free(ptr, stride);
133 ok(1, "Free one object");
134
0ba2a93e 135 ret = rseq_mempool_destroy(mempool);
d273fd4b
MD
136 ok(ret == 0, "Destroy mempool");
137}
138
0ba2a93e 139static void test_robust_double_free(struct rseq_mempool *pool)
c15b99f6
OD
140{
141 struct test_data __rseq_percpu *ptr;
142
15da5c27 143 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_malloc(pool);
c15b99f6 144
15da5c27
MD
145 rseq_mempool_percpu_free(ptr);
146 rseq_mempool_percpu_free(ptr);
c15b99f6
OD
147}
148
0ba2a93e 149static void test_robust_corrupt_after_free(struct rseq_mempool *pool)
c15b99f6
OD
150{
151 struct test_data __rseq_percpu *ptr;
152 struct test_data *cpuptr;
153
15da5c27 154 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_malloc(pool);
c15b99f6
OD
155 cpuptr = (struct test_data *) rseq_percpu_ptr(ptr, 0);
156
15da5c27 157 rseq_mempool_percpu_free(ptr);
5e000535 158 cpuptr->value[0] = (uintptr_t) test_robust_corrupt_after_free;
c15b99f6 159
0ba2a93e 160 rseq_mempool_destroy(pool);
c15b99f6
OD
161}
162
0ba2a93e 163static void test_robust_memory_leak(struct rseq_mempool *pool)
c15b99f6 164{
15da5c27 165 (void) rseq_mempool_percpu_malloc(pool);
c15b99f6 166
0ba2a93e 167 rseq_mempool_destroy(pool);
c15b99f6
OD
168}
169
0ba2a93e 170static void test_robust_free_list_corruption(struct rseq_mempool *pool)
c15b99f6
OD
171{
172 struct test_data __rseq_percpu *ptr;
173 struct test_data *cpuptr;
174
15da5c27 175 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_malloc(pool);
c15b99f6
OD
176 cpuptr = (struct test_data *) rseq_percpu_ptr(ptr, 0);
177
15da5c27 178 rseq_mempool_percpu_free(ptr);
c15b99f6 179
5e000535 180 cpuptr->value[0] = (uintptr_t) cpuptr;
c15b99f6 181
15da5c27
MD
182 (void) rseq_mempool_percpu_malloc(pool);
183 (void) rseq_mempool_percpu_malloc(pool);
c15b99f6
OD
184}
185
5e000535
MD
186static void test_robust_poison_corruption_malloc(struct rseq_mempool *pool)
187{
188 struct test_data __rseq_percpu *ptr;
189 struct test_data *cpuptr;
190
191 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_malloc(pool);
192 cpuptr = (struct test_data *) rseq_percpu_ptr(ptr, 0);
193
194 rseq_mempool_percpu_free(ptr);
195
196 cpuptr->value[1] = 1;
197
198 (void) rseq_mempool_percpu_malloc(pool);
199}
200
201static void test_robust_poison_corruption_destroy(struct rseq_mempool *pool)
202{
203 struct test_data __rseq_percpu *ptr;
204 struct test_data *cpuptr;
205
206 ptr = (struct test_data __rseq_percpu *) rseq_mempool_percpu_malloc(pool);
207 cpuptr = (struct test_data *) rseq_percpu_ptr(ptr, 0);
208
209 rseq_mempool_percpu_free(ptr);
210
211 cpuptr->value[1] = 1;
212
213 rseq_mempool_destroy(pool);
214}
215
0ba2a93e
MD
216static int run_robust_test(void (*test)(struct rseq_mempool*),
217 struct rseq_mempool *pool)
c15b99f6
OD
218{
219 pid_t cpid;
220 int status;
221
222 cpid = fork();
223
224 switch (cpid) {
225 case -1:
226 return 0;
227 case 0:
228 test(pool);
229 _exit(EXIT_FAILURE);
230 default:
231 waitpid(cpid, &status, 0);
232 }
233
234 if (WIFSIGNALED(status) &&
235 (SIGABRT == WTERMSIG(status)))
236 return 1;
237
238 return 0;
239}
240
53569d03 241static void run_robust_tests(enum rseq_mempool_populate_policy policy)
c15b99f6 242{
0ba2a93e
MD
243 struct rseq_mempool_attr *attr;
244 struct rseq_mempool *pool;
cb475906 245 int ret;
c15b99f6 246
0ba2a93e 247 attr = rseq_mempool_attr_create();
cb475906
MD
248 ok(attr, "Create mempool attributes");
249
250 ret = rseq_mempool_attr_set_robust(attr);
251 ok(ret == 0, "Setting mempool robust attribute");
c15b99f6 252
cb475906
MD
253 ret = rseq_mempool_attr_set_percpu(attr, RSEQ_MEMPOOL_STRIDE, 1);
254 ok(ret == 0, "Setting mempool percpu type");
c15b99f6 255
53569d03
MD
256 ret = rseq_mempool_attr_set_populate_policy(attr, policy);
257 ok(ret == 0, "Setting mempool populate policy to %s",
258 policy == RSEQ_MEMPOOL_POPULATE_NONE ? "NONE" : "ALL");
a5694a4d 259
0ba2a93e 260 pool = rseq_mempool_create("mempool-robust",
5e000535 261 sizeof(struct test_data), attr);
c15b99f6 262
0ba2a93e 263 rseq_mempool_attr_destroy(attr);
c15b99f6
OD
264
265 ok(run_robust_test(test_robust_double_free, pool),
266 "robust-double-free");
267
c15b99f6
OD
268 ok(run_robust_test(test_robust_memory_leak, pool),
269 "robust-memory-leak");
270
5e000535
MD
271 ok(run_robust_test(test_robust_poison_corruption_malloc, pool),
272 "robust-poison-corruption-malloc");
273
274 ok(run_robust_test(test_robust_poison_corruption_destroy, pool),
275 "robust-poison-corruption-destroy");
276
53569d03
MD
277 /*
278 * Those tests expect the freelist to be on CPU 0's mapping,
279 * which is only the case for populate all policy.
280 */
281 if (policy == RSEQ_MEMPOOL_POPULATE_ALL) {
282 ok(run_robust_test(test_robust_corrupt_after_free, pool),
283 "robust-corrupt-after-free");
284 ok(run_robust_test(test_robust_free_list_corruption, pool),
285 "robust-free-list-corruption");
286 }
287
0ba2a93e 288 rseq_mempool_destroy(pool);
c15b99f6
OD
289}
290
d273fd4b
MD
291int main(void)
292{
293 size_t len;
dcb59d50 294 unsigned long nr_ranges;
d273fd4b 295
579badcb
MD
296 plan_no_plan();
297
dcb59d50
MD
298 for (nr_ranges = 1; nr_ranges < 32; nr_ranges <<= 1) {
299 /* From page size to 64kB */
300 for (len = rseq_get_page_len(); len < 65536; len <<= 1) {
53569d03
MD
301 test_mempool_fill(RSEQ_MEMPOOL_POPULATE_ALL, nr_ranges, len);
302 test_mempool_fill(RSEQ_MEMPOOL_POPULATE_NONE, nr_ranges, len);
dcb59d50 303 }
d273fd4b
MD
304 }
305
dcb59d50
MD
306 len = rseq_get_page_len();
307 if (len < 65536)
308 len = 65536;
309 /* From min(page size, 64kB) to 4MB */
53569d03
MD
310 for (; len < 4096 * 1024; len <<= 1) {
311 test_mempool_fill(RSEQ_MEMPOOL_POPULATE_ALL, 1, len);
312 test_mempool_fill(RSEQ_MEMPOOL_POPULATE_NONE, 1, len);
313 }
dcb59d50 314
53569d03
MD
315 run_robust_tests(RSEQ_MEMPOOL_POPULATE_ALL);
316 run_robust_tests(RSEQ_MEMPOOL_POPULATE_NONE);
c15b99f6 317
d273fd4b
MD
318 exit(exit_status());
319}
This page took 0.040476 seconds and 4 git commands to generate.