^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * fsgsbase.c, an fsgsbase test
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (c) 2014-2016 Andy Lutomirski
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #define _GNU_SOURCE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <stdbool.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <sys/syscall.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <sys/user.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <asm/prctl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <sys/prctl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <signal.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <limits.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <sys/ucontext.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/futex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <pthread.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <asm/ldt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <sys/mman.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <stddef.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <sys/ptrace.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <sys/wait.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <setjmp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #ifndef __x86_64__
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) # error This test is 64-bit only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static volatile sig_atomic_t want_segv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static volatile unsigned long segv_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static unsigned short *shared_scratch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static int nerrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) int flags)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) struct sigaction sa;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) memset(&sa, 0, sizeof(sa));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) sa.sa_sigaction = handler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) sa.sa_flags = SA_SIGINFO | flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) sigemptyset(&sa.sa_mask);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) if (sigaction(sig, &sa, 0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) err(1, "sigaction");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static void clearhandler(int sig)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) struct sigaction sa;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) memset(&sa, 0, sizeof(sa));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) sa.sa_handler = SIG_DFL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) sigemptyset(&sa.sa_mask);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) if (sigaction(sig, &sa, 0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) err(1, "sigaction");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) ucontext_t *ctx = (ucontext_t*)ctx_void;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) if (!want_segv) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) clearhandler(SIGSEGV);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return; /* Crash cleanly. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) want_segv = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) segv_addr = (unsigned long)si->si_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) ctx->uc_mcontext.gregs[REG_RIP] += 4; /* Skip the faulting mov */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static jmp_buf jmpbuf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static void sigill(int sig, siginfo_t *si, void *ctx_void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) siglongjmp(jmpbuf, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) static bool have_fsgsbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static inline unsigned long rdgsbase(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) unsigned long gsbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) asm volatile("rdgsbase %0" : "=r" (gsbase) :: "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) return gsbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static inline unsigned long rdfsbase(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) unsigned long fsbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) asm volatile("rdfsbase %0" : "=r" (fsbase) :: "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) return fsbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static inline void wrgsbase(unsigned long gsbase)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) asm volatile("wrgsbase %0" :: "r" (gsbase) : "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static inline void wrfsbase(unsigned long fsbase)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) asm volatile("wrfsbase %0" :: "r" (fsbase) : "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) enum which_base { FS, GS };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static unsigned long read_base(enum which_base which)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) unsigned long offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) * Unless we have FSGSBASE, there's no direct way to do this from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) * user mode. We can get at it indirectly using signals, though.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) want_segv = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) offset = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (which == FS) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) /* Use a constant-length instruction here. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) asm volatile ("mov %%fs:(%%rcx), %%rax" : : "c" (offset) : "rax");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) asm volatile ("mov %%gs:(%%rcx), %%rax" : : "c" (offset) : "rax");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (!want_segv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) return segv_addr + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) * If that didn't segfault, try the other end of the address space.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) * Unless we get really unlucky and run into the vsyscall page, this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) * is guaranteed to segfault.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) offset = (ULONG_MAX >> 1) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (which == FS) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) asm volatile ("mov %%fs:(%%rcx), %%rax"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) : : "c" (offset) : "rax");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) asm volatile ("mov %%gs:(%%rcx), %%rax"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) : : "c" (offset) : "rax");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) if (!want_segv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return segv_addr + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) abort();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static void check_gs_value(unsigned long value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) unsigned long base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) unsigned short sel;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) printf("[RUN]\tARCH_SET_GS to 0x%lx\n", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (syscall(SYS_arch_prctl, ARCH_SET_GS, value) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) asm volatile ("mov %%gs, %0" : "=rm" (sel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) base = read_base(GS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (base == value) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) printf("[OK]\tGSBASE was set as expected (selector 0x%hx)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) sel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) printf("[FAIL]\tGSBASE was not as expected: got 0x%lx (selector 0x%hx)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) base, sel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) err(1, "ARCH_GET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if (base == value) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) printf("[OK]\tARCH_GET_GS worked as expected (selector 0x%hx)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) sel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) printf("[FAIL]\tARCH_GET_GS was not as expected: got 0x%lx (selector 0x%hx)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) base, sel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) static void mov_0_gs(unsigned long initial_base, bool schedule)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) unsigned long base, arch_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) printf("[RUN]\tARCH_SET_GS to 0x%lx then mov 0 to %%gs%s\n", initial_base, schedule ? " and schedule " : "");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) if (syscall(SYS_arch_prctl, ARCH_SET_GS, initial_base) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) if (schedule)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) usleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) asm volatile ("mov %0, %%gs" : : "rm" (0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) base = read_base(GS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) if (syscall(SYS_arch_prctl, ARCH_GET_GS, &arch_base) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) err(1, "ARCH_GET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) if (base == arch_base) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) printf("[OK]\tGSBASE is 0x%lx\n", base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) printf("[FAIL]\tGSBASE changed to 0x%lx but kernel reports 0x%lx\n", base, arch_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static volatile unsigned long remote_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) static volatile bool remote_hard_zero;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) static volatile unsigned int ftx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) * ARCH_SET_FS/GS(0) may or may not program a selector of zero. HARD_ZERO
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) * means to force the selector to zero to improve test coverage.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) #define HARD_ZERO 0xa1fa5f343cb85fa4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) static void do_remote_base()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) unsigned long to_set = remote_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) bool hard_zero = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) if (to_set == HARD_ZERO) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) to_set = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) hard_zero = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (syscall(SYS_arch_prctl, ARCH_SET_GS, to_set) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) if (hard_zero)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) unsigned short sel;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) asm volatile ("mov %%gs, %0" : "=rm" (sel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) printf("\tother thread: ARCH_SET_GS(0x%lx)%s -- sel is 0x%hx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) to_set, hard_zero ? " and clear gs" : "", sel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) static __thread int set_thread_area_entry_number = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) static unsigned short load_gs(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) * Sets GS != 0 and GSBASE != 0 but arranges for the kernel to think
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) * that GSBASE == 0 (i.e. thread.gsbase == 0).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) /* Step 1: tell the kernel that we have GSBASE == 0. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) if (syscall(SYS_arch_prctl, ARCH_SET_GS, 0) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) /* Step 2: change GSBASE without telling the kernel. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) struct user_desc desc = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) .entry_number = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) .base_addr = 0xBAADF00D,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) .limit = 0xfffff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) .seg_32bit = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) .contents = 0, /* Data, grow-up */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) .read_exec_only = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) .limit_in_pages = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) .seg_not_present = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) .useable = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) printf("\tusing LDT slot 0\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) return 0x7;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) /* No modify_ldt for us (configured out, perhaps) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) struct user_desc *low_desc = mmap(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) NULL, sizeof(desc),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) PROT_READ | PROT_WRITE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) memcpy(low_desc, &desc, sizeof(desc));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) low_desc->entry_number = set_thread_area_entry_number;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) /* 32-bit set_thread_area */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) long ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) asm volatile ("int $0x80"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) : "=a" (ret), "+m" (*low_desc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) : "a" (243), "b" (low_desc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) : "r8", "r9", "r10", "r11");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) memcpy(&desc, low_desc, sizeof(desc));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) munmap(low_desc, sizeof(desc));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) if (ret != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) printf("[NOTE]\tcould not create a segment -- test won't do anything\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) printf("\tusing GDT slot %d\n", desc.entry_number);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) set_thread_area_entry_number = desc.entry_number;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) unsigned short gs = (unsigned short)((desc.entry_number << 3) | 0x3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) asm volatile ("mov %0, %%gs" : : "rm" (gs));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) return gs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) void test_wrbase(unsigned short index, unsigned long base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) unsigned short newindex;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) unsigned long newbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) printf("[RUN]\tGS = 0x%hx, GSBASE = 0x%lx\n", index, base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) asm volatile ("mov %0, %%gs" : : "rm" (index));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) wrgsbase(base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) remote_base = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) ftx = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) while (ftx != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) asm volatile ("mov %%gs, %0" : "=rm" (newindex));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) newbase = rdgsbase();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) if (newindex == index && newbase == base) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) printf("[OK]\tIndex and base were preserved\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) printf("[FAIL]\tAfter switch, GS = 0x%hx and GSBASE = 0x%lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) newindex, newbase);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) static void *threadproc(void *ctx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) while (ftx == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) syscall(SYS_futex, &ftx, FUTEX_WAIT, 0, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) if (ftx == 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) if (ftx == 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) do_remote_base();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) } else if (ftx == 2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) * On AMD chips, this causes GSBASE != 0, GS == 0, and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) * thread.gsbase == 0.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) load_gs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) errx(1, "helper thread got bad command");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) ftx = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) static void set_gs_and_switch_to(unsigned long local,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) unsigned short force_sel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) unsigned long remote)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) unsigned long base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) unsigned short sel_pre_sched, sel_post_sched;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) bool hard_zero = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) if (local == HARD_ZERO) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) hard_zero = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) local = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) printf("[RUN]\tARCH_SET_GS(0x%lx)%s, then schedule to 0x%lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) local, hard_zero ? " and clear gs" : "", remote);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) if (force_sel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) printf("\tBefore schedule, set selector to 0x%hx\n", force_sel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) if (syscall(SYS_arch_prctl, ARCH_SET_GS, local) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) if (hard_zero)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) if (read_base(GS) != local) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) printf("[FAIL]\tGSBASE wasn't set as expected\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) if (force_sel) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) sel_pre_sched = force_sel;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) local = read_base(GS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) * Signal delivery seems to mess up weird selectors. Put it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) * back.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) asm volatile ("mov %%gs, %0" : "=rm" (sel_pre_sched));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) remote_base = remote;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) ftx = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) while (ftx != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) asm volatile ("mov %%gs, %0" : "=rm" (sel_post_sched));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) base = read_base(GS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) if (base == local && sel_pre_sched == sel_post_sched) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) printf("[OK]\tGS/BASE remained 0x%hx/0x%lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) sel_pre_sched, local);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) printf("[FAIL]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) sel_pre_sched, local, sel_post_sched, base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) static void test_unexpected_base(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) unsigned long base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) printf("[RUN]\tARCH_SET_GS(0), clear gs, then manipulate GSBASE in a different thread\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) if (syscall(SYS_arch_prctl, ARCH_SET_GS, 0) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) ftx = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) while (ftx != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) base = read_base(GS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) if (base == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) printf("[OK]\tGSBASE remained 0\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) printf("[FAIL]\tGSBASE changed to 0x%lx\n", base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) #define USER_REGS_OFFSET(r) offsetof(struct user_regs_struct, r)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) static void test_ptrace_write_gs_read_base(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) pid_t child = fork();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) if (child < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) err(1, "fork");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) if (child == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) printf("[RUN]\tPTRACE_POKE GS, read GSBASE back\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) printf("[RUN]\tARCH_SET_GS to 1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) if (syscall(SYS_arch_prctl, ARCH_SET_GS, 1) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) err(1, "ARCH_SET_GS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) err(1, "PTRACE_TRACEME");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) raise(SIGTRAP);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) _exit(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) wait(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) if (WSTOPSIG(status) == SIGTRAP) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) unsigned long base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) unsigned long gs_offset = USER_REGS_OFFSET(gs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) unsigned long base_offset = USER_REGS_OFFSET(gs_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) /* Read the initial base. It should be 1. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) if (base == 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) printf("[OK]\tGSBASE started at 1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) printf("[FAIL]\tGSBASE started at 0x%lx\n", base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) printf("[RUN]\tSet GS = 0x7, read GSBASE\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) /* Poke an LDT selector into GS. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) if (ptrace(PTRACE_POKEUSER, child, gs_offset, 0x7) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) err(1, "PTRACE_POKEUSER");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) /* And read the base. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) if (base == 0 || base == 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) printf("[OK]\tGSBASE reads as 0x%lx with invalid GS\n", base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) printf("[FAIL]\tGSBASE=0x%lx (should be 0 or 1)\n", base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) ptrace(PTRACE_CONT, child, NULL, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) wait(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) if (!WIFEXITED(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) printf("[WARN]\tChild didn't exit cleanly.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) static void test_ptrace_write_gsbase(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) pid_t child = fork();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) if (child < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) err(1, "fork");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) if (child == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) printf("[RUN]\tPTRACE_POKE(), write GSBASE from ptracer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) *shared_scratch = load_gs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) err(1, "PTRACE_TRACEME");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) raise(SIGTRAP);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) _exit(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) wait(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) if (WSTOPSIG(status) == SIGTRAP) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) unsigned long gs, base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) unsigned long gs_offset = USER_REGS_OFFSET(gs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) unsigned long base_offset = USER_REGS_OFFSET(gs_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) if (gs != *shared_scratch) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) printf("[FAIL]\tGS is not prepared with nonzero\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) goto END;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) if (ptrace(PTRACE_POKEUSER, child, base_offset, 0xFF) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) err(1, "PTRACE_POKEUSER");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) * In a non-FSGSBASE system, the nonzero selector will load
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) * GSBASE (again). But what is tested here is whether the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) * selector value is changed or not by the GSBASE write in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) * a ptracer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) if (gs != *shared_scratch) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) printf("[FAIL]\tGS changed to %lx\n", gs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) * On older kernels, poking a nonzero value into the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) * base would zero the selector. On newer kernels,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) * this behavior has changed -- poking the base
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) * changes only the base and, if FSGSBASE is not
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) * available, this may have no effect once the tracee
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) * is resumed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) if (gs == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) printf("\tNote: this is expected behavior on older kernels.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) } else if (have_fsgsbase && (base != 0xFF)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) nerrs++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) printf("[FAIL]\tGSBASE changed to %lx\n", base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) printf("[OK]\tGS remained 0x%hx", *shared_scratch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) if (have_fsgsbase)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) printf(" and GSBASE changed to 0xFF");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) printf("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) END:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) ptrace(PTRACE_CONT, child, NULL, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) wait(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) if (!WIFEXITED(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) printf("[WARN]\tChild didn't exit cleanly.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) int main()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) pthread_t thread;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) shared_scratch = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) MAP_ANONYMOUS | MAP_SHARED, -1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) /* Do these tests before we have an LDT. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) test_ptrace_write_gs_read_base();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) /* Probe FSGSBASE */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) sethandler(SIGILL, sigill, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) if (sigsetjmp(jmpbuf, 1) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) rdfsbase();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) have_fsgsbase = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) printf("\tFSGSBASE instructions are enabled\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) printf("\tFSGSBASE instructions are disabled\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) clearhandler(SIGILL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) sethandler(SIGSEGV, sigsegv, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) check_gs_value(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) check_gs_value(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) check_gs_value(0x200000000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) check_gs_value(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) check_gs_value(0x200000000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) check_gs_value(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) for (int sched = 0; sched < 2; sched++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) mov_0_gs(0, !!sched);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) mov_0_gs(1, !!sched);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) mov_0_gs(0x200000000, !!sched);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) /* Set up for multithreading. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) cpu_set_t cpuset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) CPU_ZERO(&cpuset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) CPU_SET(0, &cpuset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) err(1, "sched_setaffinity to CPU 0"); /* should never fail */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) if (pthread_create(&thread, 0, threadproc, 0) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) err(1, "pthread_create");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) static unsigned long bases_with_hard_zero[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) 0, HARD_ZERO, 1, 0x200000000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) for (int local = 0; local < 4; local++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) for (int remote = 0; remote < 4; remote++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) for (unsigned short s = 0; s < 5; s++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) unsigned short sel = s;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) if (s == 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) asm ("mov %%ss, %0" : "=rm" (sel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) set_gs_and_switch_to(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) bases_with_hard_zero[local],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) sel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) bases_with_hard_zero[remote]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) test_unexpected_base();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) if (have_fsgsbase) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) unsigned short ss;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) asm volatile ("mov %%ss, %0" : "=rm" (ss));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) test_wrbase(0, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) test_wrbase(0, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) test_wrbase(0, 0x200000000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) test_wrbase(0, 0xffffffffffffffff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) test_wrbase(ss, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) test_wrbase(ss, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) test_wrbase(ss, 0x200000000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) test_wrbase(ss, 0xffffffffffffffff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) ftx = 3; /* Kill the thread. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) if (pthread_join(thread, NULL) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) err(1, "pthread_join");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) test_ptrace_write_gsbase();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) return nerrs == 0 ? 0 : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) }