File Index
Files and directories
Sign in
FILE

sysret_rip.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/x86/sysret_rip.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls
  4 * Copyright (c) 2014-2016 Andrew Lutomirski
  5 */
  6
  7#define _GNU_SOURCE
  8
  9#include <stdlib.h>
 10#include <unistd.h>
 11#include <stdio.h>
 12#include <string.h>
 13#include <inttypes.h>
 14#include <sys/signal.h>
 15#include <sys/ucontext.h>
 16#include <sys/syscall.h>
 17#include <err.h>
 18#include <stddef.h>
 19#include <stdbool.h>
 20#include <setjmp.h>
 21#include <sys/user.h>
 22#include <sys/mman.h>
 23#include <assert.h>
 24
 25#include "helpers.h"
 26
 27/*
 28 * These items are in clang_helpers_64.S, in order to avoid clang inline asm
 29 * limitations:
 30 */
 31void test_syscall_ins(void);
 32extern const char test_page[];
 33
 34static void const *current_test_page_addr = test_page;
 35
 36/* State used by our signal handlers. */
 37static gregset_t initial_regs;
 38
 39static volatile unsigned long rip;
 40
 41static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void)
 42{
 43	ucontext_t *ctx = (ucontext_t*)ctx_void;
 44
 45	if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
 46		printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n",
 47		       rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
 48		fflush(stdout);
 49		_exit(1);
 50	}
 51
 52	memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
 53
 54	printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip);
 55}
 56
 57static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
 58{
 59	ucontext_t *ctx = (ucontext_t*)ctx_void;
 60
 61	memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
 62
 63	/* Set IP and CX to match so that SYSRET can happen. */
 64	ctx->uc_mcontext.gregs[REG_RIP] = rip;
 65	ctx->uc_mcontext.gregs[REG_RCX] = rip;
 66
 67	/* R11 and EFLAGS should already match. */
 68	assert(ctx->uc_mcontext.gregs[REG_EFL] ==
 69	       ctx->uc_mcontext.gregs[REG_R11]);
 70
 71	sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND);
 72
 73	return;
 74}
 75
 76static void test_sigreturn_to(unsigned long ip)
 77{
 78	rip = ip;
 79	printf("[RUN]\tsigreturn to 0x%lx\n", ip);
 80	raise(SIGUSR1);
 81}
 82
 83static jmp_buf jmpbuf;
 84
 85static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void)
 86{
 87	ucontext_t *ctx = (ucontext_t*)ctx_void;
 88
 89	if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
 90		printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n",
 91		       rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
 92		fflush(stdout);
 93		_exit(1);
 94	}
 95
 96	siglongjmp(jmpbuf, 1);
 97}
 98
 99static void test_syscall_fallthrough_to(unsigned long ip)
100{
101	void *new_address = (void *)(ip - 4096);
102	void *ret;
103
104	printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip);
105
106	ret = mremap((void *)current_test_page_addr, 4096, 4096,
107		     MREMAP_MAYMOVE | MREMAP_FIXED, new_address);
108	if (ret == MAP_FAILED) {
109		if (ip <= (1UL << 47) - PAGE_SIZE) {
110			err(1, "mremap to %p", new_address);
111		} else {
112			printf("[OK]\tmremap to %p failed\n", new_address);
113			return;
114		}
115	}
116
117	if (ret != new_address)
118		errx(1, "mremap malfunctioned: asked for %p but got %p\n",
119		     new_address, ret);
120
121	current_test_page_addr = new_address;
122	rip = ip;
123
124	if (sigsetjmp(jmpbuf, 1) == 0) {
125		asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid),
126			      [syscall_insn] "rm" (ip - 2));
127		errx(1, "[FAIL]\tSyscall trampoline returned");
128	}
129
130	printf("[OK]\tWe survived\n");
131}
132
133int main()
134{
135	/*
136	 * When the kernel returns from a slow-path syscall, it will
137	 * detect whether SYSRET is appropriate.  If it incorrectly
138	 * thinks that SYSRET is appropriate when RIP is noncanonical,
139	 * it'll crash on Intel CPUs.
140	 */
141	sethandler(SIGUSR1, sigusr1, 0);
142	for (int i = 47; i < 64; i++)
143		test_sigreturn_to(1UL<<i);
144
145	clearhandler(SIGUSR1);
146
147	sethandler(SIGSEGV, sigsegv_for_fallthrough, 0);
148
149	/* One extra test to check that we didn't screw up the mremap logic. */
150	test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE);
151
152	/* These are the interesting cases. */
153	for (int i = 47; i < 64; i++) {
154		test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE);
155		test_syscall_fallthrough_to(1UL<<i);
156	}
157
158	return 0;
159}
~/ Read-only file preview.