Messages in this thread | | | Subject | Re: [patch V3 6/6] selftests/x86: Test signal frame XSTATE header corruption handling | From | David Edmondson <> | Date | Wed, 09 Jun 2021 09:38:30 +0100 |
| |
On Tuesday, 2021-06-08 at 16:36:23 +02, Thomas Gleixner wrote:
> From: Andy Lutomirski <luto@kernel.org> > > This is very heavily based on some code from Thomas Gleixner. On a system > without XSAVES, it triggers the WARN_ON(): > > Bad FPU state detected at copy_kernel_to_fpregs+0x2f/0x40, reinitializing FPU registers. > > Signed-off-by: Andy Lutomirski <luto@kernel.org> > Signed-off-by: Thomas Gleixner <tglx@linutronix.de> > --- > V2: main() -> main(void) - Borislav > --- > tools/testing/selftests/x86/Makefile | 3 > tools/testing/selftests/x86/corrupt_xstate_header.c | 114 ++++++++++++++++++++ > 2 files changed, 116 insertions(+), 1 deletion(-) > create mode 100644 tools/testing/selftests/x86/corrupt_xstate_header.c > > --- a/tools/testing/selftests/x86/Makefile > +++ b/tools/testing/selftests/x86/Makefile > @@ -17,7 +17,8 @@ TARGETS_C_BOTHBITS := single_step_syscal > TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ > test_FCMOV test_FCOMI test_FISTTP \ > vdso_restorer > -TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering > +TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \ > + corrupt_xstate_header > # Some selftests require 32bit support enabled also on 64bit systems > TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall > > --- /dev/null > +++ b/tools/testing/selftests/x86/corrupt_xstate_header.c > @@ -0,0 +1,114 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Corrupt the XSTATE header in a signal frame > + * > + * Based on analysis and a test case from Thomas Gleixner. > + */ > + > +#define _GNU_SOURCE > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <string.h> > +#include <sched.h> > +#include <signal.h> > +#include <err.h> > +#include <unistd.h> > +#include <stdint.h> > +#include <sys/wait.h> > + > +static inline void __cpuid(unsigned int *eax, unsigned int *ebx, > + unsigned int *ecx, unsigned int *edx) > +{ > + asm volatile( > + "cpuid;" > + : "=a" (*eax), > + "=b" (*ebx), > + "=c" (*ecx), > + "=d" (*edx) > + : "0" (*eax), "2" (*ecx)); > +} > + > +static inline int xsave_enabled(void) > +{ > + unsigned int eax, ebx, ecx, edx; > + > + eax = 0x1; > + ecx = 0x0; > + __cpuid(&eax, &ebx, &ecx, &edx); > + > + /* Is CR4.OSXSAVE enabled ? */ > + return ecx & (1U << 27); > +} > + > +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 void sigusr1(int sig, siginfo_t *info, void *uc_void) > +{ > + ucontext_t *uc = uc_void; > + uint8_t *fpstate = (uint8_t *)uc->uc_mcontext.fpregs; > + uint64_t *xfeatures = (uint64_t *)(fpstate + 512); > + > + printf("\tWreckage XSTATE header\n");
"Wreckage" is an odd word to use here, as it usually refers to the objects that are the result of a wreck. Maybe "wreck" or (to match the test name) "corrupt"?
> + /* Wreckage the first reserved byte in the header */ > + *(xfeatures + 2) = 0xfffffff;
This trashes more than a byte, which doesn't seem significant, but it would be good to have the comment and code match.
> +} > + > +static void sigsegv(int sig, siginfo_t *info, void *uc_void) > +{ > + printf("\tGot SIGSEGV\n"); > +} > + > +int main(void) > +{ > + cpu_set_t set; > + > + sethandler(SIGUSR1, sigusr1, 0); > + sethandler(SIGSEGV, sigsegv, 0); > + > + if (!xsave_enabled()) { > + printf("[SKIP] CR4.OSXSAVE disabled.\n"); > + return 0; > + } > + > + CPU_ZERO(&set); > + CPU_SET(0, &set); > + > + /* > + * Enforce that the child runs on the same CPU > + * which in turn forces a schedule. > + */ > + sched_setaffinity(getpid(), sizeof(set), &set); > + > + printf("[RUN]\tSend ourselves a signal\n"); > + raise(SIGUSR1); > + > + printf("[OK]\tBack from the signal. Now schedule.\n"); > + pid_t child = fork(); > + if (child < 0) > + err(1, "fork"); > + if (child == 0) > + return 0; > + if (child) > + waitpid(child, NULL, 0); > + printf("[OK]\tBack in the main thread.\n"); > + > + /* > + * We could try to confirm that extended state is still preserved > + * when we schedule. For now, the only indication of failure is > + * a warning in the kernel logs. > + */ > + > + return 0; > +}
dme. -- It's alright, we told you what to dream.
| |