1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ldt_gdt.c - Test cases for LDT and GDT access 4 * Copyright (c) 2015 Andrew Lutomirski 5 */ 6 7 #define _GNU_SOURCE 8 #include <err.h> 9 #include <stdio.h> 10 #include <stdint.h> 11 #include <signal.h> 12 #include <setjmp.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <errno.h> 16 #include <unistd.h> 17 #include <sys/syscall.h> 18 #include <asm/ldt.h> 19 #include <sys/types.h> 20 #include <sys/wait.h> 21 #include <stdbool.h> 22 #include <pthread.h> 23 #include <sched.h> 24 #include <linux/futex.h> 25 #include <sys/mman.h> 26 #include <asm/prctl.h> 27 #include <sys/prctl.h> 28 29 #include "helpers.h" 30 31 #define AR_ACCESSED (1<<8) 32 33 #define AR_TYPE_RODATA (0 * (1<<9)) 34 #define AR_TYPE_RWDATA (1 * (1<<9)) 35 #define AR_TYPE_RODATA_EXPDOWN (2 * (1<<9)) 36 #define AR_TYPE_RWDATA_EXPDOWN (3 * (1<<9)) 37 #define AR_TYPE_XOCODE (4 * (1<<9)) 38 #define AR_TYPE_XRCODE (5 * (1<<9)) 39 #define AR_TYPE_XOCODE_CONF (6 * (1<<9)) 40 #define AR_TYPE_XRCODE_CONF (7 * (1<<9)) 41 42 #define AR_DPL3 (3 * (1<<13)) 43 44 #define AR_S (1 << 12) 45 #define AR_P (1 << 15) 46 #define AR_AVL (1 << 20) 47 #define AR_L (1 << 21) 48 #define AR_DB (1 << 22) 49 #define AR_G (1 << 23) 50 51 #ifdef __x86_64__ 52 # define INT80_CLOBBERS "r8", "r9", "r10", "r11" 53 #else 54 # define INT80_CLOBBERS 55 #endif 56 57 static int nerrs; 58 59 /* Points to an array of 1024 ints, each holding its own index. */ 60 static const unsigned int *counter_page; 61 static struct user_desc *low_user_desc; 62 static struct user_desc *low_user_desc_clear; /* Use to delete GDT entry */ 63 static int gdt_entry_num; 64 65 static void check_invalid_segment(uint16_t index, int ldt) 66 { 67 uint32_t has_limit = 0, has_ar = 0, limit, ar; 68 uint32_t selector = (index << 3) | (ldt << 2) | 3; 69 70 asm ("lsl %[selector], %[limit]\n\t" 71 "jnz 1f\n\t" 72 "movl $1, %[has_limit]\n\t" 73 "1:" 74 : [limit] "=r" (limit), [has_limit] "+rm" (has_limit) 75 : [selector] "r" (selector)); 76 asm ("larl %[selector], %[ar]\n\t" 77 "jnz 1f\n\t" 78 "movl $1, %[has_ar]\n\t" 79 "1:" 80 : [ar] "=r" (ar), [has_ar] "+rm" (has_ar) 81 : [selector] "r" (selector)); 82 83 if (has_limit || has_ar) { 84 printf("[FAIL]\t%s entry %hu is valid but should be invalid\n", 85 (ldt ? "LDT" : "GDT"), index); 86 nerrs++; 87 } else { 88 printf("[OK]\t%s entry %hu is invalid\n", 89 (ldt ? "LDT" : "GDT"), index); 90 } 91 } 92 93 static void check_valid_segment(uint16_t index, int ldt, 94 uint32_t expected_ar, uint32_t expected_limit, 95 bool verbose) 96 { 97 uint32_t has_limit = 0, has_ar = 0, limit, ar; 98 uint32_t selector = (index << 3) | (ldt << 2) | 3; 99 100 asm ("lsl %[selector], %[limit]\n\t" 101 "jnz 1f\n\t" 102 "movl $1, %[has_limit]\n\t" 103 "1:" 104 : [limit] "=r" (limit), [has_limit] "+rm" (has_limit) 105 : [selector] "r" (selector)); 106 asm ("larl %[selector], %[ar]\n\t" 107 "jnz 1f\n\t" 108 "movl $1, %[has_ar]\n\t" 109 "1:" 110 : [ar] "=r" (ar), [has_ar] "+rm" (has_ar) 111 : [selector] "r" (selector)); 112 113 if (!has_limit || !has_ar) { 114 printf("[FAIL]\t%s entry %hu is invalid but should be valid\n", 115 (ldt ? "LDT" : "GDT"), index); 116 nerrs++; 117 return; 118 } 119 120 /* The SDM says "bits 19:16 are undefined". Thanks. */ 121 ar &= ~0xF0000; 122 123 /* 124 * NB: Different Linux versions do different things with the 125 * accessed bit in set_thread_area(). 126 */ 127 if (ar != expected_ar && ar != (expected_ar | AR_ACCESSED)) { 128 printf("[FAIL]\t%s entry %hu has AR 0x%08X but expected 0x%08X\n", 129 (ldt ? "LDT" : "GDT"), index, ar, expected_ar); 130 nerrs++; 131 } else if (limit != expected_limit) { 132 printf("[FAIL]\t%s entry %hu has limit 0x%08X but expected 0x%08X\n", 133 (ldt ? "LDT" : "GDT"), index, limit, expected_limit); 134 nerrs++; 135 } else if (verbose) { 136 printf("[OK]\t%s entry %hu has AR 0x%08X and limit 0x%08X\n", 137 (ldt ? "LDT" : "GDT"), index, ar, limit); 138 } 139 } 140 141 static bool install_valid_mode(const struct user_desc *d, uint32_t ar, 142 bool oldmode, bool ldt) 143 { 144 struct user_desc desc = *d; 145 int ret; 146 147 if (!ldt) { 148 #ifndef __i386__ 149 /* No point testing set_thread_area in a 64-bit build */ 150 return false; 151 #endif 152 if (!gdt_entry_num) 153 return false; 154 desc.entry_number = gdt_entry_num; 155 156 ret = syscall(SYS_set_thread_area, &desc); 157 } else { 158 ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, 159 &desc, sizeof(desc)); 160 161 if (ret < -1) 162 errno = -ret; 163 164 if (ret != 0 && errno == ENOSYS) { 165 printf("[OK]\tmodify_ldt returned -ENOSYS\n"); 166 return false; 167 } 168 } 169 170 if (ret == 0) { 171 uint32_t limit = desc.limit; 172 if (desc.limit_in_pages) 173 limit = (limit << 12) + 4095; 174 check_valid_segment(desc.entry_number, ldt, ar, limit, true); 175 return true; 176 } else { 177 if (desc.seg_32bit) { 178 printf("[FAIL]\tUnexpected %s failure %d\n", 179 ldt ? "modify_ldt" : "set_thread_area", 180 errno); 181 nerrs++; 182 return false; 183 } else { 184 printf("[OK]\t%s rejected 16 bit segment\n", 185 ldt ? "modify_ldt" : "set_thread_area"); 186 return false; 187 } 188 } 189 } 190 191 static bool install_valid(const struct user_desc *desc, uint32_t ar) 192 { 193 bool ret = install_valid_mode(desc, ar, false, true); 194 195 if (desc->contents <= 1 && desc->seg_32bit && 196 !desc->seg_not_present) { 197 /* Should work in the GDT, too. */ 198 install_valid_mode(desc, ar, false, false); 199 } 200 201 return ret; 202 } 203 204 static void install_invalid(const struct user_desc *desc, bool oldmode) 205 { 206 int ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, 207 desc, sizeof(*desc)); 208 if (ret < -1) 209 errno = -ret; 210 if (ret == 0) { 211 check_invalid_segment(desc->entry_number, 1); 212 } else if (errno == ENOSYS) { 213 printf("[OK]\tmodify_ldt returned -ENOSYS\n"); 214 } else { 215 if (desc->seg_32bit) { 216 printf("[FAIL]\tUnexpected modify_ldt failure %d\n", 217 errno); 218 nerrs++; 219 } else { 220 printf("[OK]\tmodify_ldt rejected 16 bit segment\n"); 221 } 222 } 223 } 224 225 static int safe_modify_ldt(int func, struct user_desc *ptr, 226 unsigned long bytecount) 227 { 228 int ret = syscall(SYS_modify_ldt, 0x11, ptr, bytecount); 229 if (ret < -1) 230 errno = -ret; 231 return ret; 232 } 233 234 static void fail_install(struct user_desc *desc) 235 { 236 if (safe_modify_ldt(0x11, desc, sizeof(*desc)) == 0) { 237 printf("[FAIL]\tmodify_ldt accepted a bad descriptor\n"); 238 nerrs++; 239 } else if (errno == ENOSYS) { 240 printf("[OK]\tmodify_ldt returned -ENOSYS\n"); 241 } else { 242 printf("[OK]\tmodify_ldt failure %d\n", errno); 243 } 244 } 245 246 static void do_simple_tests(void) 247 { 248 struct user_desc desc = { 249 .entry_number = 0, 250 .base_addr = 0, 251 .limit = 10, 252 .seg_32bit = 1, 253 .contents = 2, /* Code, not conforming */ 254 .read_exec_only = 0, 255 .limit_in_pages = 0, 256 .seg_not_present = 0, 257 .useable = 0 258 }; 259 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB); 260 261 desc.limit_in_pages = 1; 262 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 263 AR_S | AR_P | AR_DB | AR_G); 264 265 check_invalid_segment(1, 1); 266 267 desc.entry_number = 2; 268 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 269 AR_S | AR_P | AR_DB | AR_G); 270 271 check_invalid_segment(1, 1); 272 273 desc.base_addr = 0xf0000000; 274 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 275 AR_S | AR_P | AR_DB | AR_G); 276 277 desc.useable = 1; 278 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 279 AR_S | AR_P | AR_DB | AR_G | AR_AVL); 280 281 desc.seg_not_present = 1; 282 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 283 AR_S | AR_DB | AR_G | AR_AVL); 284 285 desc.seg_32bit = 0; 286 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 287 AR_S | AR_G | AR_AVL); 288 289 desc.seg_32bit = 1; 290 desc.contents = 0; 291 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | 292 AR_S | AR_DB | AR_G | AR_AVL); 293 294 desc.read_exec_only = 1; 295 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | 296 AR_S | AR_DB | AR_G | AR_AVL); 297 298 desc.contents = 1; 299 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA_EXPDOWN | 300 AR_S | AR_DB | AR_G | AR_AVL); 301 302 desc.read_exec_only = 0; 303 desc.limit_in_pages = 0; 304 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA_EXPDOWN | 305 AR_S | AR_DB | AR_AVL); 306 307 desc.contents = 3; 308 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE_CONF | 309 AR_S | AR_DB | AR_AVL); 310 311 desc.read_exec_only = 1; 312 install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE_CONF | 313 AR_S | AR_DB | AR_AVL); 314 315 desc.read_exec_only = 0; 316 desc.contents = 2; 317 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | 318 AR_S | AR_DB | AR_AVL); 319 320 desc.read_exec_only = 1; 321 322 #ifdef __x86_64__ 323 desc.lm = 1; 324 install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE | 325 AR_S | AR_DB | AR_AVL); 326 desc.lm = 0; 327 #endif 328 329 bool entry1_okay = install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE | 330 AR_S | AR_DB | AR_AVL); 331 332 if (entry1_okay) { 333 printf("[RUN]\tTest fork\n"); 334 pid_t child = fork(); 335 if (child == 0) { 336 nerrs = 0; 337 check_valid_segment(desc.entry_number, 1, 338 AR_DPL3 | AR_TYPE_XOCODE | 339 AR_S | AR_DB | AR_AVL, desc.limit, 340 true); 341 check_invalid_segment(1, 1); 342 exit(nerrs ? 1 : 0); 343 } else { 344 int status; 345 if (waitpid(child, &status, 0) != child || 346 !WIFEXITED(status)) { 347 printf("[FAIL]\tChild died\n"); 348 nerrs++; 349 } else if (WEXITSTATUS(status) != 0) { 350 printf("[FAIL]\tChild failed\n"); 351 nerrs++; 352 } else { 353 printf("[OK]\tChild succeeded\n"); 354 } 355 } 356 357 printf("[RUN]\tTest size\n"); 358 int i; 359 for (i = 0; i < 8192; i++) { 360 desc.entry_number = i; 361 desc.limit = i; 362 if (safe_modify_ldt(0x11, &desc, sizeof(desc)) != 0) { 363 printf("[FAIL]\tFailed to install entry %d\n", i); 364 nerrs++; 365 break; 366 } 367 } 368 for (int j = 0; j < i; j++) { 369 check_valid_segment(j, 1, AR_DPL3 | AR_TYPE_XOCODE | 370 AR_S | AR_DB | AR_AVL, j, false); 371 } 372 printf("[DONE]\tSize test\n"); 373 } else { 374 printf("[SKIP]\tSkipping fork and size tests because we have no LDT\n"); 375 } 376 377 /* Test entry_number too high. */ 378 desc.entry_number = 8192; 379 fail_install(&desc); 380 381 /* Test deletion and actions mistakeable for deletion. */ 382 memset(&desc, 0, sizeof(desc)); 383 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P); 384 385 desc.seg_not_present = 1; 386 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S); 387 388 desc.seg_not_present = 0; 389 desc.read_exec_only = 1; 390 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S | AR_P); 391 392 desc.read_exec_only = 0; 393 desc.seg_not_present = 1; 394 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S); 395 396 desc.read_exec_only = 1; 397 desc.limit = 1; 398 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S); 399 400 desc.limit = 0; 401 desc.base_addr = 1; 402 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S); 403 404 desc.base_addr = 0; 405 install_invalid(&desc, false); 406 407 desc.seg_not_present = 0; 408 desc.seg_32bit = 1; 409 desc.read_exec_only = 0; 410 desc.limit = 0xfffff; 411 412 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P | AR_DB); 413 414 desc.limit_in_pages = 1; 415 416 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P | AR_DB | AR_G); 417 desc.read_exec_only = 1; 418 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S | AR_P | AR_DB | AR_G); 419 desc.contents = 1; 420 desc.read_exec_only = 0; 421 install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA_EXPDOWN | AR_S | AR_P | AR_DB | AR_G); 422 desc.read_exec_only = 1; 423 install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA_EXPDOWN | AR_S | AR_P | AR_DB | AR_G); 424 425 desc.limit = 0; 426 install_invalid(&desc, true); 427 } 428 429 /* 430 * 0: thread is idle 431 * 1: thread armed 432 * 2: thread should clear LDT entry 0 433 * 3: thread should exit 434 */ 435 static volatile unsigned int ftx; 436 437 static void *threadproc(void *ctx) 438 { 439 cpu_set_t cpuset; 440 CPU_ZERO(&cpuset); 441 CPU_SET(1, &cpuset); 442 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 443 err(1, "sched_setaffinity to CPU 1"); /* should never fail */ 444 445 while (1) { 446 syscall(SYS_futex, &ftx, FUTEX_WAIT, 0, NULL, NULL, 0); 447 while (ftx != 2) { 448 if (ftx >= 3) 449 return NULL; 450 } 451 452 /* clear LDT entry 0 */ 453 const struct user_desc desc = {}; 454 if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) != 0) 455 err(1, "modify_ldt"); 456 457 /* If ftx == 2, set it to zero. If ftx == 100, quit. */ 458 unsigned int x = -2; 459 asm volatile ("lock xaddl %[x], %[ftx]" : 460 [x] "+r" (x), [ftx] "+m" (ftx)); 461 if (x != 2) 462 return NULL; 463 } 464 } 465 466 #ifdef __i386__ 467 468 #ifndef SA_RESTORE 469 #define SA_RESTORER 0x04000000 470 #endif 471 472 /* 473 * The UAPI header calls this 'struct sigaction', which conflicts with 474 * glibc. Sigh. 475 */ 476 struct fake_ksigaction { 477 void *handler; /* the real type is nasty */ 478 unsigned long sa_flags; 479 void (*sa_restorer)(void); 480 unsigned char sigset[8]; 481 }; 482 483 static void fix_sa_restorer(int sig) 484 { 485 struct fake_ksigaction ksa; 486 487 if (syscall(SYS_rt_sigaction, sig, NULL, &ksa, 8) == 0) { 488 /* 489 * glibc has a nasty bug: it sometimes writes garbage to 490 * sa_restorer. This interacts quite badly with anything 491 * that fiddles with SS because it can trigger legacy 492 * stack switching. Patch it up. See: 493 * 494 * https://sourceware.org/bugzilla/show_bug.cgi?id=21269 495 */ 496 if (!(ksa.sa_flags & SA_RESTORER) && ksa.sa_restorer) { 497 ksa.sa_restorer = NULL; 498 if (syscall(SYS_rt_sigaction, sig, &ksa, NULL, 499 sizeof(ksa.sigset)) != 0) 500 err(1, "rt_sigaction"); 501 } 502 } 503 } 504 #else 505 static void fix_sa_restorer(int sig) 506 { 507 /* 64-bit glibc works fine. */ 508 } 509 #endif 510 511 static jmp_buf jmpbuf; 512 513 static void sigsegv(int sig, siginfo_t *info, void *ctx_void) 514 { 515 siglongjmp(jmpbuf, 1); 516 } 517 518 static void do_multicpu_tests(void) 519 { 520 cpu_set_t cpuset; 521 pthread_t thread; 522 int failures = 0, iters = 5, i; 523 unsigned short orig_ss; 524 525 CPU_ZERO(&cpuset); 526 CPU_SET(1, &cpuset); 527 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { 528 printf("[SKIP]\tCannot set affinity to CPU 1\n"); 529 return; 530 } 531 532 CPU_ZERO(&cpuset); 533 CPU_SET(0, &cpuset); 534 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { 535 printf("[SKIP]\tCannot set affinity to CPU 0\n"); 536 return; 537 } 538 539 sethandler(SIGSEGV, sigsegv, 0); 540 fix_sa_restorer(SIGSEGV); 541 #ifdef __i386__ 542 /* True 32-bit kernels send SIGILL instead of SIGSEGV on IRET faults. */ 543 sethandler(SIGILL, sigsegv, 0); 544 fix_sa_restorer(SIGILL); 545 #endif 546 547 printf("[RUN]\tCross-CPU LDT invalidation\n"); 548 549 if (pthread_create(&thread, 0, threadproc, 0) != 0) 550 err(1, "pthread_create"); 551 552 asm volatile ("mov %%ss, %0" : "=rm" (orig_ss)); 553 554 for (i = 0; i < 5; i++) { 555 if (sigsetjmp(jmpbuf, 1) != 0) 556 continue; 557 558 /* Make sure the thread is ready after the last test. */ 559 while (ftx != 0) 560 ; 561 562 struct user_desc desc = { 563 .entry_number = 0, 564 .base_addr = 0, 565 .limit = 0xfffff, 566 .seg_32bit = 1, 567 .contents = 0, /* Data */ 568 .read_exec_only = 0, 569 .limit_in_pages = 1, 570 .seg_not_present = 0, 571 .useable = 0 572 }; 573 574 if (safe_modify_ldt(0x11, &desc, sizeof(desc)) != 0) { 575 if (errno != ENOSYS) 576 err(1, "modify_ldt"); 577 printf("[SKIP]\tmodify_ldt unavailable\n"); 578 break; 579 } 580 581 /* Arm the thread. */ 582 ftx = 1; 583 syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); 584 585 asm volatile ("mov %0, %%ss" : : "r" (0x7)); 586 587 /* Go! */ 588 ftx = 2; 589 590 while (ftx != 0) 591 ; 592 593 /* 594 * On success, modify_ldt will segfault us synchronously, 595 * and we'll escape via siglongjmp. 596 */ 597 598 failures++; 599 asm volatile ("mov %0, %%ss" : : "rm" (orig_ss)); 600 } 601 602 ftx = 100; /* Kill the thread. */ 603 syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); 604 605 if (pthread_join(thread, NULL) != 0) 606 err(1, "pthread_join"); 607 608 if (failures) { 609 printf("[FAIL]\t%d of %d iterations failed\n", failures, iters); 610 nerrs++; 611 } else { 612 printf("[OK]\tAll %d iterations succeeded\n", iters); 613 } 614 } 615 616 static int finish_exec_test(void) 617 { 618 /* 619 * Older kernel versions did inherit the LDT on exec() which is 620 * wrong because exec() starts from a clean state. 621 */ 622 check_invalid_segment(0, 1); 623 624 return nerrs ? 1 : 0; 625 } 626 627 static void do_exec_test(void) 628 { 629 printf("[RUN]\tTest exec\n"); 630 631 struct user_desc desc = { 632 .entry_number = 0, 633 .base_addr = 0, 634 .limit = 42, 635 .seg_32bit = 1, 636 .contents = 2, /* Code, not conforming */ 637 .read_exec_only = 0, 638 .limit_in_pages = 0, 639 .seg_not_present = 0, 640 .useable = 0 641 }; 642 install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB); 643 644 pid_t child = fork(); 645 if (child == 0) { 646 execl("/proc/self/exe", "ldt_gdt_test_exec", NULL); 647 printf("[FAIL]\tCould not exec self\n"); 648 exit(1); /* exec failed */ 649 } else { 650 int status; 651 if (waitpid(child, &status, 0) != child || 652 !WIFEXITED(status)) { 653 printf("[FAIL]\tChild died\n"); 654 nerrs++; 655 } else if (WEXITSTATUS(status) != 0) { 656 printf("[FAIL]\tChild failed\n"); 657 nerrs++; 658 } else { 659 printf("[OK]\tChild succeeded\n"); 660 } 661 } 662 } 663 664 static void setup_counter_page(void) 665 { 666 unsigned int *page = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 667 MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0); 668 if (page == MAP_FAILED) 669 err(1, "mmap"); 670 671 for (int i = 0; i < 1024; i++) 672 page[i] = i; 673 counter_page = page; 674 } 675 676 static int invoke_set_thread_area(void) 677 { 678 int ret; 679 asm volatile ("int $0x80" 680 : "=a" (ret), "+m" (low_user_desc) : 681 "a" (243), "b" (low_user_desc) 682 : INT80_CLOBBERS); 683 return ret; 684 } 685 686 static void setup_low_user_desc(void) 687 { 688 low_user_desc = mmap(NULL, 2 * sizeof(struct user_desc), 689 PROT_READ | PROT_WRITE, 690 MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0); 691 if (low_user_desc == MAP_FAILED) 692 err(1, "mmap"); 693 694 low_user_desc->entry_number = -1; 695 low_user_desc->base_addr = (unsigned long)&counter_page[1]; 696 low_user_desc->limit = 0xfffff; 697 low_user_desc->seg_32bit = 1; 698 low_user_desc->contents = 0; /* Data, grow-up*/ 699 low_user_desc->read_exec_only = 0; 700 low_user_desc->limit_in_pages = 1; 701 low_user_desc->seg_not_present = 0; 702 low_user_desc->useable = 0; 703 704 if (invoke_set_thread_area() == 0) { 705 gdt_entry_num = low_user_desc->entry_number; 706 printf("[NOTE]\tset_thread_area is available; will use GDT index %d\n", gdt_entry_num); 707 } else { 708 printf("[NOTE]\tset_thread_area is unavailable\n"); 709 } 710 711 low_user_desc_clear = low_user_desc + 1; 712 low_user_desc_clear->entry_number = gdt_entry_num; 713 low_user_desc_clear->read_exec_only = 1; 714 low_user_desc_clear->seg_not_present = 1; 715 } 716 717 static void test_gdt_invalidation(void) 718 { 719 if (!gdt_entry_num) 720 return; /* 64-bit only system -- we can't use set_thread_area */ 721 722 unsigned short prev_sel; 723 unsigned short sel; 724 unsigned int eax; 725 const char *result; 726 #ifdef __x86_64__ 727 unsigned long saved_base; 728 unsigned long new_base; 729 #endif 730 731 /* Test DS */ 732 invoke_set_thread_area(); 733 eax = 243; 734 sel = (gdt_entry_num << 3) | 3; 735 asm volatile ("movw %%ds, %[prev_sel]\n\t" 736 "movw %[sel], %%ds\n\t" 737 #ifdef __i386__ 738 "pushl %%ebx\n\t" 739 #endif 740 "movl %[arg1], %%ebx\n\t" 741 "int $0x80\n\t" /* Should invalidate ds */ 742 #ifdef __i386__ 743 "popl %%ebx\n\t" 744 #endif 745 "movw %%ds, %[sel]\n\t" 746 "movw %[prev_sel], %%ds" 747 : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), 748 "+a" (eax) 749 : "m" (low_user_desc_clear), 750 [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) 751 : INT80_CLOBBERS); 752 753 if (sel != 0) { 754 result = "FAIL"; 755 nerrs++; 756 } else { 757 result = "OK"; 758 } 759 printf("[%s]\tInvalidate DS with set_thread_area: new DS = 0x%hx\n", 760 result, sel); 761 762 /* Test ES */ 763 invoke_set_thread_area(); 764 eax = 243; 765 sel = (gdt_entry_num << 3) | 3; 766 asm volatile ("movw %%es, %[prev_sel]\n\t" 767 "movw %[sel], %%es\n\t" 768 #ifdef __i386__ 769 "pushl %%ebx\n\t" 770 #endif 771 "movl %[arg1], %%ebx\n\t" 772 "int $0x80\n\t" /* Should invalidate es */ 773 #ifdef __i386__ 774 "popl %%ebx\n\t" 775 #endif 776 "movw %%es, %[sel]\n\t" 777 "movw %[prev_sel], %%es" 778 : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), 779 "+a" (eax) 780 : "m" (low_user_desc_clear), 781 [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) 782 : INT80_CLOBBERS); 783 784 if (sel != 0) { 785 result = "FAIL"; 786 nerrs++; 787 } else { 788 result = "OK"; 789 } 790 printf("[%s]\tInvalidate ES with set_thread_area: new ES = 0x%hx\n", 791 result, sel); 792 793 /* Test FS */ 794 invoke_set_thread_area(); 795 eax = 243; 796 sel = (gdt_entry_num << 3) | 3; 797 #ifdef __x86_64__ 798 syscall(SYS_arch_prctl, ARCH_GET_FS, &saved_base); 799 #endif 800 asm volatile ("movw %%fs, %[prev_sel]\n\t" 801 "movw %[sel], %%fs\n\t" 802 #ifdef __i386__ 803 "pushl %%ebx\n\t" 804 #endif 805 "movl %[arg1], %%ebx\n\t" 806 "int $0x80\n\t" /* Should invalidate fs */ 807 #ifdef __i386__ 808 "popl %%ebx\n\t" 809 #endif 810 "movw %%fs, %[sel]\n\t" 811 : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), 812 "+a" (eax) 813 : "m" (low_user_desc_clear), 814 [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) 815 : INT80_CLOBBERS); 816 817 #ifdef __x86_64__ 818 syscall(SYS_arch_prctl, ARCH_GET_FS, &new_base); 819 #endif 820 821 /* Restore FS/BASE for glibc */ 822 asm volatile ("movw %[prev_sel], %%fs" : : [prev_sel] "rm" (prev_sel)); 823 #ifdef __x86_64__ 824 if (saved_base) 825 syscall(SYS_arch_prctl, ARCH_SET_FS, saved_base); 826 #endif 827 828 if (sel != 0) { 829 result = "FAIL"; 830 nerrs++; 831 } else { 832 result = "OK"; 833 } 834 printf("[%s]\tInvalidate FS with set_thread_area: new FS = 0x%hx\n", 835 result, sel); 836 837 #ifdef __x86_64__ 838 if (sel == 0 && new_base != 0) { 839 nerrs++; 840 printf("[FAIL]\tNew FSBASE was 0x%lx\n", new_base); 841 } else { 842 printf("[OK]\tNew FSBASE was zero\n"); 843 } 844 #endif 845 846 /* Test GS */ 847 invoke_set_thread_area(); 848 eax = 243; 849 sel = (gdt_entry_num << 3) | 3; 850 #ifdef __x86_64__ 851 syscall(SYS_arch_prctl, ARCH_GET_GS, &saved_base); 852 #endif 853 asm volatile ("movw %%gs, %[prev_sel]\n\t" 854 "movw %[sel], %%gs\n\t" 855 #ifdef __i386__ 856 "pushl %%ebx\n\t" 857 #endif 858 "movl %[arg1], %%ebx\n\t" 859 "int $0x80\n\t" /* Should invalidate gs */ 860 #ifdef __i386__ 861 "popl %%ebx\n\t" 862 #endif 863 "movw %%gs, %[sel]\n\t" 864 : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), 865 "+a" (eax) 866 : "m" (low_user_desc_clear), 867 [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) 868 : INT80_CLOBBERS); 869 870 #ifdef __x86_64__ 871 syscall(SYS_arch_prctl, ARCH_GET_GS, &new_base); 872 #endif 873 874 /* Restore GS/BASE for glibc */ 875 asm volatile ("movw %[prev_sel], %%gs" : : [prev_sel] "rm" (prev_sel)); 876 #ifdef __x86_64__ 877 if (saved_base) 878 syscall(SYS_arch_prctl, ARCH_SET_GS, saved_base); 879 #endif 880 881 if (sel != 0) { 882 result = "FAIL"; 883 nerrs++; 884 } else { 885 result = "OK"; 886 } 887 printf("[%s]\tInvalidate GS with set_thread_area: new GS = 0x%hx\n", 888 result, sel); 889 890 #ifdef __x86_64__ 891 if (sel == 0 && new_base != 0) { 892 nerrs++; 893 printf("[FAIL]\tNew GSBASE was 0x%lx\n", new_base); 894 } else { 895 printf("[OK]\tNew GSBASE was zero\n"); 896 } 897 #endif 898 } 899 900 int main(int argc, char **argv) 901 { 902 if (argc == 1 && !strcmp(argv[0], "ldt_gdt_test_exec")) 903 return finish_exec_test(); 904 905 setup_counter_page(); 906 setup_low_user_desc(); 907 908 do_simple_tests(); 909 910 do_multicpu_tests(); 911 912 do_exec_test(); 913 914 test_gdt_invalidation(); 915 916 return nerrs ? 1 : 0; 917 } 918