1 // SPDX-License-Identifier: MIT
2 // SPDX-FileCopyrightText: 2024 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
5 * rseq memory pool COW race test.
7 * Test that the entire malloc init value is visible in CPU mappings. If
8 * the COW page copy race vs init happens while init is in the middle of
9 * storing to the newly allocated area, iteration on all CPUs comparing
10 * the visible content to the init value is responsible for detecting
11 * and mitigating uninitialized or partially initialized init value from
12 * the point of view of a CPU. Validate that this scheme has the
13 * intended effect wrt a concurrent COW caused by storing to a nearby
14 * per-cpu area on the same page.
33 #include <rseq/rseq.h>
34 #include <rseq/mempool.h>
35 #include "../src/rseq-utils.h"
39 #define TEST_DURATION_S 10 /* seconds */
40 #define TEST_ARRAY_LEN 256
41 #define TEST_STRIDE 16384
49 char c
[TEST_ARRAY_LEN
];
52 struct test_thread_args
{
53 struct rseq_mempool
*mempool
;
54 int phase
; /* enum phase */
56 int stop_writer_thread
;
57 struct test_data
*ptr1
;
58 struct test_data
*ptr2
;
61 struct test_data init_value
;
63 static void *test_init_thread(void *arg
)
65 struct test_thread_args
*thread_args
= (struct test_thread_args
*) arg
;
67 while (!RSEQ_READ_ONCE(thread_args
->stop_init_thread
)) {
68 struct rseq_mempool_attr
*attr
;
69 struct rseq_mempool
*mempool
;
73 attr
= rseq_mempool_attr_create();
74 ret
= rseq_mempool_attr_set_robust(attr
);
77 ret
= rseq_mempool_attr_set_percpu(attr
, TEST_STRIDE
, 1);
80 ret
= rseq_mempool_attr_set_max_nr_ranges(attr
, 1);
83 ret
= rseq_mempool_attr_set_populate_policy(attr
, RSEQ_MEMPOOL_POPULATE_NONE
);
86 mempool
= rseq_mempool_create("test_data", sizeof(struct test_data
), attr
);
89 thread_args
->mempool
= mempool
;
90 rseq_mempool_attr_destroy(attr
);
92 thread_args
->ptr1
= (struct test_data __rseq_percpu
*) rseq_mempool_percpu_malloc(mempool
);
93 if (!thread_args
->ptr1
)
96 rseq_smp_store_release(&thread_args
->phase
, PHASE_WRITE_POOL
);
98 /* malloc init runs concurrently with COW. */
99 thread_args
->ptr2
= (struct test_data __rseq_percpu
*)
100 rseq_mempool_percpu_malloc_init(mempool
,
101 &init_value
, sizeof(struct test_data
));
102 if (!thread_args
->ptr2
)
105 p
= rseq_percpu_ptr(thread_args
->ptr2
, 0);
106 for (i
= 0; i
< TEST_ARRAY_LEN
; i
++) {
107 if (p
->c
[i
] != 0x22) {
108 fprintf(stderr
, "Unexpected value\n");
113 while (rseq_smp_load_acquire(&thread_args
->phase
) != PHASE_RESET_POOL
) { }
115 rseq_mempool_percpu_free(thread_args
->ptr2
, TEST_STRIDE
);
116 rseq_mempool_percpu_free(thread_args
->ptr1
, TEST_STRIDE
);
118 if (rseq_mempool_destroy(mempool
))
121 RSEQ_WRITE_ONCE(thread_args
->stop_writer_thread
, 1);
122 rseq_smp_store_release(&thread_args
->phase
, PHASE_WRITE_POOL
);
126 static void *test_writer_thread(void *arg
)
128 struct test_thread_args
*thread_args
= (struct test_thread_args
*) arg
;
131 unsigned int loop
, delay
;
133 delay
= rand() % 10000;
134 while (rseq_smp_load_acquire(&thread_args
->phase
) != PHASE_WRITE_POOL
) { }
136 if (RSEQ_READ_ONCE(thread_args
->stop_writer_thread
))
139 for (loop
= 0; loop
< delay
; loop
++)
143 rseq_percpu_ptr(thread_args
->ptr1
, 0)->c
[0] = 0x33;
145 rseq_smp_store_release(&thread_args
->phase
, PHASE_RESET_POOL
);
153 struct test_thread_args thread_args
= {};
154 pthread_t writer_thread
, init_thread
;
160 diag("Beginning COW vs malloc init race validation (%u seconds)...", TEST_DURATION_S
);
163 memset(&init_value
.c
, 0x22, TEST_ARRAY_LEN
);
165 thread_args
.phase
= PHASE_RESET_POOL
;
167 ret
= pthread_create(&init_thread
, NULL
, test_init_thread
, &thread_args
);
170 perror("pthread_create");
174 ret
= pthread_create(&writer_thread
, NULL
, test_writer_thread
, &thread_args
);
177 perror("pthread_create");
181 remain
= TEST_DURATION_S
;
183 remain
= sleep(remain
);
184 } while (remain
> 0);
186 RSEQ_WRITE_ONCE(thread_args
.stop_init_thread
, 1);
188 ret
= pthread_join(writer_thread
, NULL
);
191 perror("pthread_join");
195 ret
= pthread_join(init_thread
, NULL
);
198 perror("pthread_join");
202 ok(1, "Validate COW vs malloc init race");