fsgsbase.c 16 KB

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