Re: [PATCH v5 1/2] x86/fpu: Allow PKRU to be (once again) written by ptrace.
From: Kyle Huey
Date: Thu Aug 18 2022 - 15:48:56 EST
On Thu, Aug 18, 2022 at 3:57 AM Thomas Gleixner <tglx@xxxxxxxxxxxxx> wrote:
>
> Kyle!
Hi.
> On Mon, Aug 08 2022 at 07:15, Kyle Huey wrote:
> > When management of the PKRU register was moved away from XSTATE, emulation
> > of PKRU's existence in XSTATE was added for APIs that read XSTATE, but not
> > for APIs that write XSTATE. This 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.
> >
> > There are three relevant APIs: PTRACE_SETREGSET with NT_X86_XSTATE,
> > sigreturn, and KVM_SET_XSAVE. KVM_SET_XSAVE has its own special handling to
> > make PKRU writes take effect (in fpu_copy_uabi_to_guest_fpstate). Push that
> > down into copy_uabi_to_xstate and have PTRACE_SETREGSET with NT_X86_XSTATE
> > and sigreturn pass in pointers to the appropriate PKRU value.
> >
> > This also adds code to initialize the PKRU value to the hardware init value
> > (namely 0) if the PKRU bit is not set in the XSTATE header to match XRSTOR.
> > This is a change to the current KVM_SET_XSAVE behavior.
>
> You are stating a fact here, but provide 0 justification why this is
> correct.
Well, the justification is that this *is* the behavior we want for
ptrace/sigreturn, and it's very likely the existing KVM_SET_XSAVE
behavior in this edge case is an oversight rather than intentional,
and in the absence of confirmation that KVM wants the existing
behavior (the KVM mailing list and maintainer are CCd) one correct
code path is better than one correct code path and one buggy code
path.
> >
> > Changelog since v4:
>
> Can you please put the change log past the --- seperator line, so it
> gets stripped off when the patch is applied? That spares manual fixups.
Ok.
> >
> > Signed-off-by: Kyle Huey <me@xxxxxxxxxxxx>
> > Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
> > Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> > Cc: Borislav Petkov <bp@xxxxxxx>
> > Cc: kvm@xxxxxxxxxxxxxxx # For edge case behavior of KVM_SET_XSAVE
> > Cc: stable@xxxxxxxxxxxxxxx # 5.14+
> > Fixes: e84ba47e313d ("x86/fpu: Hook up PKRU into ptrace()")
>
> Can you please use the documented tag ordering?
>
> https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#patch-submission-notes
Ok.
> > @@ -1235,6 +1235,24 @@ static int copy_uabi_to_xstate(struct fpstate *fpstate, const void *kbuf,
> > for (i = 0; i < XFEATURE_MAX; i++) {
> > mask = BIT_ULL(i);
> >
> > + if (i == XFEATURE_PKRU) {
> > + /*
> > + * Retrieve PKRU if not in init state, otherwise
> > + * initialize it.
> > + */
> > + if (hdr.xfeatures & mask) {
> > + struct pkru_state xpkru = {0};
> > +
> > + if (copy_from_buffer(&xpkru, xstate_offsets[i],
> > + sizeof(xpkru), kbuf, ubuf))
> > + return -EFAULT;
> > +
> > + *pkru = xpkru.pkru;
> > + } else {
> > + *pkru = 0;
> > + }
> > + }
>
> That's really horrible and there is no point in copying the stuff from
> the buffer twice:
>
> @@ -1246,6 +1246,15 @@ static int copy_uabi_to_xstate(struct fp
> }
> }
>
> + /* Update the user protection key storage */
> + *pkru = 0;
> + if (hdr.xfeatures & XFEATURE_MASK_PKRU) {
> + struct pkru_state *xpkru;
> +
> + xpkru = get_xsave_addr(xsave, XFEATURE_PKRU);
> + *pkru = xpkru->pkru;
> + }
> +
>
> Hmm?
It took me a bit to figure out what this is actually trying to do. To
work, it would need to come at the very end of copy_uabi_to_xstate
after xsave->header.xfeatures is updated. If you just want to avoid
two copies I would counter-propose this though:
@@ -1235,7 +1235,19 @@ static int copy_uabi_to_xstate(struct fpstate
*fpstate, const void *kbuf,
for (i = 0; i < XFEATURE_MAX; i++) {
mask = BIT_ULL(i);
- if (hdr.xfeatures & mask) {
+ if (i == XFEATURE_PKRU) {
+ /* Update the user protection key storage */
+ *pkru = 0;
+ if (hdr.xfeatures & XFEATURE_MASK_PKRU) {
+ struct pkru_state xpkru = {0};
+
+ if (copy_from_buffer(&xpkru, xstate_offsets[i],
+ sizeof(xpkru), kbuf, ubuf))
+ return -EFAULT;
+
+ *pkru = xpkru.pkru;
+ }
+ } else if (hdr.xfeatures & mask) {
void *dst = __raw_xsave_addr(xsave, i);
offset = xstate_offsets[i];
Thoughts? This avoids a second copy and avoids having to calculate the
offset into the (now potentially compressed) XSTATE.
- Kyle
>
> Thanks,
>
> tglx