1 /* $NetBSD: t_futex_robust.c,v 1.2 2020/05/01 01:44:30 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __COPYRIGHT("@(#) Copyright (c) 2019\
31 The NetBSD Foundation, inc. All rights reserved.");
32 __RCSID("$NetBSD: t_futex_robust.c,v 1.2 2020/05/01 01:44:30 thorpej Exp $");
33
34 #include <sys/mman.h>
35 #include <errno.h>
36 #include <lwp.h>
37 #include <stdio.h>
38 #include <time.h>
39
40 #include <atf-c.h>
41
42 #include <libc/include/futex_private.h>
43
44 #define STACK_SIZE 65536
45 #define NLOCKS 16
46
47 struct futex_lock_pos {
48 struct futex_robust_list list;
49 int fword;
50 };
51 struct futex_lock_pos pos_locks[NLOCKS];
52
53 struct futex_lock_neg {
54 int fword;
55 struct futex_robust_list list;
56 };
57 struct futex_lock_neg neg_locks[NLOCKS];
58
59 struct lwp_data {
60 ucontext_t context;
61 void *stack_base;
62 lwpid_t lwpid;
63 lwpid_t threadid;
64 struct futex_robust_list_head rhead;
65
66 /* Results to be asserted by main thread. */
67 bool set_robust_list_failed;
68 };
69
70 struct lwp_data lwp_data;
71
72 static void
setup_lwp_context(void (* func)(void *))73 setup_lwp_context(void (*func)(void *))
74 {
75
76 memset(&lwp_data, 0, sizeof(lwp_data));
77 lwp_data.stack_base = mmap(NULL, STACK_SIZE,
78 PROT_READ | PROT_WRITE,
79 MAP_ANON | MAP_STACK | MAP_PRIVATE, -1, 0);
80 ATF_REQUIRE(lwp_data.stack_base != MAP_FAILED);
81 _lwp_makecontext(&lwp_data.context, func,
82 &lwp_data, NULL, lwp_data.stack_base, STACK_SIZE);
83 lwp_data.threadid = 0;
84 }
85
86 static void
do_cleanup(void)87 do_cleanup(void)
88 {
89 if (lwp_data.stack_base != NULL &&
90 lwp_data.stack_base != MAP_FAILED) {
91 (void) munmap(lwp_data.stack_base, STACK_SIZE);
92 }
93 memset(&lwp_data, 0, sizeof(lwp_data));
94 memset(pos_locks, 0, sizeof(pos_locks));
95 memset(neg_locks, 0, sizeof(neg_locks));
96 }
97
98 static void
test_pos_robust_list(void * arg)99 test_pos_robust_list(void *arg)
100 {
101 struct lwp_data *d = arg;
102 int i;
103
104 d->rhead.list.next = &d->rhead.list;
105 d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) -
106 offsetof(struct futex_lock_pos, list);
107 d->rhead.pending_list = NULL;
108
109 if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) {
110 d->set_robust_list_failed = true;
111 _lwp_exit();
112 }
113
114 memset(pos_locks, 0, sizeof(pos_locks));
115
116 d->threadid = _lwp_self();
117
118 for (i = 0; i < NLOCKS-1; i++) {
119 pos_locks[i].fword = _lwp_self();
120 pos_locks[i].list.next = d->rhead.list.next;
121 d->rhead.list.next = &pos_locks[i].list;
122 }
123
124 pos_locks[i].fword = _lwp_self();
125 d->rhead.pending_list = &pos_locks[i].list;
126
127 _lwp_exit();
128 }
129
130 static void
test_neg_robust_list(void * arg)131 test_neg_robust_list(void *arg)
132 {
133 struct lwp_data *d = arg;
134 int i;
135
136 d->rhead.list.next = &d->rhead.list;
137 d->rhead.futex_offset = offsetof(struct futex_lock_neg, fword) -
138 offsetof(struct futex_lock_neg, list);
139 d->rhead.pending_list = NULL;
140
141 if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) {
142 d->set_robust_list_failed = true;
143 _lwp_exit();
144 }
145
146 memset(neg_locks, 0, sizeof(neg_locks));
147
148 d->threadid = _lwp_self();
149
150 for (i = 0; i < NLOCKS-1; i++) {
151 neg_locks[i].fword = _lwp_self();
152 neg_locks[i].list.next = d->rhead.list.next;
153 d->rhead.list.next = &neg_locks[i].list;
154 }
155
156 neg_locks[i].fword = _lwp_self();
157 d->rhead.pending_list = &neg_locks[i].list;
158
159 _lwp_exit();
160 }
161
162 static void
test_unmapped_robust_list(void * arg)163 test_unmapped_robust_list(void *arg)
164 {
165 struct lwp_data *d = arg;
166
167 d->rhead.list.next = &d->rhead.list;
168 d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) -
169 offsetof(struct futex_lock_pos, list);
170 d->rhead.pending_list = NULL;
171
172 if (__futex_set_robust_list((void *)sizeof(d->rhead),
173 sizeof(d->rhead)) != 0) {
174 d->set_robust_list_failed = true;
175 _lwp_exit();
176 }
177
178 memset(pos_locks, 0, sizeof(pos_locks));
179
180 d->threadid = _lwp_self();
181
182 _lwp_exit();
183 }
184
185 static void
test_evil_circular_robust_list(void * arg)186 test_evil_circular_robust_list(void *arg)
187 {
188 struct lwp_data *d = arg;
189 int i;
190
191 d->rhead.list.next = &d->rhead.list;
192 d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) -
193 offsetof(struct futex_lock_pos, list);
194 d->rhead.pending_list = NULL;
195
196 if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) {
197 d->set_robust_list_failed = true;
198 _lwp_exit();
199 }
200
201 memset(pos_locks, 0, sizeof(pos_locks));
202
203 d->threadid = _lwp_self();
204
205 for (i = 0; i < NLOCKS; i++) {
206 pos_locks[i].fword = _lwp_self();
207 pos_locks[i].list.next = d->rhead.list.next;
208 d->rhead.list.next = &pos_locks[i].list;
209 }
210
211 /* Make a loop. */
212 pos_locks[0].list.next = pos_locks[NLOCKS-1].list.next;
213
214 _lwp_exit();
215 }
216
217 static void
test_bad_pending_robust_list(void * arg)218 test_bad_pending_robust_list(void *arg)
219 {
220 struct lwp_data *d = arg;
221 int i;
222
223 d->rhead.list.next = &d->rhead.list;
224 d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) -
225 offsetof(struct futex_lock_pos, list);
226 d->rhead.pending_list = NULL;
227
228 if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) {
229 d->set_robust_list_failed = true;
230 _lwp_exit();
231 }
232
233 memset(pos_locks, 0, sizeof(pos_locks));
234
235 d->threadid = _lwp_self();
236
237 for (i = 0; i < NLOCKS; i++) {
238 pos_locks[i].fword = _lwp_self();
239 pos_locks[i].list.next = d->rhead.list.next;
240 d->rhead.list.next = &pos_locks[i].list;
241 }
242
243 d->rhead.pending_list = (void *)sizeof(d->rhead);
244
245 _lwp_exit();
246 }
247
248 ATF_TC_WITH_CLEANUP(futex_robust_positive);
ATF_TC_HEAD(futex_robust_positive,tc)249 ATF_TC_HEAD(futex_robust_positive, tc)
250 {
251 atf_tc_set_md_var(tc, "descr",
252 "checks futex robust list with positive futex word offset");
253 }
254
ATF_TC_BODY(futex_robust_positive,tc)255 ATF_TC_BODY(futex_robust_positive, tc)
256 {
257 int i;
258
259 setup_lwp_context(test_pos_robust_list);
260
261 ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0);
262 ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0);
263
264 ATF_REQUIRE(lwp_data.set_robust_list_failed == false);
265
266 for (i = 0; i < NLOCKS; i++) {
267 ATF_REQUIRE((pos_locks[i].fword & FUTEX_TID_MASK) ==
268 lwp_data.threadid);
269 ATF_REQUIRE((pos_locks[i].fword & FUTEX_OWNER_DIED) != 0);
270 }
271 }
272
ATF_TC_CLEANUP(futex_robust_positive,tc)273 ATF_TC_CLEANUP(futex_robust_positive, tc)
274 {
275 do_cleanup();
276 }
277
278 ATF_TC_WITH_CLEANUP(futex_robust_negative);
ATF_TC_HEAD(futex_robust_negative,tc)279 ATF_TC_HEAD(futex_robust_negative, tc)
280 {
281 atf_tc_set_md_var(tc, "descr",
282 "checks futex robust list with negative futex word offset");
283 }
284
ATF_TC_BODY(futex_robust_negative,tc)285 ATF_TC_BODY(futex_robust_negative, tc)
286 {
287 int i;
288
289 setup_lwp_context(test_neg_robust_list);
290
291 ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0);
292 ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0);
293
294 ATF_REQUIRE(lwp_data.set_robust_list_failed == false);
295
296 for (i = 0; i < NLOCKS; i++) {
297 ATF_REQUIRE((neg_locks[i].fword & FUTEX_TID_MASK) ==
298 lwp_data.threadid);
299 ATF_REQUIRE((neg_locks[i].fword & FUTEX_OWNER_DIED) != 0);
300 }
301 }
302
ATF_TC_CLEANUP(futex_robust_negative,tc)303 ATF_TC_CLEANUP(futex_robust_negative, tc)
304 {
305 do_cleanup();
306 }
307
308 ATF_TC_WITH_CLEANUP(futex_robust_unmapped);
ATF_TC_HEAD(futex_robust_unmapped,tc)309 ATF_TC_HEAD(futex_robust_unmapped, tc)
310 {
311 atf_tc_set_md_var(tc, "descr",
312 "checks futex robust list with unmapped robust list pointer");
313 }
314
ATF_TC_BODY(futex_robust_unmapped,tc)315 ATF_TC_BODY(futex_robust_unmapped, tc)
316 {
317
318 setup_lwp_context(test_unmapped_robust_list);
319
320 ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0);
321 ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0);
322
323 ATF_REQUIRE(lwp_data.set_robust_list_failed == false);
324
325 /*
326 * No additional validation; just exercises a code path
327 * in the kernel.
328 */
329 }
330
ATF_TC_CLEANUP(futex_robust_unmapped,tc)331 ATF_TC_CLEANUP(futex_robust_unmapped, tc)
332 {
333 do_cleanup();
334 }
335
336 ATF_TC_WITH_CLEANUP(futex_robust_evil_circular);
ATF_TC_HEAD(futex_robust_evil_circular,tc)337 ATF_TC_HEAD(futex_robust_evil_circular, tc)
338 {
339 atf_tc_set_md_var(tc, "descr",
340 "checks futex robust list processing faced with a deliberately "
341 "ciruclar list");
342 }
343
ATF_TC_BODY(futex_robust_evil_circular,tc)344 ATF_TC_BODY(futex_robust_evil_circular, tc)
345 {
346 int i;
347
348 setup_lwp_context(test_evil_circular_robust_list);
349
350 ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0);
351 ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0);
352
353 ATF_REQUIRE(lwp_data.set_robust_list_failed == false);
354
355 for (i = 0; i < NLOCKS; i++) {
356 ATF_REQUIRE((pos_locks[i].fword & FUTEX_TID_MASK) ==
357 lwp_data.threadid);
358 ATF_REQUIRE((pos_locks[i].fword & FUTEX_OWNER_DIED) != 0);
359 }
360 }
361
ATF_TC_CLEANUP(futex_robust_evil_circular,tc)362 ATF_TC_CLEANUP(futex_robust_evil_circular, tc)
363 {
364 do_cleanup();
365 }
366
367 ATF_TC_WITH_CLEANUP(futex_robust_bad_pending);
ATF_TC_HEAD(futex_robust_bad_pending,tc)368 ATF_TC_HEAD(futex_robust_bad_pending, tc)
369 {
370 atf_tc_set_md_var(tc, "descr",
371 "checks futex robust list processing with a bad pending pointer");
372 }
373
ATF_TC_BODY(futex_robust_bad_pending,tc)374 ATF_TC_BODY(futex_robust_bad_pending, tc)
375 {
376 int i;
377
378 setup_lwp_context(test_bad_pending_robust_list);
379
380 ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0);
381 ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0);
382
383 ATF_REQUIRE(lwp_data.set_robust_list_failed == false);
384
385 for (i = 0; i < NLOCKS; i++) {
386 ATF_REQUIRE((pos_locks[i].fword & FUTEX_TID_MASK) ==
387 lwp_data.threadid);
388 ATF_REQUIRE((pos_locks[i].fword & FUTEX_OWNER_DIED) != 0);
389 }
390 }
391
ATF_TC_CLEANUP(futex_robust_bad_pending,tc)392 ATF_TC_CLEANUP(futex_robust_bad_pending, tc)
393 {
394 do_cleanup();
395 }
396
ATF_TP_ADD_TCS(tp)397 ATF_TP_ADD_TCS(tp)
398 {
399 ATF_TP_ADD_TC(tp, futex_robust_positive);
400 ATF_TP_ADD_TC(tp, futex_robust_negative);
401 ATF_TP_ADD_TC(tp, futex_robust_unmapped);
402 ATF_TP_ADD_TC(tp, futex_robust_evil_circular);
403 ATF_TP_ADD_TC(tp, futex_robust_bad_pending);
404
405 return atf_no_error();
406 }
407