1 /* $NetBSD: t_user_ldt.c,v 1.1 2020/04/19 13:22:58 maxv Exp $ */ 2 3 /* 4 * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Maxime Villard. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <errno.h> 37 #include <signal.h> 38 39 #include <sys/types.h> 40 #include <sys/mman.h> 41 #include <machine/segments.h> 42 #include <machine/sysarch.h> 43 #include <machine/vmparam.h> 44 45 #include <atf-c.h> 46 47 static uint8_t *ldt_base; 48 static bool user_ldt_supported; 49 50 static void 51 user_ldt_detect(void) 52 { 53 union descriptor desc; 54 int ret; 55 56 ret = i386_get_ldt(0, &desc, 1); 57 user_ldt_supported = (ret != -1) || (errno != ENOTSUP); 58 } 59 60 static void 61 init_ldt_base(void) 62 { 63 ldt_base = (uint8_t *)mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, 64 MAP_PRIVATE | MAP_ANON, -1, 0); 65 if (ldt_base == MAP_FAILED) 66 atf_tc_fail("mmap failed"); 67 munmap(ldt_base + PAGE_SIZE, PAGE_SIZE); 68 } 69 70 static void 71 build_desc(union descriptor *desc, void *basep, uint32_t limit, int type, 72 int dpl, int def32, int gran) 73 { 74 uintptr_t base = (uintptr_t)basep; 75 76 limit--; 77 78 desc->sd.sd_lolimit = limit & 0x0000ffff; 79 desc->sd.sd_lobase = base & 0x00ffffff; 80 desc->sd.sd_type = type & 0x1F; 81 desc->sd.sd_dpl = dpl & 0x3; 82 desc->sd.sd_p = 1; 83 desc->sd.sd_hilimit = (limit & 0x00ff0000) >> 16; 84 desc->sd.sd_xx = 0; 85 desc->sd.sd_def32 = def32 ? 1 : 0; 86 desc->sd.sd_gran = gran ? 1 : 0; 87 desc->sd.sd_hibase = (base & 0xff000000) >> 24; 88 } 89 90 static void 91 set_fs(unsigned int val) 92 { 93 __asm volatile("mov %0,%%fs"::"r" ((unsigned short)val)); 94 } 95 96 static uint8_t __noinline 97 get_fs_byte(const char *addr) 98 { 99 uint8_t val; 100 __asm volatile ( 101 ".globl fs_read_begin; fs_read_begin:\n" 102 "movb %%fs:%1,%0\n" 103 ".globl fs_read_end; fs_read_end:\n" 104 : "=q" (val) : "m" (*addr) 105 ); 106 return val; 107 } 108 109 /* -------------------------------------------------------------------------- */ 110 111 ATF_TC(filter_ops); 112 ATF_TC_HEAD(filter_ops, tc) 113 { 114 atf_tc_set_md_var(tc, "descr", 115 "Ensure that the kernel correctly filters the descriptors"); 116 } 117 ATF_TC_BODY(filter_ops, tc) 118 { 119 union descriptor desc; 120 const int forbidden_types[] = { 121 SDT_SYS286TSS, 122 SDT_SYSLDT, 123 SDT_SYS286BSY, 124 SDT_SYS286CGT, 125 SDT_SYSTASKGT, 126 SDT_SYS286IGT, 127 SDT_SYS286TGT, 128 SDT_SYSNULL2, 129 SDT_SYS386TSS, 130 SDT_SYSNULL3, 131 SDT_SYS386BSY, 132 SDT_SYS386CGT, 133 SDT_SYSNULL4, 134 SDT_SYS386IGT, 135 SDT_SYS386TGT 136 }; 137 size_t i; 138 139 if (!user_ldt_supported) { 140 atf_tc_skip("USER_LDT disabled"); 141 } 142 143 /* The first LDT slots should not be settable. */ 144 for (i = 0; i < 10; i++) { 145 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, 146 SEL_UPL, 1, 0); 147 ATF_REQUIRE_EQ(i386_set_ldt(i, &desc, 1), -1); 148 ATF_REQUIRE_EQ(errno, EINVAL); 149 } 150 151 /* SEL_KPL should not be allowed. */ 152 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_KPL, 1, 0); 153 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); 154 ATF_REQUIRE_EQ(errno, EACCES); 155 156 /* Long-mode segments should not be allowed. */ 157 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); 158 desc.sd.sd_xx = 0b11; /* sd_avl | sd_long */ 159 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); 160 ATF_REQUIRE_EQ(errno, EACCES); 161 162 /* No forbidden type should be allowed. */ 163 for (i = 0; i < __arraycount(forbidden_types); i++) { 164 build_desc(&desc, ldt_base, PAGE_SIZE, forbidden_types[i], 165 SEL_UPL, 1, 0); 166 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); 167 ATF_REQUIRE_EQ(errno, EACCES); 168 } 169 } 170 171 /* -------------------------------------------------------------------------- */ 172 173 static volatile bool expect_crash; 174 175 static void 176 gp_handler(int signo, siginfo_t *sig, void *ctx) 177 { 178 ucontext_t *uctx = ctx; 179 extern uint8_t fs_read_begin; 180 181 if (!expect_crash) { 182 atf_tc_fail("unexpected #GP"); 183 } 184 185 ATF_REQUIRE(sig->si_signo == SIGSEGV); 186 ATF_REQUIRE(sig->si_code == SEGV_ACCERR); 187 188 if (uctx->uc_mcontext.__gregs[_REG_EIP] != (intptr_t)&fs_read_begin) { 189 atf_tc_fail("got #GP on the wrong instruction"); 190 } 191 192 set_fs(GSEL(GUDATA_SEL, SEL_UPL)); 193 194 atf_tc_pass(); 195 /* NOTREACHED */ 196 } 197 198 ATF_TC(user_ldt); 199 ATF_TC_HEAD(user_ldt, tc) 200 { 201 atf_tc_set_md_var(tc, "descr", 202 "Ensure that USER_LDT works as expected"); 203 } 204 ATF_TC_BODY(user_ldt, tc) 205 { 206 union descriptor desc; 207 struct sigaction act; 208 209 if (!user_ldt_supported) { 210 atf_tc_skip("USER_LDT disabled"); 211 } 212 213 memset(&act, 0, sizeof(act)); 214 act.sa_sigaction = gp_handler; 215 act.sa_flags = SA_SIGINFO; 216 ATF_REQUIRE_EQ(sigaction(SIGSEGV, &act, NULL), 0); 217 218 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); 219 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); 220 221 set_fs(LSEL(256, SEL_UPL)); 222 223 ldt_base[666] = 123; 224 ldt_base[PAGE_SIZE-1] = 213; 225 __insn_barrier(); 226 ATF_REQUIRE_EQ(get_fs_byte((char *)666), 123); 227 ATF_REQUIRE_EQ(get_fs_byte((char *)PAGE_SIZE-1), 213); 228 229 /* This one should fault, and it concludes our test case. */ 230 expect_crash = true; 231 get_fs_byte((char *)PAGE_SIZE); 232 233 atf_tc_fail("test did not fault as expected"); 234 } 235 236 /* -------------------------------------------------------------------------- */ 237 238 ATF_TP_ADD_TCS(tp) 239 { 240 user_ldt_detect(); 241 init_ldt_base(); 242 243 ATF_TP_ADD_TC(tp, filter_ops); 244 ATF_TP_ADD_TC(tp, user_ldt); 245 246 return atf_no_error(); 247 } 248