diff --git a/compel/arch/x86/src/lib/cpu.c b/compel/arch/x86/src/lib/cpu.c index 617512167..c96f01353 100644 --- a/compel/arch/x86/src/lib/cpu.c +++ b/compel/arch/x86/src/lib/cpu.c @@ -125,7 +125,7 @@ static int compel_fpuid(compel_cpuinfo_t *c) c->xfeatures_mask &= ~(1 << i); } - c->xfeatures_mask &= XCNTXT_MASK; + c->xfeatures_mask &= XFEATURE_MASK_USER; c->xfeatures_mask &= ~XFEATURE_MASK_SUPERVISOR; /* diff --git a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h index 4ff531fb9..8985ad7f6 100644 --- a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h +++ b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h @@ -76,7 +76,7 @@ enum xfeature { #define XFEATURE_MASK_SUPERVISOR (XFEATURE_MASK_PT | XFEATURE_HDC) /* All currently supported features */ -#define XCNTXT_MASK \ +#define XFEATURE_MASK_USER \ (XFEATURE_MASK_FP | XFEATURE_MASK_SSE | \ XFEATURE_MASK_YMM | XFEATURE_MASK_OPMASK | \ XFEATURE_MASK_ZMM_Hi256 | XFEATURE_MASK_Hi16_ZMM | \ @@ -232,7 +232,7 @@ struct pkru_state { * can vary quite a bit between CPUs. * * - * One page should be enough for the whole xsave state. + * One page should be enough for the whole xsave state ;-) */ #define EXTENDED_STATE_AREA_SIZE (4096 - sizeof(struct i387_fxsave_struct) - sizeof(struct xsave_hdr_struct)) diff --git a/compel/arch/x86/src/lib/infect.c b/compel/arch/x86/src/lib/infect.c index 9c4abb60c..401001942 100644 --- a/compel/arch/x86/src/lib/infect.c +++ b/compel/arch/x86/src/lib/infect.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -258,6 +260,80 @@ static int get_task_fpregs(pid_t pid, user_fpregs_struct_t *xsave) return 0; } +/* See arch/x86/kernel/fpu/xstate.c */ +static void validate_random_xstate(struct xsave_struct *xsave) +{ + struct xsave_hdr_struct *hdr = &xsave->xsave_hdr; + unsigned int i; + + /* No unknown or supervisor features may be set */ + hdr->xstate_bv &= XFEATURE_MASK_USER; + hdr->xstate_bv &= ~XFEATURE_MASK_SUPERVISOR; + + for (i = 0; i < XFEATURE_MAX; i++) { + if (!compel_fpu_has_feature(i)) + hdr->xstate_bv &= ~(1 << i); + } + + /* Userspace must use the uncompacted format */ + hdr->xcomp_bv = 0; + + /* + * If 'reserved' is shrunken to add a new field, make sure to validate + * that new field here! + */ + BUILD_BUG_ON(sizeof(hdr->reserved) != 48); + + /* No reserved bits may be set */ + memset(&hdr->reserved, 0, sizeof(hdr->reserved)); +} + +/* + * TODO: Put fault-injection under CONFIG_* and move + * extended regset corruption to generic code + */ +static int corrupt_extregs(pid_t pid) +{ + bool use_xsave = compel_cpu_has_feature(X86_FEATURE_OSXSAVE); + user_fpregs_struct_t ext_regs; + int *rand_to = (int *)&ext_regs; + unsigned int seed; + size_t i; + + seed = time(NULL); + for (i = 0; i < sizeof(ext_regs) / sizeof(int); i++) + *rand_to++ = rand_r(&seed); + + /* + * Error log-level as: + * - not intended to be used outside of testing, + * - zdtm.py will grep it auto-magically from logs + * (and the seed will be known from an automatical testing) + */ + pr_err("Corrupting %s for %d, seed %u\n", + use_xsave ? "xsave" : "fpuregs", pid, seed); + + if (!use_xsave) { + if (ptrace(PTRACE_SETFPREGS, pid, NULL, &ext_regs)) { + pr_perror("Can't set FPU registers for %d", pid); + return -1; + } + } else { + struct iovec iov; + + validate_random_xstate((void *)&ext_regs); + + iov.iov_base = &ext_regs; + iov.iov_len = sizeof(ext_regs); + + if (ptrace(PTRACE_SETREGSET, pid, (unsigned int)NT_X86_XSTATE, &iov) < 0) { + pr_perror("Can't set xstate for %d", pid); + return -1; + } + } + return 0; +} + int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, void *arg, unsigned long flags) { @@ -309,6 +385,9 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, ret = get_task_xsave(pid, &xsave); } + if (!ret && unlikely(flags & INFECT_CORRUPT_EXTREGS)) + ret = corrupt_extregs(pid); + if (ret) goto err; diff --git a/compel/include/uapi/infect.h b/compel/include/uapi/infect.h index fa326eafe..6cf3daf46 100644 --- a/compel/include/uapi/infect.h +++ b/compel/include/uapi/infect.h @@ -136,6 +136,8 @@ extern struct infect_ctx *compel_infect_ctx(struct parasite_ctl *); #define INFECT_COMPATIBLE (1UL << 3) /* Workaround for ptrace bug on Skylake CPUs with kernels older than v4.14 */ #define INFECT_X86_PTRACE_MXCSR_BUG (1UL << 4) +/* After infecting - corrupt extended registers (fault-injection) */ +#define INFECT_CORRUPT_EXTREGS (1UL << 5) /* * There are several ways to describe a blob to compel diff --git a/criu/include/fault-injection.h b/criu/include/fault-injection.h index 31fe16178..0fd9b512d 100644 --- a/criu/include/fault-injection.h +++ b/criu/include/fault-injection.h @@ -18,6 +18,7 @@ enum faults { FI_PARTIAL_PAGES = 131, FI_HUGE_ANON_SHMEM_ID = 132, FI_CANNOT_MAP_VDSO = 133, + FI_CORRUPT_EXTREGS = 134, FI_MAX, }; diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c index a961cb8ae..04364e5bf 100644 --- a/criu/parasite-syscall.c +++ b/criu/parasite-syscall.c @@ -575,6 +575,8 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, ictx->flags |= INFECT_COMPATIBLE; if (kdat.x86_has_ptrace_fpu_xsave_bug) ictx->flags |= INFECT_X86_PTRACE_MXCSR_BUG; + if (fault_injected(FI_CORRUPT_EXTREGS)) + ictx->flags |= INFECT_CORRUPT_EXTREGS; ictx->log_fd = log_get_fd();