Linux-libre 4.14.68-gnu
[librecmc/linux-libre.git] / tools / testing / selftests / x86 / ldt_gdt.c
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 #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 #else
52 # define INT80_CLOBBERS
53 #endif
54
55 static int nerrs;
56
57 /* Points to an array of 1024 ints, each holding its own index. */
58 static const unsigned int *counter_page;
59 static struct user_desc *low_user_desc;
60 static struct user_desc *low_user_desc_clear;  /* Use to delete GDT entry */
61 static int gdt_entry_num;
62
63 static 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
91 static 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 the
123          * 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
139 static 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 #endif
150                 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
189 static 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
202 static 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
223 static 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
232 static 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
244 static 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         = 0
256         };
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 #endif
326
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 idle
429  * 1: thread armed
430  * 2: thread should clear LDT entry 0
431  * 3: thread should exit
432  */
433 static volatile unsigned int ftx;
434
435 static 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_RESTORE
467 #define SA_RESTORER 0x04000000
468 #endif
469
470 /*
471  * The UAPI header calls this 'struct sigaction', which conflicts with
472  * glibc.  Sigh.
473  */
474 struct 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
481 static 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 to
488                  * sa_restorer.  This interacts quite badly with anything
489                  * that fiddles with SS because it can trigger legacy
490                  * stack switching.  Patch it up.  See:
491                  *
492                  * https://sourceware.org/bugzilla/show_bug.cgi?id=21269
493                  */
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 #else
503 static void fix_sa_restorer(int sig)
504 {
505         /* 64-bit glibc works fine. */
506 }
507 #endif
508
509 static 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
523 static jmp_buf jmpbuf;
524
525 static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
526 {
527         siglongjmp(jmpbuf, 1);
528 }
529
530 static 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 #endif
556
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         = 0
582                 };
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
626 static int finish_exec_test(void)
627 {
628         /*
629          * Older kernel versions did inherit the LDT on exec() which is
630          * wrong because exec() starts from a clean state.
631          */
632         check_invalid_segment(0, 1);
633
634         return nerrs ? 1 : 0;
635 }
636
637 static 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         = 0
651         };
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
674 static 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
686 static 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
696 static 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
727 static 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 #endif
740
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 #endif
750                       "movl %[arg1], %%ebx\n\t"
751                       "int $0x80\n\t"   /* Should invalidate ds */
752 #ifdef __i386__
753                       "popl %%ebx\n\t"
754 #endif
755                       "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 #endif
781                       "movl %[arg1], %%ebx\n\t"
782                       "int $0x80\n\t"   /* Should invalidate es */
783 #ifdef __i386__
784                       "popl %%ebx\n\t"
785 #endif
786                       "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 #endif
810         asm volatile ("movw %%fs, %[prev_sel]\n\t"
811                       "movw %[sel], %%fs\n\t"
812 #ifdef __i386__
813                       "pushl %%ebx\n\t"
814 #endif
815                       "movl %[arg1], %%ebx\n\t"
816                       "int $0x80\n\t"   /* Should invalidate fs */
817 #ifdef __i386__
818                       "popl %%ebx\n\t"
819 #endif
820                       "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 #endif
830
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 #endif
837
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 #endif
855
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 #endif
863         asm volatile ("movw %%gs, %[prev_sel]\n\t"
864                       "movw %[sel], %%gs\n\t"
865 #ifdef __i386__
866                       "pushl %%ebx\n\t"
867 #endif
868                       "movl %[arg1], %%ebx\n\t"
869                       "int $0x80\n\t"   /* Should invalidate gs */
870 #ifdef __i386__
871                       "popl %%ebx\n\t"
872 #endif
873                       "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 #endif
883
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 #endif
890
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 #endif
908 }
909
910 int 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 }