x86/pkeys: Update PKRU to enable all pkeys before XSAVE
authorAruna Ramakrishna <aruna.ramakrishna@oracle.com>
Fri, 2 Aug 2024 06:13:16 +0000 (06:13 +0000)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 2 Aug 2024 12:12:21 +0000 (14:12 +0200)
If the alternate signal stack is protected by a different PKEY than the
current execution stack, copying XSAVE data to the sigaltstack will fail
if its PKEY is not enabled in the PKRU register.

It's unknown which pkey was used by the application for the altstack, so
enable all PKEYS before XSAVE.

But this updated PKRU value is also pushed onto the sigframe, which
means the register value restored from sigcontext will be different from
the user-defined one, which is incorrect.

Fix that by overwriting the PKRU value on the sigframe with the original,
user-defined PKRU.

Signed-off-by: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240802061318.2140081-4-aruna.ramakrishna@oracle.com
arch/x86/kernel/fpu/signal.c
arch/x86/kernel/signal.c

index 931c5469d7f3d18c0b721728bce479f85d24082c..1065ab995305cd14f4d9d46b4fc52f22d00cd19a 100644 (file)
@@ -168,8 +168,15 @@ static inline bool save_xstate_epilog(void __user *buf, int ia32_frame,
 
 static inline int copy_fpregs_to_sigframe(struct xregs_state __user *buf, u32 pkru)
 {
-       if (use_xsave())
-               return xsave_to_user_sigframe(buf);
+       int err = 0;
+
+       if (use_xsave()) {
+               err = xsave_to_user_sigframe(buf);
+               if (!err)
+                       err = update_pkru_in_sigframe(buf, pkru);
+               return err;
+       }
+
        if (use_fxsr())
                return fxsave_to_user_sigframe((struct fxregs_state __user *) buf);
        else
index 9dc77ad03a0e98fa69261e1246885185a0e5e043..5f441039b5725fd1a23416a686f68a667e0a8774 100644 (file)
@@ -102,7 +102,7 @@ get_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size,
        unsigned long math_size = 0;
        unsigned long sp = regs->sp;
        unsigned long buf_fx = 0;
-       u32 pkru = read_pkru();
+       u32 pkru;
 
        /* redzone */
        if (!ia32_frame)
@@ -157,9 +157,17 @@ get_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size,
                return (void __user *)-1L;
        }
 
+       /* Update PKRU to enable access to the alternate signal stack. */
+       pkru = sig_prepare_pkru();
        /* save i387 and extended state */
-       if (!copy_fpstate_to_sigframe(*fpstate, (void __user *)buf_fx, math_size, pkru))
+       if (!copy_fpstate_to_sigframe(*fpstate, (void __user *)buf_fx, math_size, pkru)) {
+               /*
+                * Restore PKRU to the original, user-defined value; disable
+                * extra pkeys enabled for the alternate signal stack, if any.
+                */
+               write_pkru(pkru);
                return (void __user *)-1L;
+       }
 
        return (void __user *)sp;
 }