cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ldt_gdt.c (22795B)


      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
     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 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
    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#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
    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         = 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 */
    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_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 */
    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 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
    503static void fix_sa_restorer(int sig)
    504{
    505	/* 64-bit glibc works fine. */
    506}
    507#endif
    508
    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#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
    626static 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
    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         = 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
    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#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
    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}