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