x86/fpu: Allow PKRU to be (once again) written by ptrace.
authorKyle Huey <me@kylehuey.com>
Tue, 15 Nov 2022 23:09:30 +0000 (15:09 -0800)
committerDave Hansen <dave.hansen@linux.intel.com>
Wed, 16 Nov 2022 23:03:53 +0000 (15:03 -0800)
Move KVM's PKRU handling code in fpu_copy_uabi_to_guest_fpstate() to
copy_uabi_to_xstate() so that it is shared with other APIs that write the
XSTATE such as PTRACE_SETREGSET with NT_X86_XSTATE.

This restores the pre-5.14 behavior of ptrace. The regression can be seen
by running gdb and executing `p $pkru`, `set $pkru = 42`, and `p $pkru`.
On affected kernels (5.14+) the write to the PKRU register (which gdb
performs through ptrace) is ignored.

[ dhansen: removed stable@ tag for now.  The ABI was broken for long
   enough that this is not urgent material.  Let's let it stew
   in tip for a few weeks before it's submitted to stable
   because there are so many ABIs potentially affected. ]

Fixes: e84ba47e313d ("x86/fpu: Hook up PKRU into ptrace()")
Signed-off-by: Kyle Huey <me@kylehuey.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://lore.kernel.org/all/20221115230932.7126-5-khuey%40kylehuey.com
arch/x86/kernel/fpu/core.c
arch/x86/kernel/fpu/xstate.c

index 5501576..46b935b 100644 (file)
@@ -391,8 +391,6 @@ int fpu_copy_uabi_to_guest_fpstate(struct fpu_guest *gfpu, const void *buf,
 {
        struct fpstate *kstate = gfpu->fpstate;
        const union fpregs_state *ustate = buf;
-       struct pkru_state *xpkru;
-       int ret;
 
        if (!cpu_feature_enabled(X86_FEATURE_XSAVE)) {
                if (ustate->xsave.header.xfeatures & ~XFEATURE_MASK_FPSSE)
@@ -406,16 +404,7 @@ int fpu_copy_uabi_to_guest_fpstate(struct fpu_guest *gfpu, const void *buf,
        if (ustate->xsave.header.xfeatures & ~xcr0)
                return -EINVAL;
 
-       ret = copy_uabi_from_kernel_to_xstate(kstate, ustate, vpkru);
-       if (ret)
-               return ret;
-
-       /* Retrieve PKRU if not in init state */
-       if (kstate->regs.xsave.header.xfeatures & XFEATURE_MASK_PKRU) {
-               xpkru = get_xsave_addr(&kstate->regs.xsave, XFEATURE_PKRU);
-               *vpkru = xpkru->pkru;
-       }
-       return 0;
+       return copy_uabi_from_kernel_to_xstate(kstate, ustate, vpkru);
 }
 EXPORT_SYMBOL_GPL(fpu_copy_uabi_to_guest_fpstate);
 #endif /* CONFIG_KVM */
index d657c8b..a8cf604 100644 (file)
@@ -1205,10 +1205,22 @@ static int copy_from_buffer(void *dst, unsigned int offset, unsigned int size,
  * @fpstate:   The fpstate buffer to copy to
  * @kbuf:      The UABI format buffer, if it comes from the kernel
  * @ubuf:      The UABI format buffer, if it comes from userspace
- * @pkru:      unused
+ * @pkru:      The location to write the PKRU value to
  *
  * Converts from the UABI format into the kernel internal hardware
  * dependent format.
+ *
+ * This function ultimately has three different callers with distinct PKRU
+ * behavior.
+ * 1.  When called from sigreturn the PKRU register will be restored from
+ *     @fpstate via an XRSTOR. Correctly copying the UABI format buffer to
+ *     @fpstate is sufficient to cover this case, but the caller will also
+ *     pass a pointer to the thread_struct's pkru field in @pkru and updating
+ *     it is harmless.
+ * 2.  When called from ptrace the PKRU register will be restored from the
+ *     thread_struct's pkru field. A pointer to that is passed in @pkru.
+ * 3.  When called from KVM the PKRU register will be restored from the vcpu's
+ *     pkru field. A pointer to that is passed in @pkru.
  */
 static int copy_uabi_to_xstate(struct fpstate *fpstate, const void *kbuf,
                               const void __user *ubuf, u32 *pkru)
@@ -1260,6 +1272,13 @@ static int copy_uabi_to_xstate(struct fpstate *fpstate, const void *kbuf,
                }
        }
 
+       if (hdr.xfeatures & XFEATURE_MASK_PKRU) {
+               struct pkru_state *xpkru;
+
+               xpkru = __raw_xsave_addr(xsave, XFEATURE_PKRU);
+               *pkru = xpkru->pkru;
+       }
+
        /*
         * The state that came in from userspace was user-state only.
         * Mask all the user states out of 'xfeatures':