[PATCH v6 11/26] x86/fpu/xstate: Update the XSTATE context copy function to support dynamic states
From: Chang S. Bae
Date: Wed Jun 30 2021 - 02:08:38 EST
ptrace() and signal return paths use XSTATE context copy functions. They
allow callers to read (or write) XSTATE values in the target's buffer. With
dynamic user states, a component's position in the buffer may vary and the
init fpstate is not always large enough to cover all the states.
Adjust the helpers to find a component's offset correctly. Also, update the
copy loop in the ptrace read path to support dynamic states.
Signed-off-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
Reviewed-by: Len Brown <len.brown@xxxxxxxxx>
Cc: x86@xxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
---
Changes from v5:
* Updated to ensure xstate_bv aligned with the target.
* Rewrote the xstate copy loop, for the ptrace() read path, in an open
code.
* Adjusted the changelog.
Changes from v3:
* Cleaned up the code change with more comments.
* Removed 'no functional change' in the changelog. (Borislav Petkov)
Changes from v2:
* Updated the changelog with task->fpu removed. (Borislav Petkov)
---
arch/x86/kernel/fpu/xstate.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 58e7567dc3cc..8939345778a7 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -1287,6 +1287,7 @@ void copy_xstate_to_uabi_buf(struct membuf to, struct task_struct *tsk,
zerofrom = offsetof(struct xregs_state, extended_state_area);
for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
+ u64 mask = BIT_ULL(i);
/*
* The ptrace buffer is in non-compacted XSAVE format.
* In non-compacted format disabled features still occupy
@@ -1294,7 +1295,7 @@ void copy_xstate_to_uabi_buf(struct membuf to, struct task_struct *tsk,
* compacted init_fpstate. The gap tracking will zero this
* later.
*/
- if (!(xfeatures_mask_uabi() & BIT_ULL(i)))
+ if (!(xfeatures_mask_uabi() & mask))
continue;
/*
@@ -1314,10 +1315,24 @@ void copy_xstate_to_uabi_buf(struct membuf to, struct task_struct *tsk,
pkru.pkru = tsk->thread.pkru;
membuf_write(&to, &pkru, sizeof(pkru));
} else {
- copy_feature(header.xfeatures & BIT_ULL(i), &to,
- __raw_xsave_addr(&tsk->thread.fpu, i),
- __raw_xsave_addr(NULL, i),
- xstate_sizes[i]);
+ unsigned int size = xstate_sizes[i];
+ void *from = NULL;
+
+ /*
+ * Copy the xstate if available. Otherwise, copy the
+ * non-zero init states for legacy states (FP and
+ * SSE) or fill zeros.
+ */
+
+ if (header.xfeatures & mask)
+ from = __raw_xsave_addr(&tsk->thread.fpu, i);
+ else if (XFEATURE_MASK_FPSSE & mask)
+ from = __raw_xsave_addr(NULL, i);
+
+ if (from)
+ membuf_write(&to, from, size);
+ else
+ membuf_zero(&to, size);
}
/*
* Keep track of the last copied state in the non-compacted
@@ -1359,6 +1374,8 @@ static int copy_uabi_to_xstate(struct fpu *fpu, const void *kbuf,
if (validate_user_xstate_header(&hdr))
return -EINVAL;
+ hdr.xfeatures &= fpu->state_mask;
+
/* Validate MXCSR when any of the related features is in use */
mask = XFEATURE_MASK_FP | XFEATURE_MASK_SSE | XFEATURE_MASK_YMM;
if (hdr.xfeatures & mask) {
@@ -1385,6 +1402,9 @@ static int copy_uabi_to_xstate(struct fpu *fpu, const void *kbuf,
if (hdr.xfeatures & mask) {
void *dst = __raw_xsave_addr(fpu, i);
+ if (!dst)
+ continue;
+
offset = xstate_offsets[i];
size = xstate_sizes[i];
--
2.17.1