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 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 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 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 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 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 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 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); 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 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 273 ATF_TC_CLEANUP(futex_robust_positive, tc) 274 { 275 do_cleanup(); 276 } 277 278 ATF_TC_WITH_CLEANUP(futex_robust_negative); 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 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 303 ATF_TC_CLEANUP(futex_robust_negative, tc) 304 { 305 do_cleanup(); 306 } 307 308 ATF_TC_WITH_CLEANUP(futex_robust_unmapped); 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 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 331 ATF_TC_CLEANUP(futex_robust_unmapped, tc) 332 { 333 do_cleanup(); 334 } 335 336 ATF_TC_WITH_CLEANUP(futex_robust_evil_circular); 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 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 362 ATF_TC_CLEANUP(futex_robust_evil_circular, tc) 363 { 364 do_cleanup(); 365 } 366 367 ATF_TC_WITH_CLEANUP(futex_robust_bad_pending); 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 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 392 ATF_TC_CLEANUP(futex_robust_bad_pending, tc) 393 { 394 do_cleanup(); 395 } 396 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