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