forked from Minki/linux
a051b2e56f
A glibc 2.34 feature adds support for variable MINSIGSTKSZ and SIGSTKSZ. When _DYNAMIC_STACK_SIZE_SOURCE or _GNU_SOURCE are defined, MINSIGSTKSZ and SIGSTKSZ are no longer constant on Linux. glibc 2.34 flags code paths assuming MINSIGSTKSZ or SIGSTKSZ are constant. Fix these error in x86 test. Feature description and build error: NEWS for version 2.34 ===================== Major new features: * Add _SC_MINSIGSTKSZ and _SC_SIGSTKSZ. When _DYNAMIC_STACK_SIZE_SOURCE or _GNU_SOURCE are defined, MINSIGSTKSZ and SIGSTKSZ are no longer constant on Linux. MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ) and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ). This supports dynamic sized register sets for modern architectural features like Arm SVE. ===================== If _SC_SIGSTKSZ_SOURCE or _GNU_SOURCE are defined, MINSIGSTKSZ and SIGSTKSZ are redefined as: /* Default stack size for a signal handler: sysconf (SC_SIGSTKSZ). */ # undef SIGSTKSZ # define SIGSTKSZ sysconf (_SC_SIGSTKSZ) /* Minimum stack size for a signal handler: SIGSTKSZ. */ # undef MINSIGSTKSZ # define MINSIGSTKSZ SIGSTKSZ Compilation will fail if the source assumes constant MINSIGSTKSZ or SIGSTKSZ. Build error with the GNU C Library 2.34: DEBUG: | sigreturn.c:150:13: error: variably modified 'altstack_data' at file scope | sigreturn.c:150:13: error: variably modified 'altstack_data' at file scope DEBUG: | 150 | static char altstack_data[SIGSTKSZ]; | 150 | static char altstack_data[SIGSTKSZ]; DEBUG: | | ^~~~~~~~~~~~~ DEBUG: | single_step_syscall.c:60:22: error: variably modified 'altstack_data' at file scope DEBUG: | 60 | static unsigned char altstack_data[SIGSTKSZ]; DEBUG: | | ^~~~~~~~~~~~~ Fixed commit log to improve formatting and clarity: Shuah Khan <skhan@linuxfoundation.org> Link: https://sourceware.org/pipermail/libc-alpha/2021-January/121996.html Link: https://sourceware.org/pipermail/libc-alpha/2021-August/129718.html Suggested-by: Jianwei Hu <jianwei.hu@windriver.com> Signed-off-by: Jun Miao <jun.miao@windriver.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
237 lines
6.1 KiB
C
237 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
|
|
* Copyright (c) 2015 Andrew Lutomirski
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/ucontext.h>
|
|
#include <err.h>
|
|
#include <setjmp.h>
|
|
#include <errno.h>
|
|
|
|
#include "helpers.h"
|
|
|
|
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
|
|
int flags)
|
|
{
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_sigaction = handler;
|
|
sa.sa_flags = SA_SIGINFO | flags;
|
|
sigemptyset(&sa.sa_mask);
|
|
if (sigaction(sig, &sa, 0))
|
|
err(1, "sigaction");
|
|
}
|
|
|
|
static volatile sig_atomic_t sig_traps;
|
|
static sigjmp_buf jmpbuf;
|
|
|
|
static volatile sig_atomic_t n_errs;
|
|
|
|
#ifdef __x86_64__
|
|
#define REG_AX REG_RAX
|
|
#define REG_IP REG_RIP
|
|
#else
|
|
#define REG_AX REG_EAX
|
|
#define REG_IP REG_EIP
|
|
#endif
|
|
|
|
static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)
|
|
{
|
|
ucontext_t *ctx = (ucontext_t*)ctx_void;
|
|
long ax = (long)ctx->uc_mcontext.gregs[REG_AX];
|
|
|
|
if (ax != -EFAULT && ax != -ENOSYS) {
|
|
printf("[FAIL]\tAX had the wrong value: 0x%lx\n",
|
|
(unsigned long)ax);
|
|
printf("\tIP = 0x%lx\n", (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
|
|
n_errs++;
|
|
} else {
|
|
printf("[OK]\tSeems okay\n");
|
|
}
|
|
|
|
siglongjmp(jmpbuf, 1);
|
|
}
|
|
|
|
static volatile sig_atomic_t sigtrap_consecutive_syscalls;
|
|
|
|
static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
|
|
{
|
|
/*
|
|
* KVM has some bugs that can cause us to stop making progress.
|
|
* detect them and complain, but don't infinite loop or fail the
|
|
* test.
|
|
*/
|
|
|
|
ucontext_t *ctx = (ucontext_t*)ctx_void;
|
|
unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
|
|
|
|
if (*ip == 0x340f || *ip == 0x050f) {
|
|
/* The trap was on SYSCALL or SYSENTER */
|
|
sigtrap_consecutive_syscalls++;
|
|
if (sigtrap_consecutive_syscalls > 3) {
|
|
printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");
|
|
siglongjmp(jmpbuf, 1);
|
|
}
|
|
} else {
|
|
sigtrap_consecutive_syscalls = 0;
|
|
}
|
|
}
|
|
|
|
static void sigill(int sig, siginfo_t *info, void *ctx_void)
|
|
{
|
|
ucontext_t *ctx = (ucontext_t*)ctx_void;
|
|
unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
|
|
|
|
if (*ip == 0x0b0f) {
|
|
/* one of the ud2 instructions faulted */
|
|
printf("[OK]\tSYSCALL returned normally\n");
|
|
} else {
|
|
printf("[SKIP]\tIllegal instruction\n");
|
|
}
|
|
siglongjmp(jmpbuf, 1);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
stack_t stack = {
|
|
/* Our sigaltstack scratch space. */
|
|
.ss_sp = malloc(sizeof(char) * SIGSTKSZ),
|
|
.ss_size = SIGSTKSZ,
|
|
};
|
|
if (sigaltstack(&stack, NULL) != 0)
|
|
err(1, "sigaltstack");
|
|
|
|
sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);
|
|
/*
|
|
* The actual exception can vary. On Atom CPUs, we get #SS
|
|
* instead of #PF when the vDSO fails to access the stack when
|
|
* ESP is too close to 2^32, and #SS causes SIGBUS.
|
|
*/
|
|
sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);
|
|
sethandler(SIGILL, sigill, SA_ONSTACK);
|
|
|
|
/*
|
|
* Exercise another nasty special case. The 32-bit SYSCALL
|
|
* and SYSENTER instructions (even in compat mode) each
|
|
* clobber one register. A Linux system call has a syscall
|
|
* number and six arguments, and the user stack pointer
|
|
* needs to live in some register on return. That means
|
|
* that we need eight registers, but SYSCALL and SYSENTER
|
|
* only preserve seven registers. As a result, one argument
|
|
* ends up on the stack. The stack is user memory, which
|
|
* means that the kernel can fail to read it.
|
|
*
|
|
* The 32-bit fast system calls don't have a defined ABI:
|
|
* we're supposed to invoke them through the vDSO. So we'll
|
|
* fudge it: we set all regs to invalid pointer values and
|
|
* invoke the entry instruction. The return will fail no
|
|
* matter what, and we completely lose our program state,
|
|
* but we can fix it up with a signal handler.
|
|
*/
|
|
|
|
printf("[RUN]\tSYSENTER with invalid state\n");
|
|
if (sigsetjmp(jmpbuf, 1) == 0) {
|
|
asm volatile (
|
|
"movl $-1, %%eax\n\t"
|
|
"movl $-1, %%ebx\n\t"
|
|
"movl $-1, %%ecx\n\t"
|
|
"movl $-1, %%edx\n\t"
|
|
"movl $-1, %%esi\n\t"
|
|
"movl $-1, %%edi\n\t"
|
|
"movl $-1, %%ebp\n\t"
|
|
"movl $-1, %%esp\n\t"
|
|
"sysenter"
|
|
: : : "memory", "flags");
|
|
}
|
|
|
|
printf("[RUN]\tSYSCALL with invalid state\n");
|
|
if (sigsetjmp(jmpbuf, 1) == 0) {
|
|
asm volatile (
|
|
"movl $-1, %%eax\n\t"
|
|
"movl $-1, %%ebx\n\t"
|
|
"movl $-1, %%ecx\n\t"
|
|
"movl $-1, %%edx\n\t"
|
|
"movl $-1, %%esi\n\t"
|
|
"movl $-1, %%edi\n\t"
|
|
"movl $-1, %%ebp\n\t"
|
|
"movl $-1, %%esp\n\t"
|
|
"syscall\n\t"
|
|
"ud2" /* make sure we recover cleanly */
|
|
: : : "memory", "flags");
|
|
}
|
|
|
|
printf("[RUN]\tSYSENTER with TF and invalid state\n");
|
|
sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
|
|
|
|
if (sigsetjmp(jmpbuf, 1) == 0) {
|
|
sigtrap_consecutive_syscalls = 0;
|
|
set_eflags(get_eflags() | X86_EFLAGS_TF);
|
|
asm volatile (
|
|
"movl $-1, %%eax\n\t"
|
|
"movl $-1, %%ebx\n\t"
|
|
"movl $-1, %%ecx\n\t"
|
|
"movl $-1, %%edx\n\t"
|
|
"movl $-1, %%esi\n\t"
|
|
"movl $-1, %%edi\n\t"
|
|
"movl $-1, %%ebp\n\t"
|
|
"movl $-1, %%esp\n\t"
|
|
"sysenter"
|
|
: : : "memory", "flags");
|
|
}
|
|
set_eflags(get_eflags() & ~X86_EFLAGS_TF);
|
|
|
|
printf("[RUN]\tSYSCALL with TF and invalid state\n");
|
|
if (sigsetjmp(jmpbuf, 1) == 0) {
|
|
sigtrap_consecutive_syscalls = 0;
|
|
set_eflags(get_eflags() | X86_EFLAGS_TF);
|
|
asm volatile (
|
|
"movl $-1, %%eax\n\t"
|
|
"movl $-1, %%ebx\n\t"
|
|
"movl $-1, %%ecx\n\t"
|
|
"movl $-1, %%edx\n\t"
|
|
"movl $-1, %%esi\n\t"
|
|
"movl $-1, %%edi\n\t"
|
|
"movl $-1, %%ebp\n\t"
|
|
"movl $-1, %%esp\n\t"
|
|
"syscall\n\t"
|
|
"ud2" /* make sure we recover cleanly */
|
|
: : : "memory", "flags");
|
|
}
|
|
set_eflags(get_eflags() & ~X86_EFLAGS_TF);
|
|
|
|
#ifdef __x86_64__
|
|
printf("[RUN]\tSYSENTER with TF, invalid state, and GSBASE < 0\n");
|
|
|
|
if (sigsetjmp(jmpbuf, 1) == 0) {
|
|
sigtrap_consecutive_syscalls = 0;
|
|
|
|
asm volatile ("wrgsbase %%rax\n\t"
|
|
:: "a" (0xffffffffffff0000UL));
|
|
|
|
set_eflags(get_eflags() | X86_EFLAGS_TF);
|
|
asm volatile (
|
|
"movl $-1, %%eax\n\t"
|
|
"movl $-1, %%ebx\n\t"
|
|
"movl $-1, %%ecx\n\t"
|
|
"movl $-1, %%edx\n\t"
|
|
"movl $-1, %%esi\n\t"
|
|
"movl $-1, %%edi\n\t"
|
|
"movl $-1, %%ebp\n\t"
|
|
"movl $-1, %%esp\n\t"
|
|
"sysenter"
|
|
: : : "memory", "flags");
|
|
}
|
|
set_eflags(get_eflags() & ~X86_EFLAGS_TF);
|
|
#endif
|
|
|
|
free(stack.ss_sp);
|
|
return 0;
|
|
}
|