brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 48ab065 Raw
236 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args4 * Copyright (c) 2015 Andrew Lutomirski5 */6 7#define _GNU_SOURCE8 9#include <stdlib.h>10#include <stdio.h>11#include <string.h>12#include <sys/signal.h>13#include <sys/ucontext.h>14#include <err.h>15#include <setjmp.h>16#include <errno.h>17 18#include "helpers.h"19 20static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),21		       int flags)22{23	struct sigaction sa;24	memset(&sa, 0, sizeof(sa));25	sa.sa_sigaction = handler;26	sa.sa_flags = SA_SIGINFO | flags;27	sigemptyset(&sa.sa_mask);28	if (sigaction(sig, &sa, 0))29		err(1, "sigaction");30}31 32static sigjmp_buf jmpbuf;33 34static volatile sig_atomic_t n_errs;35 36#ifdef __x86_64__37#define REG_AX REG_RAX38#define REG_IP REG_RIP39#else40#define REG_AX REG_EAX41#define REG_IP REG_EIP42#endif43 44static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)45{46	ucontext_t *ctx = (ucontext_t*)ctx_void;47	long ax = (long)ctx->uc_mcontext.gregs[REG_AX];48 49	if (ax != -EFAULT && ax != -ENOSYS) {50		printf("[FAIL]\tAX had the wrong value: 0x%lx\n",51		       (unsigned long)ax);52		printf("\tIP = 0x%lx\n", (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);53		n_errs++;54	} else {55		printf("[OK]\tSeems okay\n");56	}57 58	siglongjmp(jmpbuf, 1);59}60 61static volatile sig_atomic_t sigtrap_consecutive_syscalls;62 63static void sigtrap(int sig, siginfo_t *info, void *ctx_void)64{65	/*66	 * KVM has some bugs that can cause us to stop making progress.67	 * detect them and complain, but don't infinite loop or fail the68	 * test.69	 */70 71	ucontext_t *ctx = (ucontext_t*)ctx_void;72	unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];73 74	if (*ip == 0x340f || *ip == 0x050f) {75		/* The trap was on SYSCALL or SYSENTER */76		sigtrap_consecutive_syscalls++;77		if (sigtrap_consecutive_syscalls > 3) {78			printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");79			siglongjmp(jmpbuf, 1);80		}81	} else {82		sigtrap_consecutive_syscalls = 0;83	}84}85 86static void sigill(int sig, siginfo_t *info, void *ctx_void)87{88	ucontext_t *ctx = (ucontext_t*)ctx_void;89	unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];90 91	if (*ip == 0x0b0f) {92		/* one of the ud2 instructions faulted */93		printf("[OK]\tSYSCALL returned normally\n");94	} else {95		printf("[SKIP]\tIllegal instruction\n");96	}97	siglongjmp(jmpbuf, 1);98}99 100int main()101{102	stack_t stack = {103		/* Our sigaltstack scratch space. */104		.ss_sp = malloc(sizeof(char) * SIGSTKSZ),105		.ss_size = SIGSTKSZ,106	};107	if (sigaltstack(&stack, NULL) != 0)108		err(1, "sigaltstack");109 110	sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);111	/*112	 * The actual exception can vary.  On Atom CPUs, we get #SS113	 * instead of #PF when the vDSO fails to access the stack when114	 * ESP is too close to 2^32, and #SS causes SIGBUS.115	 */116	sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);117	sethandler(SIGILL, sigill, SA_ONSTACK);118 119	/*120	 * Exercise another nasty special case.  The 32-bit SYSCALL121	 * and SYSENTER instructions (even in compat mode) each122	 * clobber one register.  A Linux system call has a syscall123	 * number and six arguments, and the user stack pointer124	 * needs to live in some register on return.  That means125	 * that we need eight registers, but SYSCALL and SYSENTER126	 * only preserve seven registers.  As a result, one argument127	 * ends up on the stack.  The stack is user memory, which128	 * means that the kernel can fail to read it.129	 *130	 * The 32-bit fast system calls don't have a defined ABI:131	 * we're supposed to invoke them through the vDSO.  So we'll132	 * fudge it: we set all regs to invalid pointer values and133	 * invoke the entry instruction.  The return will fail no134	 * matter what, and we completely lose our program state,135	 * but we can fix it up with a signal handler.136	 */137 138	printf("[RUN]\tSYSENTER with invalid state\n");139	if (sigsetjmp(jmpbuf, 1) == 0) {140		asm volatile (141			"movl $-1, %%eax\n\t"142			"movl $-1, %%ebx\n\t"143			"movl $-1, %%ecx\n\t"144			"movl $-1, %%edx\n\t"145			"movl $-1, %%esi\n\t"146			"movl $-1, %%edi\n\t"147			"movl $-1, %%ebp\n\t"148			"movl $-1, %%esp\n\t"149			"sysenter"150			: : : "memory", "flags");151	}152 153	printf("[RUN]\tSYSCALL with invalid state\n");154	if (sigsetjmp(jmpbuf, 1) == 0) {155		asm volatile (156			"movl $-1, %%eax\n\t"157			"movl $-1, %%ebx\n\t"158			"movl $-1, %%ecx\n\t"159			"movl $-1, %%edx\n\t"160			"movl $-1, %%esi\n\t"161			"movl $-1, %%edi\n\t"162			"movl $-1, %%ebp\n\t"163			"movl $-1, %%esp\n\t"164			"syscall\n\t"165			"ud2"		/* make sure we recover cleanly */166			: : : "memory", "flags");167	}168 169	printf("[RUN]\tSYSENTER with TF and invalid state\n");170	sethandler(SIGTRAP, sigtrap, SA_ONSTACK);171 172	if (sigsetjmp(jmpbuf, 1) == 0) {173		sigtrap_consecutive_syscalls = 0;174		set_eflags(get_eflags() | X86_EFLAGS_TF);175		asm volatile (176			"movl $-1, %%eax\n\t"177			"movl $-1, %%ebx\n\t"178			"movl $-1, %%ecx\n\t"179			"movl $-1, %%edx\n\t"180			"movl $-1, %%esi\n\t"181			"movl $-1, %%edi\n\t"182			"movl $-1, %%ebp\n\t"183			"movl $-1, %%esp\n\t"184			"sysenter"185			: : : "memory", "flags");186	}187	set_eflags(get_eflags() & ~X86_EFLAGS_TF);188 189	printf("[RUN]\tSYSCALL with TF and invalid state\n");190	if (sigsetjmp(jmpbuf, 1) == 0) {191		sigtrap_consecutive_syscalls = 0;192		set_eflags(get_eflags() | X86_EFLAGS_TF);193		asm volatile (194			"movl $-1, %%eax\n\t"195			"movl $-1, %%ebx\n\t"196			"movl $-1, %%ecx\n\t"197			"movl $-1, %%edx\n\t"198			"movl $-1, %%esi\n\t"199			"movl $-1, %%edi\n\t"200			"movl $-1, %%ebp\n\t"201			"movl $-1, %%esp\n\t"202			"syscall\n\t"203			"ud2"		/* make sure we recover cleanly */204			: : : "memory", "flags");205	}206	set_eflags(get_eflags() & ~X86_EFLAGS_TF);207 208#ifdef __x86_64__209	printf("[RUN]\tSYSENTER with TF, invalid state, and GSBASE < 0\n");210 211	if (sigsetjmp(jmpbuf, 1) == 0) {212		sigtrap_consecutive_syscalls = 0;213 214		asm volatile ("wrgsbase %%rax\n\t"215			      :: "a" (0xffffffffffff0000UL));216 217		set_eflags(get_eflags() | X86_EFLAGS_TF);218		asm volatile (219			"movl $-1, %%eax\n\t"220			"movl $-1, %%ebx\n\t"221			"movl $-1, %%ecx\n\t"222			"movl $-1, %%edx\n\t"223			"movl $-1, %%esi\n\t"224			"movl $-1, %%edi\n\t"225			"movl $-1, %%ebp\n\t"226			"movl $-1, %%esp\n\t"227			"sysenter"228			: : : "memory", "flags");229	}230	set_eflags(get_eflags() & ~X86_EFLAGS_TF);231#endif232 233	free(stack.ss_sp);234	return 0;235}236