linux/tools/testing/selftests/x86/ldt_gdt.c
<<
>>
Prefs
   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
  45static int nerrs;
  46
  47static 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
  75static 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
 116static 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
 145static bool install_valid(const struct user_desc *desc, uint32_t ar)
 146{
 147        return install_valid_mode(desc, ar, false);
 148}
 149
 150static 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
 171static 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
 180static 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
 192static 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 */
 366static volatile unsigned int ftx;
 367
 368static 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
 397static 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
 410static jmp_buf jmpbuf;
 411
 412static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
 413{
 414        siglongjmp(jmpbuf, 1);
 415}
 416
 417static 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
 513static 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
 527static 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
 564int 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