1/* $NetBSD: t_user_ldt.c,v 1.6 2021/04/30 13:53:30 christos 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#include <machine/gdt.h> 45 46#include <atf-c.h> 47 48static uint8_t *ldt_base; 49static bool user_ldt_supported; 50 51static void 52user_ldt_detect(void) 53{ 54 union descriptor desc; 55 int ret; 56 57 ret = i386_get_ldt(0, &desc, 1); 58 user_ldt_supported = (ret != -1) || (errno != ENOSYS && errno != EPERM); 59} 60 61static void 62init_ldt_base(void) 63{ 64 ldt_base = (uint8_t *)mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, 65 MAP_PRIVATE | MAP_ANON, -1, 0); 66 if (ldt_base == MAP_FAILED) 67 atf_tc_fail("mmap failed"); 68 munmap(ldt_base + PAGE_SIZE, PAGE_SIZE); 69} 70 71static void 72build_desc(union descriptor *desc, void *basep, uint32_t limit, int type, 73 int dpl, int def32, int gran) 74{ 75 uintptr_t base = (uintptr_t)basep; 76 77 limit--; 78 79 desc->sd.sd_lolimit = limit & 0x0000ffff; 80 desc->sd.sd_lobase = base & 0x00ffffff; 81 desc->sd.sd_type = type & 0x1F; 82 desc->sd.sd_dpl = dpl & 0x3; 83 desc->sd.sd_p = 1; 84 desc->sd.sd_hilimit = (limit & 0x00ff0000) >> 16; 85 desc->sd.sd_xx = 0; 86 desc->sd.sd_def32 = def32 ? 1 : 0; 87 desc->sd.sd_gran = gran ? 1 : 0; 88 desc->sd.sd_hibase = (base & 0xff000000) >> 24; 89} 90 91static void 92set_ds(unsigned int val) 93{ 94 __asm volatile("mov %0,%%ds"::"r" ((unsigned short)val)); 95} 96 97static void 98set_fs(unsigned int val) 99{ 100 __asm volatile("mov %0,%%fs"::"r" ((unsigned short)val)); 101} 102 103static uint8_t __noinline 104get_fs_byte(const char *addr) 105{ 106 uint8_t val; 107 __asm volatile ( 108 ".globl fs_read_begin; fs_read_begin:\n" 109 "movb %%fs:%1,%0\n" 110 ".globl fs_read_end; fs_read_end:\n" 111 : "=q" (val) : "m" (*addr) 112 ); 113 return val; 114} 115 116/* -------------------------------------------------------------------------- */ 117 118ATF_TC(filter_ops); 119ATF_TC_HEAD(filter_ops, tc) 120{ 121 atf_tc_set_md_var(tc, "descr", 122 "Ensure that the kernel correctly filters the descriptors"); 123} 124ATF_TC_BODY(filter_ops, tc) 125{ 126 union descriptor desc; 127 const int forbidden_types[] = { 128 SDT_SYS286TSS, 129 SDT_SYSLDT, 130 SDT_SYS286BSY, 131 SDT_SYS286CGT, 132 SDT_SYSTASKGT, 133 SDT_SYS286IGT, 134 SDT_SYS286TGT, 135 SDT_SYSNULL2, 136 SDT_SYS386TSS, 137 SDT_SYSNULL3, 138 SDT_SYS386BSY, 139 SDT_SYS386CGT, 140 SDT_SYSNULL4, 141 SDT_SYS386IGT, 142 SDT_SYS386TGT 143 }; 144 size_t i; 145 146 if (!user_ldt_supported) { 147 atf_tc_skip("USER_LDT disabled"); 148 } 149 150 /* The first LDT slots should not be settable. */ 151 for (i = 0; i < 10; i++) { 152 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, 153 SEL_UPL, 1, 0); 154 ATF_REQUIRE_EQ(i386_set_ldt(i, &desc, 1), -1); 155 ATF_REQUIRE_EQ(errno, EINVAL); 156 } 157 158 /* SEL_KPL should not be allowed. */ 159 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_KPL, 1, 0); 160 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); 161 ATF_REQUIRE_EQ(errno, EACCES); 162 163 /* Long-mode segments should not be allowed. */ 164 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); 165 desc.sd.sd_xx = 0b11; /* sd_avl | sd_long */ 166 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); 167 ATF_REQUIRE_EQ(errno, EACCES); 168 169 /* No forbidden type should be allowed. */ 170 for (i = 0; i < __arraycount(forbidden_types); i++) { 171 build_desc(&desc, ldt_base, PAGE_SIZE, forbidden_types[i], 172 SEL_UPL, 1, 0); 173 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); 174 ATF_REQUIRE_EQ(errno, EACCES); 175 } 176 177 /* Check the slot limit. */ 178 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); 179 ATF_REQUIRE_EQ(i386_set_ldt(MAX_USERLDT_SLOTS-1, &desc, 1), 180 MAX_USERLDT_SLOTS-1); 181 ATF_REQUIRE_EQ(i386_set_ldt(MAX_USERLDT_SLOTS, &desc, 1), -1); 182 ATF_REQUIRE_EQ(errno, EINVAL); 183} 184 185/* -------------------------------------------------------------------------- */ 186 187static void 188iretq_gp__gp_handler(int signo, siginfo_t *sig, void *ctx) 189{ 190 ATF_REQUIRE(sig->si_signo == SIGSEGV); 191 ATF_REQUIRE(sig->si_code == SEGV_ACCERR); 192 atf_tc_pass(); 193 /* NOTREACHED */ 194} 195 196ATF_TC(iretq_gp); 197ATF_TC_HEAD(iretq_gp, tc) 198{ 199 atf_tc_set_md_var(tc, "descr", 200 "Ensure that the kernel correctly handles iretq #GP faults"); 201} 202ATF_TC_BODY(iretq_gp, tc) 203{ 204 union descriptor desc; 205 struct sigaction act; 206 207 if (!user_ldt_supported) { 208 atf_tc_skip("USER_LDT disabled"); 209 } 210 211 /* Build a desc, set %ds to it. */ 212 build_desc(&desc, 0, 0xFFFFFUL, SDT_MEMRWA, SEL_UPL, 1, 1); 213 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); 214 set_ds(LSEL(256, SEL_UPL)); 215 216 /* Register the fault handler. */ 217 memset(&act, 0, sizeof(act)); 218 act.sa_sigaction = iretq_gp__gp_handler; 219 act.sa_flags = SA_SIGINFO; 220 ATF_REQUIRE_EQ(sigaction(SIGSEGV, &act, NULL), 0); 221 222 /* Set NULL on %ds, iretq should fault. */ 223 memset(&desc, 0, sizeof(desc)); 224 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); 225 226 atf_tc_fail("test did not fault as expected"); 227} 228 229/* -------------------------------------------------------------------------- */ 230 231static void 232iretq_np__np_handler(int signo, siginfo_t *sig, void *ctx) 233{ 234 ATF_REQUIRE(sig->si_signo == SIGBUS); 235 ATF_REQUIRE(sig->si_code == BUS_ADRERR); 236 atf_tc_pass(); 237 /* NOTREACHED */ 238} 239 240ATF_TC(iretq_np); 241ATF_TC_HEAD(iretq_np, tc) 242{ 243 atf_tc_set_md_var(tc, "descr", 244 "Ensure that the kernel correctly handles iretq #NP faults"); 245} 246ATF_TC_BODY(iretq_np, tc) 247{ 248 union descriptor desc; 249 struct sigaction act; 250 251 if (!user_ldt_supported) { 252 atf_tc_skip("USER_LDT disabled"); 253 } 254 255 /* Build a desc, set %ds to it. */ 256 build_desc(&desc, 0, 0xFFFFFFFFUL, SDT_MEMRWA, SEL_UPL, 1, 1); 257 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); 258 set_ds(LSEL(256, SEL_UPL)); 259 260 /* Register the fault handler. */ 261 memset(&act, 0, sizeof(act)); 262 act.sa_sigaction = iretq_np__np_handler; 263 act.sa_flags = SA_SIGINFO; 264 ATF_REQUIRE_EQ(sigaction(SIGBUS, &act, NULL), 0); 265 266 /* Set non-present on %ds, iretq should fault. */ 267 desc.sd.sd_p = 0; 268 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); 269 270 atf_tc_fail("test did not fault as expected"); 271} 272 273/* -------------------------------------------------------------------------- */ 274 275static volatile bool expect_crash; 276 277static void 278user_ldt__gp_handler(int signo, siginfo_t *sig, void *ctx) 279{ 280 ucontext_t *uctx = ctx; 281 extern uint8_t fs_read_begin; 282 283 if (!expect_crash) { 284 atf_tc_fail("unexpected #GP"); 285 } 286 287 ATF_REQUIRE(sig->si_signo == SIGSEGV); 288 ATF_REQUIRE(sig->si_code == SEGV_ACCERR); 289 290 if (uctx->uc_mcontext.__gregs[_REG_EIP] != (intptr_t)&fs_read_begin) { 291 atf_tc_fail("got #GP on the wrong instruction"); 292 } 293 294 set_fs(GSEL(GUDATA_SEL, SEL_UPL)); 295 296 atf_tc_pass(); 297 /* NOTREACHED */ 298} 299 300ATF_TC(user_ldt); 301ATF_TC_HEAD(user_ldt, tc) 302{ 303 atf_tc_set_md_var(tc, "descr", 304 "Ensure that USER_LDT works as expected"); 305} 306ATF_TC_BODY(user_ldt, tc) 307{ 308 union descriptor desc; 309 struct sigaction act; 310 311 if (!user_ldt_supported) { 312 atf_tc_skip("USER_LDT disabled"); 313 } 314 315 memset(&act, 0, sizeof(act)); 316 act.sa_sigaction = user_ldt__gp_handler; 317 act.sa_flags = SA_SIGINFO; 318 ATF_REQUIRE_EQ(sigaction(SIGSEGV, &act, NULL), 0); 319 320 build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); 321 ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); 322 323 set_fs(LSEL(256, SEL_UPL)); 324 325 ldt_base[666] = 123; 326 ldt_base[PAGE_SIZE-1] = 213; 327 __insn_barrier(); 328 ATF_REQUIRE_EQ(get_fs_byte((char *)666), 123); 329 ATF_REQUIRE_EQ(get_fs_byte((char *)PAGE_SIZE-1), 213); 330 331 /* This one should fault, and it concludes our test case. */ 332 expect_crash = true; 333 get_fs_byte((char *)PAGE_SIZE); 334 335 atf_tc_fail("test did not fault as expected"); 336} 337 338/* -------------------------------------------------------------------------- */ 339 340ATF_TP_ADD_TCS(tp) 341{ 342 user_ldt_detect(); 343 init_ldt_base(); 344 345 ATF_TP_ADD_TC(tp, filter_ops); 346 ATF_TP_ADD_TC(tp, iretq_gp); 347 ATF_TP_ADD_TC(tp, iretq_np); 348 ATF_TP_ADD_TC(tp, user_ldt); 349 350 return atf_no_error(); 351} 352