1 /* 2 * ldt_gdt.c - Test cases for LDT and GDT access 3 * Copyright (c) 2015 Andrew Lutomirski 4 */ 5 6 #define _GNU_SOURCE 7 #include <err.h> 8 #include <stdio.h> 9 #include <stdint.h> 10 #include <signal.h> 11 #include <setjmp.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <errno.h> 15 #include <unistd.h> 16 #include <sys/syscall.h> 17 #include <asm/ldt.h> 18 #include <sys/types.h> 19 #include <sys/wait.h> 20 #include <stdbool.h> 21 #include <pthread.h> 22 #include <sched.h> 23 #include <linux/futex.h> 24 25 #define AR_ACCESSED (1<<8) 26 27 #define AR_TYPE_RODATA (0 * (1<<9)) 28 #define AR_TYPE_RWDATA (1 * (1<<9)) 29 #define AR_TYPE_RODATA_EXPDOWN (2 * (1<<9)) 30 #define AR_TYPE_RWDATA_EXPDOWN (3 * (1<<9)) 31 #define AR_TYPE_XOCODE (4 * (1<<9)) 32 #define AR_TYPE_XRCODE (5 * (1<<9)) 33 #define AR_TYPE_XOCODE_CONF (6 * (1<<9)) 34 #define AR_TYPE_XRCODE_CONF (7 * (1<<9)) 35 36 #define AR_DPL3 (3 * (1<<13)) 37 38 #define AR_S (1 << 12) 39 #define AR_P (1 << 15) 40 #define AR_AVL (1 << 20) 41 #define AR_L (1 << 21) 42 #define AR_DB (1 << 22) 43 #define AR_G (1 << 23) 44 45 static int nerrs; 46 47 static void check_invalid_segment(uint16_t index, int ldt) 48 { 49 uint32_t has_limit = 0, has_ar = 0, limit, ar; 50 uint32_t selector = (index << 3) | (ldt << 2) | 3; 51 52 asm ("lsl %[selector], %[limit]\n\t" 53 "jnz 1f\n\t" 54 "movl $1, %[has_limit]\n\t" 55 "1:" 56 : [limit] "=r" (limit), [has_limit] "+rm" (has_limit) 57 : [selector] "r" (selector)); 58 asm ("larl %[selector], %[ar]\n\t" 59 "jnz 1f\n\t" 60 "movl $1, %[has_ar]\n\t" 61 "1:" 62 : [ar] "=r" (ar), [has_ar] "+rm" (has_ar) 63 : [selector] "r" (selector)); 64 65 if (has_limit || has_ar) { 66 printf("[FAIL]\t%s entry %hu is valid but should be invalid\n", 67 (ldt ? "LDT" : "GDT"), index); 68 nerrs++; 69 } else { 70 printf("[OK]\t%s entry %hu is invalid\n", 71 (ldt ? "LDT" : "GDT"), index); 72 } 73 } 74 75 static void check_valid_segment(uint16_t index, int ldt, 76 uint32_t expected_ar, uint32_t expected_limit, 77 bool verbose) 78 { 79 uint32_t has_limit = 0, has_ar = 0, limit, ar; 80 uint32_t selector = (index << 3) | (ldt << 2) | 3; 81 82 asm ("lsl %[selector], %[limit]\n\t" 83 "jnz 1f\n\t" 84 "movl $1, %[has_limit]\n\t" 85 "1:" 86 : [limit] "=r" (limit), [has_limit] "+rm" (has_limit) 87 : [selector] "r" (selector)); 88 asm ("larl %[selector], %[ar]\n\t" 89 "jnz 1f\n\t" 90 "movl $1, %[has_ar]\n\t" 91 "1:" 92 : [ar] "=r" (ar), [has_ar] "+rm" (has_ar) 93 : [selector] "r" (selector)); 94 95 if (!has_limit || !has_ar) { 96 printf("[FAIL]\t%s entry %hu is invalid but should be valid\n", 97 (ldt ? "LDT" : "GDT"), index); 98 nerrs++; 99 return; 100 } 101 102 if (ar != expected_ar) { 103 printf("[FAIL]\t%s entry %hu has AR 0x%08X but expected 0x%08X\n", 104 (ldt ? "LDT" : "GDT"), index, ar, expected_ar); 105 nerrs++; 106 } else if (limit != expected_limit) { 107 printf("[FAIL]\t%s entry %hu has limit 0x%08X but expected 0x%08X\n", 108 (ldt ? "LDT" : "GDT"), index, limit, expected_limit); 109 nerrs++; 110 } else if (verbose) { 111 printf("[OK]\t%s entry %hu has AR 0x%08X and limit 0x%08X\n", 112 (ldt ? "LDT" : "GDT"), index, ar, limit); 113 } 114 } 115 116 static bool install_valid_mode(const struct user_desc *desc, uint32_t ar, 117 bool oldmode) 118 { 119 int ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, 120 desc, sizeof(*desc)); 121 if (ret < -1) 122 errno = -ret; 123 if (ret == 0) { 124 uint32_t limit = desc->limit; 125 if (desc->limit_in_pages) 126 limit = (limit << 12) + 4095; 127 check_valid_segment(desc->entry_number, 1, ar, limit, true); 128 return true; 129 } else if (errno == ENOSYS) { 130 printf("[OK]\tmodify_ldt returned -ENOSYS\n"); 131 return false; 132 } else { 133 if (desc->seg_32bit) { 134 printf("[FAIL]\tUnexpected modify_ldt failure %d\n", 135 errno); 136 nerrs++; 137 return false; 138 } else { 139 printf("[OK]\tmodify_ldt rejected 16 bit segment\n"); 140 return false; 141 } 142 } 143 } 144 145 static bool install_valid(const struct user_desc *desc, uint32_t ar) 146 { 147 return install_valid_mode(desc, ar, false); 148 } 149 150 static void install_invalid(const struct user_desc *desc, bool oldmode) 151 { 152 int ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, 153 desc, sizeof(*desc)); 154 if (ret < -1) 155 errno = -ret; 156 if (ret == 0) { 157 check_invalid_segment(desc->entry_number, 1); 158 } else if (errno == ENOSYS) { 159 printf("[OK]\tmodify_ldt returned -ENOSYS\n"); 160 } else { 161 if (desc->seg_32bit) { 162 printf("[FAIL]\tUnexpected modify_ldt failure %d\n", 163 errno); 164 nerrs++; 165 } else { 166 printf("[OK]\tmodify_ldt rejected 16 bit segment\n"); 167 } 168 } 169 } 170 171 static int safe_modify_ldt(int func, struct user_desc *ptr, 172 unsigned long bytecount) 173 { 174 int ret = syscall(SYS_modify_ldt, 0x11, ptr, bytecount); 175 if (ret < -1) 176 errno = -ret; 177 return ret; 178 } 179 180 static void fail_install(struct user_desc *desc) 181 { 182 if (safe_modify_ldt(0x11, desc, sizeof(*desc)) == 0) { 183 printf("[FAIL]\tmodify_ldt accepted a bad descriptor\n"); 184 nerrs++; 185 } else if (errno == ENOSYS) { 186 printf("[OK]\tmodify_ldt returned -ENOSYS\n"); 187 } else { 188 printf("[OK]\tmodify_ldt failure %d\n", errno); 189 } 190 } 191 192 static void do_simple_tests(void) 193 { 194 struct user_desc desc = { 195 .entry_number = 0, 196 .base_addr = 0, 197 .limit = 10, 198 .seg_32bit = 1, 199 .contents = 2, /* Code, not conforming */ 200 .read_exec_only = 0, 201 .limit_in_pages = 0, 202 .seg_not_present = 0, 203 .useable = 0 204 }; 205 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB); 206 207 desc.limit_in_pages = 1; 208 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 209 AR_S | AR_P | AR_DB | AR_G); 210 211 check_invalid_segment(1, 1); 212 213 desc.entry_number = 2; 214 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 215 AR_S | AR_P | AR_DB | AR_G); 216 217 check_invalid_segment(1, 1); 218 219 desc.base_addr = 0xf0000000; 220 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 221 AR_S | AR_P | AR_DB | AR_G); 222 223 desc.useable = 1; 224 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 225 AR_S | AR_P | AR_DB | AR_G | AR_AVL); 226 227 desc.seg_not_present = 1; 228 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 229 AR_S | AR_DB | AR_G | AR_AVL); 230 231 desc.seg_32bit = 0; 232 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 233 AR_S | AR_G | AR_AVL); 234 235 desc.seg_32bit = 1; 236 desc.contents = 0; 237 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | 238 AR_S | AR_DB | AR_G | AR_AVL); 239 240 desc.read_exec_only = 1; 241 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | 242 AR_S | AR_DB | AR_G | AR_AVL); 243 244 desc.contents = 1; 245 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA_EXPDOWN | 246 AR_S | AR_DB | AR_G | AR_AVL); 247 248 desc.read_exec_only = 0; 249 desc.limit_in_pages = 0; 250 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA_EXPDOWN | 251 AR_S | AR_DB | AR_AVL); 252 253 desc.contents = 3; 254 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE_CONF | 255 AR_S | AR_DB | AR_AVL); 256 257 desc.read_exec_only = 1; 258 install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE_CONF | 259 AR_S | AR_DB | AR_AVL); 260 261 desc.read_exec_only = 0; 262 desc.contents = 2; 263 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 264 AR_S | AR_DB | AR_AVL); 265 266 desc.read_exec_only = 1; 267 268 #ifdef __x86_64__ 269 desc.lm = 1; 270 install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE | 271 AR_S | AR_DB | AR_AVL); 272 desc.lm = 0; 273 #endif 274 275 bool entry1_okay = install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE | 276 AR_S | AR_DB | AR_AVL); 277 278 if (entry1_okay) { 279 printf("[RUN]\tTest fork\n"); 280 pid_t child = fork(); 281 if (child == 0) { 282 nerrs = 0; 283 check_valid_segment(desc.entry_number, 1, 284 AR_DPL3 | AR_TYPE_XOCODE | 285 AR_S | AR_DB | AR_AVL, desc.limit, 286 true); 287 check_invalid_segment(1, 1); 288 exit(nerrs ? 1 : 0); 289 } else { 290 int status; 291 if (waitpid(child, &status, 0) != child || 292 !WIFEXITED(status)) { 293 printf("[FAIL]\tChild died\n"); 294 nerrs++; 295 } else if (WEXITSTATUS(status) != 0) { 296 printf("[FAIL]\tChild failed\n"); 297 nerrs++; 298 } else { 299 printf("[OK]\tChild succeeded\n"); 300 } 301 } 302 303 printf("[RUN]\tTest size\n"); 304 int i; 305 for (i = 0; i < 8192; i++) { 306 desc.entry_number = i; 307 desc.limit = i; 308 if (safe_modify_ldt(0x11, &desc, sizeof(desc)) != 0) { 309 printf("[FAIL]\tFailed to install entry %d\n", i); 310 nerrs++; 311 break; 312 } 313 } 314 for (int j = 0; j < i; j++) { 315 check_valid_segment(j, 1, AR_DPL3 | AR_TYPE_XOCODE | 316 AR_S | AR_DB | AR_AVL, j, false); 317 } 318 printf("[DONE]\tSize test\n"); 319 } else { 320 printf("[SKIP]\tSkipping fork and size tests because we have no LDT\n"); 321 } 322 323 /* Test entry_number too high. */ 324 desc.entry_number = 8192; 325 fail_install(&desc); 326 327 /* Test deletion and actions mistakeable for deletion. */ 328 memset(&desc, 0, sizeof(desc)); 329 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P); 330 331 desc.seg_not_present = 1; 332 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S); 333 334 desc.seg_not_present = 0; 335 desc.read_exec_only = 1; 336 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S | AR_P); 337 338 desc.read_exec_only = 0; 339 desc.seg_not_present = 1; 340 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S); 341 342 desc.read_exec_only = 1; 343 desc.limit = 1; 344 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S); 345 346 desc.limit = 0; 347 desc.base_addr = 1; 348 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S); 349 350 desc.base_addr = 0; 351 install_invalid(&desc, false); 352 353 desc.seg_not_present = 0; 354 desc.read_exec_only = 0; 355 desc.seg_32bit = 1; 356 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P | AR_DB); 357 install_invalid(&desc, true); 358 } 359 360 /* 361 * 0: thread is idle 362 * 1: thread armed 363 * 2: thread should clear LDT entry 0 364 * 3: thread should exit 365 */ 366 static volatile unsigned int ftx; 367 368 static void *threadproc(void *ctx) 369 { 370 cpu_set_t cpuset; 371 CPU_ZERO(&cpuset); 372 CPU_SET(1, &cpuset); 373 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 374 err(1, "sched_setaffinity to CPU 1"); /* should never fail */ 375 376 while (1) { 377 syscall(SYS_futex, &ftx, FUTEX_WAIT, 0, NULL, NULL, 0); 378 while (ftx != 2) { 379 if (ftx >= 3) 380 return NULL; 381 } 382 383 /* clear LDT entry 0 */ 384 const struct user_desc desc = {}; 385 if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) != 0) 386 err(1, "modify_ldt"); 387 388 /* If ftx == 2, set it to zero. If ftx == 100, quit. */ 389 unsigned int x = -2; 390 asm volatile ("lock xaddl %[x], %[ftx]" : 391 [x] "+r" (x), [ftx] "+m" (ftx)); 392 if (x != 2) 393 return NULL; 394 } 395 } 396 397 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 398 int flags) 399 { 400 struct sigaction sa; 401 memset(&sa, 0, sizeof(sa)); 402 sa.sa_sigaction = handler; 403 sa.sa_flags = SA_SIGINFO | flags; 404 sigemptyset(&sa.sa_mask); 405 if (sigaction(sig, &sa, 0)) 406 err(1, "sigaction"); 407 408 } 409 410 static jmp_buf jmpbuf; 411 412 static void sigsegv(int sig, siginfo_t *info, void *ctx_void) 413 { 414 siglongjmp(jmpbuf, 1); 415 } 416 417 static void do_multicpu_tests(void) 418 { 419 cpu_set_t cpuset; 420 pthread_t thread; 421 int failures = 0, iters = 5, i; 422 unsigned short orig_ss; 423 424 CPU_ZERO(&cpuset); 425 CPU_SET(1, &cpuset); 426 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { 427 printf("[SKIP]\tCannot set affinity to CPU 1\n"); 428 return; 429 } 430 431 CPU_ZERO(&cpuset); 432 CPU_SET(0, &cpuset); 433 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { 434 printf("[SKIP]\tCannot set affinity to CPU 0\n"); 435 return; 436 } 437 438 sethandler(SIGSEGV, sigsegv, 0); 439 #ifdef __i386__ 440 /* True 32-bit kernels send SIGILL instead of SIGSEGV on IRET faults. */ 441 sethandler(SIGILL, sigsegv, 0); 442 #endif 443 444 printf("[RUN]\tCross-CPU LDT invalidation\n"); 445 446 if (pthread_create(&thread, 0, threadproc, 0) != 0) 447 err(1, "pthread_create"); 448 449 asm volatile ("mov %%ss, %0" : "=rm" (orig_ss)); 450 451 for (i = 0; i < 5; i++) { 452 if (sigsetjmp(jmpbuf, 1) != 0) 453 continue; 454 455 /* Make sure the thread is ready after the last test. */ 456 while (ftx != 0) 457 ; 458 459 struct user_desc desc = { 460 .entry_number = 0, 461 .base_addr = 0, 462 .limit = 0xfffff, 463 .seg_32bit = 1, 464 .contents = 0, /* Data */ 465 .read_exec_only = 0, 466 .limit_in_pages = 1, 467 .seg_not_present = 0, 468 .useable = 0 469 }; 470 471 if (safe_modify_ldt(0x11, &desc, sizeof(desc)) != 0) { 472 if (errno != ENOSYS) 473 err(1, "modify_ldt"); 474 printf("[SKIP]\tmodify_ldt unavailable\n"); 475 break; 476 } 477 478 /* Arm the thread. */ 479 ftx = 1; 480 syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); 481 482 asm volatile ("mov %0, %%ss" : : "r" (0x7)); 483 484 /* Go! */ 485 ftx = 2; 486 487 while (ftx != 0) 488 ; 489 490 /* 491 * On success, modify_ldt will segfault us synchronously, 492 * and we'll escape via siglongjmp. 493 */ 494 495 failures++; 496 asm volatile ("mov %0, %%ss" : : "rm" (orig_ss)); 497 }; 498 499 ftx = 100; /* Kill the thread. */ 500 syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); 501 502 if (pthread_join(thread, NULL) != 0) 503 err(1, "pthread_join"); 504 505 if (failures) { 506 printf("[FAIL]\t%d of %d iterations failed\n", failures, iters); 507 nerrs++; 508 } else { 509 printf("[OK]\tAll %d iterations succeeded\n", iters); 510 } 511 } 512 513 static int finish_exec_test(void) 514 { 515 /* 516 * In a sensible world, this would be check_invalid_segment(0, 1); 517 * For better or for worse, though, the LDT is inherited across exec. 518 * We can probably change this safely, but for now we test it. 519 */ 520 check_valid_segment(0, 1, 521 AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB, 522 42, true); 523 524 return nerrs ? 1 : 0; 525 } 526 527 static void do_exec_test(void) 528 { 529 printf("[RUN]\tTest exec\n"); 530 531 struct user_desc desc = { 532 .entry_number = 0, 533 .base_addr = 0, 534 .limit = 42, 535 .seg_32bit = 1, 536 .contents = 2, /* Code, not conforming */ 537 .read_exec_only = 0, 538 .limit_in_pages = 0, 539 .seg_not_present = 0, 540 .useable = 0 541 }; 542 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB); 543 544 pid_t child = fork(); 545 if (child == 0) { 546 execl("/proc/self/exe", "ldt_gdt_test_exec", NULL); 547 printf("[FAIL]\tCould not exec self\n"); 548 exit(1); /* exec failed */ 549 } else { 550 int status; 551 if (waitpid(child, &status, 0) != child || 552 !WIFEXITED(status)) { 553 printf("[FAIL]\tChild died\n"); 554 nerrs++; 555 } else if (WEXITSTATUS(status) != 0) { 556 printf("[FAIL]\tChild failed\n"); 557 nerrs++; 558 } else { 559 printf("[OK]\tChild succeeded\n"); 560 } 561 } 562 } 563 564 int main(int argc, char **argv) 565 { 566 if (argc == 1 && !strcmp(argv[0], "ldt_gdt_test_exec")) 567 return finish_exec_test(); 568 569 do_simple_tests(); 570 571 do_multicpu_tests(); 572 573 do_exec_test(); 574 575 return nerrs ? 1 : 0; 576 } 577