xref: /xnu-12377.41.6/tests/restartable_ranges.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1*bbb1b6f9SApple OSS Distributions #include <darwintest.h>
2*bbb1b6f9SApple OSS Distributions #include <kern/restartable.h>
3*bbb1b6f9SApple OSS Distributions #include <mach/mach.h>
4*bbb1b6f9SApple OSS Distributions #include <mach/task.h>
5*bbb1b6f9SApple OSS Distributions #include <os/atomic_private.h>
6*bbb1b6f9SApple OSS Distributions #include <pthread.h>
7*bbb1b6f9SApple OSS Distributions #include <signal.h>
8*bbb1b6f9SApple OSS Distributions #include <stdbool.h>
9*bbb1b6f9SApple OSS Distributions #include <sys/mman.h>
10*bbb1b6f9SApple OSS Distributions #include <unistd.h>
11*bbb1b6f9SApple OSS Distributions #include <dispatch/dispatch.h>
12*bbb1b6f9SApple OSS Distributions 
13*bbb1b6f9SApple OSS Distributions T_GLOBAL_META(
14*bbb1b6f9SApple OSS Distributions 	T_META_NAMESPACE("xnu"),
15*bbb1b6f9SApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
16*bbb1b6f9SApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("all"),
17*bbb1b6f9SApple OSS Distributions 	T_META_RUN_CONCURRENTLY(true));
18*bbb1b6f9SApple OSS Distributions 
19*bbb1b6f9SApple OSS Distributions extern task_restartable_range_t ranges[2];
20*bbb1b6f9SApple OSS Distributions static int step = 0;
21*bbb1b6f9SApple OSS Distributions 
22*bbb1b6f9SApple OSS Distributions extern void restartable_function(int *);
23*bbb1b6f9SApple OSS Distributions 
24*bbb1b6f9SApple OSS Distributions #if defined(__x86_64__)
25*bbb1b6f9SApple OSS Distributions __asm__("    .align 4\n"
26*bbb1b6f9SApple OSS Distributions         "    .text\n"
27*bbb1b6f9SApple OSS Distributions         "    .private_extern _restartable_function\n"
28*bbb1b6f9SApple OSS Distributions         "_restartable_function:\n"
29*bbb1b6f9SApple OSS Distributions         "    incl   (%rdi)\n"
30*bbb1b6f9SApple OSS Distributions         "1:\n"
31*bbb1b6f9SApple OSS Distributions         "    pause\n"
32*bbb1b6f9SApple OSS Distributions         "    jmp 1b\n"
33*bbb1b6f9SApple OSS Distributions         "LExit_restartable_function:\n"
34*bbb1b6f9SApple OSS Distributions         "    ret\n");
35*bbb1b6f9SApple OSS Distributions #elif defined(__arm64__)
36*bbb1b6f9SApple OSS Distributions __asm__("    .align 4\n"
37*bbb1b6f9SApple OSS Distributions         "    .text\n"
38*bbb1b6f9SApple OSS Distributions         "    .private_extern _restartable_function\n"
39*bbb1b6f9SApple OSS Distributions         "_restartable_function:\n"
40*bbb1b6f9SApple OSS Distributions         "    ldr    x11, [x0]\n"
41*bbb1b6f9SApple OSS Distributions         "    add    x11, x11, #1\n"
42*bbb1b6f9SApple OSS Distributions         "    str    x11, [x0]\n"
43*bbb1b6f9SApple OSS Distributions         "1:\n"
44*bbb1b6f9SApple OSS Distributions         "    b 1b\n"
45*bbb1b6f9SApple OSS Distributions         "LExit_restartable_function:\n"
46*bbb1b6f9SApple OSS Distributions         "    ret\n");
47*bbb1b6f9SApple OSS Distributions #else
48*bbb1b6f9SApple OSS Distributions #define SKIP_TEST 1
49*bbb1b6f9SApple OSS Distributions #endif
50*bbb1b6f9SApple OSS Distributions 
51*bbb1b6f9SApple OSS Distributions extern uint64_t __thread_selfid(void);
52*bbb1b6f9SApple OSS Distributions extern void fake_msgSend(void * _Nullable);
53*bbb1b6f9SApple OSS Distributions 
54*bbb1b6f9SApple OSS Distributions #if defined(__x86_64__)
55*bbb1b6f9SApple OSS Distributions __asm__("    .align 4\n"
56*bbb1b6f9SApple OSS Distributions         "    .text\n"
57*bbb1b6f9SApple OSS Distributions         "    .private_extern _fake_msgSend\n"
58*bbb1b6f9SApple OSS Distributions         "_fake_msgSend:\n"
59*bbb1b6f9SApple OSS Distributions         "    movq   (%rdi), %rax\n"             /* load isa */
60*bbb1b6f9SApple OSS Distributions         "1:\n"
61*bbb1b6f9SApple OSS Distributions         "    movq   16(%rax), %rcx\n"           /* load buckets */
62*bbb1b6f9SApple OSS Distributions         "    movq   (%rcx), %rcx\n"             /* load selector */
63*bbb1b6f9SApple OSS Distributions         "LRecover_fake_msgSend:\n"
64*bbb1b6f9SApple OSS Distributions         "    jmp    1b\n"
65*bbb1b6f9SApple OSS Distributions         "LExit_fake_msgSend:\n"
66*bbb1b6f9SApple OSS Distributions         "    ret\n");
67*bbb1b6f9SApple OSS Distributions #elif defined(__arm64__)
68*bbb1b6f9SApple OSS Distributions __asm__("    .align 4\n"
69*bbb1b6f9SApple OSS Distributions         "    .text\n"
70*bbb1b6f9SApple OSS Distributions         "    .private_extern _fake_msgSend\n"
71*bbb1b6f9SApple OSS Distributions         "_fake_msgSend:\n"
72*bbb1b6f9SApple OSS Distributions         "    ldr    x16, [x0]\n"                /* load isa */
73*bbb1b6f9SApple OSS Distributions         "1:\n"
74*bbb1b6f9SApple OSS Distributions #if __LP64__
75*bbb1b6f9SApple OSS Distributions         "    ldr    x11, [x16, #16]\n"          /* load buckets */
76*bbb1b6f9SApple OSS Distributions #else
77*bbb1b6f9SApple OSS Distributions         "    ldr    x11, [x16, #8]\n"           /* load buckets */
78*bbb1b6f9SApple OSS Distributions #endif
79*bbb1b6f9SApple OSS Distributions         "    ldr    x17, [x11]\n"               /* load selector */
80*bbb1b6f9SApple OSS Distributions         "LRecover_fake_msgSend:\n"
81*bbb1b6f9SApple OSS Distributions         "    b      1b\n"
82*bbb1b6f9SApple OSS Distributions         "LExit_fake_msgSend:\n"
83*bbb1b6f9SApple OSS Distributions         "    ret\n");
84*bbb1b6f9SApple OSS Distributions #else
85*bbb1b6f9SApple OSS Distributions #define SKIP_TEST 1
86*bbb1b6f9SApple OSS Distributions #endif
87*bbb1b6f9SApple OSS Distributions 
88*bbb1b6f9SApple OSS Distributions #ifndef SKIP_TEST
89*bbb1b6f9SApple OSS Distributions 
90*bbb1b6f9SApple OSS Distributions __asm__("    .align 4\n"
91*bbb1b6f9SApple OSS Distributions         "    .data\n"
92*bbb1b6f9SApple OSS Distributions         "    .private_extern _ranges\n"
93*bbb1b6f9SApple OSS Distributions         "_ranges:\n"
94*bbb1b6f9SApple OSS Distributions #if __LP64__
95*bbb1b6f9SApple OSS Distributions         "    .quad _restartable_function\n"
96*bbb1b6f9SApple OSS Distributions #else
97*bbb1b6f9SApple OSS Distributions         "    .long _restartable_function\n"
98*bbb1b6f9SApple OSS Distributions         "    .long 0\n"
99*bbb1b6f9SApple OSS Distributions #endif
100*bbb1b6f9SApple OSS Distributions         "    .short LExit_restartable_function - _restartable_function\n"
101*bbb1b6f9SApple OSS Distributions         "    .short LExit_restartable_function - _restartable_function\n"
102*bbb1b6f9SApple OSS Distributions         "    .long 0\n"
103*bbb1b6f9SApple OSS Distributions         "\n"
104*bbb1b6f9SApple OSS Distributions #if __LP64__
105*bbb1b6f9SApple OSS Distributions         "    .quad _fake_msgSend\n"
106*bbb1b6f9SApple OSS Distributions #else
107*bbb1b6f9SApple OSS Distributions         "    .long _fake_msgSend\n"
108*bbb1b6f9SApple OSS Distributions         "    .long 0\n"
109*bbb1b6f9SApple OSS Distributions #endif
110*bbb1b6f9SApple OSS Distributions         "    .short LExit_fake_msgSend - _fake_msgSend\n"
111*bbb1b6f9SApple OSS Distributions         "    .short LRecover_fake_msgSend - _fake_msgSend\n"
112*bbb1b6f9SApple OSS Distributions         "    .long 0\n");
113*bbb1b6f9SApple OSS Distributions 
114*bbb1b6f9SApple OSS Distributions static void
noop_signal(int signo __unused)115*bbb1b6f9SApple OSS Distributions noop_signal(int signo __unused)
116*bbb1b6f9SApple OSS Distributions {
117*bbb1b6f9SApple OSS Distributions }
118*bbb1b6f9SApple OSS Distributions 
119*bbb1b6f9SApple OSS Distributions static void *
task_restartable_ranges_thread(void * _ctx)120*bbb1b6f9SApple OSS Distributions task_restartable_ranges_thread(void *_ctx)
121*bbb1b6f9SApple OSS Distributions {
122*bbb1b6f9SApple OSS Distributions 	int *stepp = _ctx;
123*bbb1b6f9SApple OSS Distributions 	restartable_function(stepp); // increments step
124*bbb1b6f9SApple OSS Distributions 	T_PASS("was successfully restarted\n");
125*bbb1b6f9SApple OSS Distributions 	(*stepp)++;
126*bbb1b6f9SApple OSS Distributions 	return NULL;
127*bbb1b6f9SApple OSS Distributions }
128*bbb1b6f9SApple OSS Distributions 
129*bbb1b6f9SApple OSS Distributions static void
wait_for_step(int which)130*bbb1b6f9SApple OSS Distributions wait_for_step(int which)
131*bbb1b6f9SApple OSS Distributions {
132*bbb1b6f9SApple OSS Distributions 	for (int i = 0; step != which && i < 10; i++) {
133*bbb1b6f9SApple OSS Distributions 		usleep(100000);
134*bbb1b6f9SApple OSS Distributions 	}
135*bbb1b6f9SApple OSS Distributions }
136*bbb1b6f9SApple OSS Distributions 
137*bbb1b6f9SApple OSS Distributions #endif
138*bbb1b6f9SApple OSS Distributions 
139*bbb1b6f9SApple OSS Distributions T_DECL(task_restartable_ranges, "test task_restartable_ranges")
140*bbb1b6f9SApple OSS Distributions {
141*bbb1b6f9SApple OSS Distributions #ifdef SKIP_TEST
142*bbb1b6f9SApple OSS Distributions 	T_SKIP("Not supported");
143*bbb1b6f9SApple OSS Distributions #else
144*bbb1b6f9SApple OSS Distributions 	kern_return_t kr;
145*bbb1b6f9SApple OSS Distributions 	pthread_t th;
146*bbb1b6f9SApple OSS Distributions 	int rc;
147*bbb1b6f9SApple OSS Distributions 
148*bbb1b6f9SApple OSS Distributions 	signal(SIGUSR1, noop_signal);
149*bbb1b6f9SApple OSS Distributions 
150*bbb1b6f9SApple OSS Distributions 	kr = task_restartable_ranges_register(mach_task_self(), ranges, 2);
151*bbb1b6f9SApple OSS Distributions 	T_ASSERT_MACH_SUCCESS(kr, "task_restartable_ranges_register");
152*bbb1b6f9SApple OSS Distributions 
153*bbb1b6f9SApple OSS Distributions 	{
154*bbb1b6f9SApple OSS Distributions 		rc = pthread_create(&th, NULL, &task_restartable_ranges_thread, &step);
155*bbb1b6f9SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
156*bbb1b6f9SApple OSS Distributions 
157*bbb1b6f9SApple OSS Distributions 		wait_for_step(1);
158*bbb1b6f9SApple OSS Distributions 		T_ASSERT_EQ(step, 1, "The thread started (sync)");
159*bbb1b6f9SApple OSS Distributions 
160*bbb1b6f9SApple OSS Distributions 		kr = task_restartable_ranges_synchronize(mach_task_self());
161*bbb1b6f9SApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(kr, "task_restartable_ranges_synchronize");
162*bbb1b6f9SApple OSS Distributions 
163*bbb1b6f9SApple OSS Distributions 		T_LOG("wait for the function to be restarted (sync)");
164*bbb1b6f9SApple OSS Distributions 		wait_for_step(2);
165*bbb1b6f9SApple OSS Distributions 		T_ASSERT_EQ(step, 2, "The thread exited (sync)");
166*bbb1b6f9SApple OSS Distributions 		pthread_join(th, NULL);
167*bbb1b6f9SApple OSS Distributions 	}
168*bbb1b6f9SApple OSS Distributions 
169*bbb1b6f9SApple OSS Distributions 	{
170*bbb1b6f9SApple OSS Distributions 		rc = pthread_create(&th, NULL, &task_restartable_ranges_thread, &step);
171*bbb1b6f9SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
172*bbb1b6f9SApple OSS Distributions 
173*bbb1b6f9SApple OSS Distributions 		wait_for_step(3);
174*bbb1b6f9SApple OSS Distributions 		T_ASSERT_EQ(step, 3, "The thread started (signal)");
175*bbb1b6f9SApple OSS Distributions 
176*bbb1b6f9SApple OSS Distributions 		rc = pthread_kill(th, SIGUSR1);
177*bbb1b6f9SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(rc, "pthread_kill");
178*bbb1b6f9SApple OSS Distributions 
179*bbb1b6f9SApple OSS Distributions 		T_LOG("wait for the function to be restarted (signal)");
180*bbb1b6f9SApple OSS Distributions 		wait_for_step(4);
181*bbb1b6f9SApple OSS Distributions 		T_ASSERT_EQ(step, 4, "The thread exited (signal)");
182*bbb1b6f9SApple OSS Distributions 		pthread_join(th, NULL);
183*bbb1b6f9SApple OSS Distributions 	}
184*bbb1b6f9SApple OSS Distributions #endif
185*bbb1b6f9SApple OSS Distributions }
186*bbb1b6f9SApple OSS Distributions 
187*bbb1b6f9SApple OSS Distributions #ifndef SKIP_TEST
188*bbb1b6f9SApple OSS Distributions 
189*bbb1b6f9SApple OSS Distributions #define N_BUCKETS 4
190*bbb1b6f9SApple OSS Distributions struct bucket {
191*bbb1b6f9SApple OSS Distributions 	char buf[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE)));
192*bbb1b6f9SApple OSS Distributions };
193*bbb1b6f9SApple OSS Distributions 
194*bbb1b6f9SApple OSS Distributions static struct bucket arena[N_BUCKETS];
195*bbb1b6f9SApple OSS Distributions static size_t arena_cur = 1;
196*bbb1b6f9SApple OSS Distributions 
197*bbb1b6f9SApple OSS Distributions static void *cls[5] = { 0, 0, &arena[0], 0, 0 }; /* our fake objc Class */
198*bbb1b6f9SApple OSS Distributions static void *obj[4] = { cls, 0, 0, 0, };         /* our fake objc object */
199*bbb1b6f9SApple OSS Distributions 
200*bbb1b6f9SApple OSS Distributions static volatile long syncs = 1;
201*bbb1b6f9SApple OSS Distributions 
202*bbb1b6f9SApple OSS Distributions static void *
arena_alloc(void)203*bbb1b6f9SApple OSS Distributions arena_alloc(void)
204*bbb1b6f9SApple OSS Distributions {
205*bbb1b6f9SApple OSS Distributions 	struct bucket *p = &arena[arena_cur++ % N_BUCKETS];
206*bbb1b6f9SApple OSS Distributions 
207*bbb1b6f9SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(p, PAGE_MAX_SIZE,
208*bbb1b6f9SApple OSS Distributions 	    PROT_READ | PROT_WRITE), "arena_alloc");
209*bbb1b6f9SApple OSS Distributions 
210*bbb1b6f9SApple OSS Distributions 	return p;
211*bbb1b6f9SApple OSS Distributions }
212*bbb1b6f9SApple OSS Distributions 
213*bbb1b6f9SApple OSS Distributions static void
arena_free(void * p)214*bbb1b6f9SApple OSS Distributions arena_free(void *p)
215*bbb1b6f9SApple OSS Distributions {
216*bbb1b6f9SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(p, PAGE_MAX_SIZE,
217*bbb1b6f9SApple OSS Distributions 	    PROT_NONE), "arena_free");
218*bbb1b6f9SApple OSS Distributions }
219*bbb1b6f9SApple OSS Distributions 
220*bbb1b6f9SApple OSS Distributions static void
task_restartable_ranges_race_fail(int signo)221*bbb1b6f9SApple OSS Distributions task_restartable_ranges_race_fail(int signo)
222*bbb1b6f9SApple OSS Distributions {
223*bbb1b6f9SApple OSS Distributions 	T_FAIL("test crashed with signal %s after %d syncs",
224*bbb1b6f9SApple OSS Distributions 	    strsignal(signo), syncs);
225*bbb1b6f9SApple OSS Distributions 	T_END;
226*bbb1b6f9SApple OSS Distributions }
227*bbb1b6f9SApple OSS Distributions 
228*bbb1b6f9SApple OSS Distributions #endif
229*bbb1b6f9SApple OSS Distributions 
230*bbb1b6f9SApple OSS Distributions T_DECL(task_restartable_ranges_race, "test for 88873668")
231*bbb1b6f9SApple OSS Distributions {
232*bbb1b6f9SApple OSS Distributions #ifdef SKIP_TEST
233*bbb1b6f9SApple OSS Distributions 	T_SKIP("Not supported");
234*bbb1b6f9SApple OSS Distributions #else
235*bbb1b6f9SApple OSS Distributions 	kern_return_t kr;
236*bbb1b6f9SApple OSS Distributions 	pthread_t th;
237*bbb1b6f9SApple OSS Distributions 	void *old;
238*bbb1b6f9SApple OSS Distributions 	int rc;
239*bbb1b6f9SApple OSS Distributions 
240*bbb1b6f9SApple OSS Distributions 	signal(SIGBUS, task_restartable_ranges_race_fail);
241*bbb1b6f9SApple OSS Distributions 
242*bbb1b6f9SApple OSS Distributions 	kr = task_restartable_ranges_register(mach_task_self(), ranges, 2);
243*bbb1b6f9SApple OSS Distributions 	T_ASSERT_MACH_SUCCESS(kr, "task_restartable_ranges_register");
244*bbb1b6f9SApple OSS Distributions 
245*bbb1b6f9SApple OSS Distributions 	dispatch_async_f(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0),
246*bbb1b6f9SApple OSS Distributions 	    obj, fake_msgSend);
247*bbb1b6f9SApple OSS Distributions 
248*bbb1b6f9SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(&arena[1],
249*bbb1b6f9SApple OSS Distributions 	    (N_BUCKETS - 1) * PAGE_MAX_SIZE, PROT_NONE), "arena_init");
250*bbb1b6f9SApple OSS Distributions 
251*bbb1b6f9SApple OSS Distributions 	long step  = 16 << 10;
252*bbb1b6f9SApple OSS Distributions 	long count = 16;
253*bbb1b6f9SApple OSS Distributions 
254*bbb1b6f9SApple OSS Distributions 	for (syncs = 1; syncs <= count * step; syncs++) {
255*bbb1b6f9SApple OSS Distributions 		/*
256*bbb1b6f9SApple OSS Distributions 		 * Simulate obj-c's algorithm:
257*bbb1b6f9SApple OSS Distributions 		 *
258*bbb1b6f9SApple OSS Distributions 		 * 1. allocate a new bucket
259*bbb1b6f9SApple OSS Distributions 		 * 2. publish it
260*bbb1b6f9SApple OSS Distributions 		 * 3. synchronize
261*bbb1b6f9SApple OSS Distributions 		 * 4. dealloc the old bucket
262*bbb1b6f9SApple OSS Distributions 		 */
263*bbb1b6f9SApple OSS Distributions 		old = os_atomic_xchg(&cls[2], arena_alloc(), release);
264*bbb1b6f9SApple OSS Distributions 
265*bbb1b6f9SApple OSS Distributions 		kr = task_restartable_ranges_synchronize(mach_task_self());
266*bbb1b6f9SApple OSS Distributions 		if (kr != KERN_SUCCESS) {
267*bbb1b6f9SApple OSS Distributions 			T_FAIL("task_restartable_ranges_register failed");
268*bbb1b6f9SApple OSS Distributions 			T_END;
269*bbb1b6f9SApple OSS Distributions 		}
270*bbb1b6f9SApple OSS Distributions 
271*bbb1b6f9SApple OSS Distributions 		if (syncs % step == 0) {
272*bbb1b6f9SApple OSS Distributions 			T_LOG("%d/%d", syncs / step, count);
273*bbb1b6f9SApple OSS Distributions 		}
274*bbb1b6f9SApple OSS Distributions 
275*bbb1b6f9SApple OSS Distributions 		arena_free(old);
276*bbb1b6f9SApple OSS Distributions 	}
277*bbb1b6f9SApple OSS Distributions 
278*bbb1b6f9SApple OSS Distributions 	T_PASS("survived without crashing");
279*bbb1b6f9SApple OSS Distributions #endif
280*bbb1b6f9SApple OSS Distributions }
281