[patch 37/41] x86/fpu: Hook up PKRU into ptrace()

From: Thomas Gleixner
Date: Fri Jun 11 2021 - 12:46:09 EST


From: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>

One nice thing about having PKRU be XSAVE-managed is that it gets naturally
exposed into the XSAVE-using ABIs. Now that XSAVE will not be used to
manage PKRU, these ABIs need to be manually enabled to deal with PKRU.

ptrace() uses copy_uabi_xstate_to_kernel() to collect the tracee's
XSTATE. As PKRU is not in the task's XSTATE buffer, use task->thread.pkru
for filling in up the ptrace buffer.

Signed-off-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---

V4: Picked up from Dave's PKRU series, adopted to rename and dropped the
copy_user_to_fpregs_zeroing() part because task->thread.pkru is only
valid when the task is scheduled out. For current the authoritative
source is the hardware.

---
arch/x86/include/asm/fpu/xstate.h | 2 -
arch/x86/kernel/fpu/regset.c | 12 ++++-------
arch/x86/kernel/fpu/xstate.c | 40 ++++++++++++++++++++++++++------------
3 files changed, 34 insertions(+), 20 deletions(-)

--- a/arch/x86/include/asm/fpu/xstate.h
+++ b/arch/x86/include/asm/fpu/xstate.h
@@ -128,7 +128,7 @@ void *get_xsave_addr(struct xregs_state
int using_compacted_format(void);
int xfeature_size(int xfeature_nr);
struct membuf;
-void copy_uabi_xstate_to_membuf(struct membuf to, struct xregs_state *xsave);
+void copy_uabi_xstate_to_membuf(struct membuf to, struct task_struct *target);
int copy_uabi_from_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
int copy_sigframe_from_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);

--- a/arch/x86/kernel/fpu/regset.c
+++ b/arch/x86/kernel/fpu/regset.c
@@ -109,22 +109,20 @@ int xfpregs_set(struct task_struct *targ
}

int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
- struct membuf to)
+ struct membuf to)
{
- struct fpu *fpu = &target->thread.fpu;
-
if (!boot_cpu_has(X86_FEATURE_XSAVE))
return -ENODEV;

- fpu__prepare_read(fpu);
+ fpu__prepare_read(&target->thread.fpu);

- copy_uabi_xstate_to_membuf(to, &fpu->state.xsave);
+ copy_uabi_xstate_to_membuf(to, target);
return 0;
}

int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
struct fpu *fpu = &target->thread.fpu;
struct xregs_state *tmpbuf = NULL;
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -968,12 +968,13 @@ static void copy_part(struct membuf *to,
* It supports partial copy but pos always starts from zero. This is called
* from xstateregs_get() and there we check the CPU has XSAVE.
*/
-void copy_uabi_xstate_to_membuf(struct membuf to, struct xregs_state *xsave)
+void copy_uabi_xstate_to_membuf(struct membuf to, struct task_struct *target)
{
- struct xstate_header header;
const unsigned off_mxcsr = offsetof(struct fxregs_state, mxcsr);
- unsigned size = to.left;
- unsigned last = 0;
+ struct xregs_state *xsave = &target->thread.fpu.state.xsave;
+ struct xstate_header header;
+ unsigned int size = to.left;
+ unsigned int last = 0;
int i;

/*
@@ -983,6 +984,13 @@ void copy_uabi_xstate_to_membuf(struct m
header.xfeatures = xsave->header.xfeatures;
header.xfeatures &= xfeatures_mask_uabi();

+ /*
+ * PKRU is not XSTATE managed. If enabled set the xfeature bit as
+ * user space expects it to be part of the XSTATE buffer.
+ */
+ if (cpu_feature_enabled(X86_FEATURE_OSPKE))
+ header.xfeatures |= XFEATURE_MASK_PKRU;
+
if (header.xfeatures & XFEATURE_MASK_FP)
copy_part(&to, &last, 0, off_mxcsr, &xsave->i387);
if (header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM))
@@ -1006,16 +1014,24 @@ void copy_uabi_xstate_to_membuf(struct m
sizeof(header), &header);

for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
- /*
- * Copy only in-use xstates:
- */
- if ((header.xfeatures >> i) & 1) {
- void *src = __raw_xsave_addr(xsave, i);
+ struct pkru_state pkru = {0};
+ void *src;

- copy_part(&to, &last, xstate_offsets[i],
- xstate_sizes[i], src);
- }
+ /* Copy only in-use xstates: */
+ if (!((header.xfeatures >> i) & 1))
+ continue;

+ if (i == XFEATURE_PKRU) {
+ /*
+ * PKRU is not kept in the thread's XSAVE buffer.
+ * Fill this part from the per-thread storage.
+ */
+ pkru.pkru = target->thread.pkru;
+ src = &pkru;
+ } else {
+ src = __raw_xsave_addr(xsave, i);
+ }
+ copy_part(&to, &last, xstate_offsets[i], xstate_sizes[i], src);
}
fill_gap(&to, &last, size);
}