]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
arm64/fpsimd: signal: Consistently read FPSIMD context
authorMark Rutland <mark.rutland@arm.com>
Thu, 8 May 2025 13:26:24 +0000 (14:26 +0100)
committerWill Deacon <will@kernel.org>
Thu, 8 May 2025 14:28:27 +0000 (15:28 +0100)
For historical reasons, restore_sve_fpsimd_context() has an open-coded
copy of the logic from read_fpsimd_context(), which is used to either
restore an FPSIMD-only context, or to merge FPSIMD state into an
SVE state when restoring an SVE+FPSIMD context. The logic is *almost*
identical.

Refactor the logic to avoid duplication and make this clearer.

This comes with two functional changes that I do not believe will be
problematic in practice:

* The user_fpsimd_state::size field will be checked in all restore paths
  that consume it user_fpsimd_state. The kernel always populates this
  field when delivering a signal, and so this should contain the
  expected value unless it has been corrupted.

* If a read of user_fpsimd_state fails, we will return early without
  modifying TIF_SVE, the saved SVCR, or the save fp_type. This will
  leave the task in a consistent state, without potentially resurrecting
  stale FPSIMD state. A read of user_fpsimd_state should never fail
  unless the structure has been corrupted or the stack has been
  unmapped.

Suggested-by: Will Deacon <will@kernel.org>
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-5-mark.rutland@arm.com
[will: Ensure read_fpsimd_context() returns negative error code or zero]
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/kernel/signal.c

index e898948a31b659e4e96295f2c0c41090188cad3d..d9c9bad5f0a89730a0d97e0f89863f7aa82dc600 100644 (file)
@@ -264,30 +264,40 @@ static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
        return err ? -EFAULT : 0;
 }
 
-static int restore_fpsimd_context(struct user_ctxs *user)
+static int read_fpsimd_context(struct user_fpsimd_state *fpsimd,
+                              struct user_ctxs *user)
 {
-       struct user_fpsimd_state fpsimd;
-       int err = 0;
+       int err;
 
        /* check the size information */
        if (user->fpsimd_size != sizeof(struct fpsimd_context))
                return -EINVAL;
 
        /* copy the FP and status/control registers */
-       err = __copy_from_user(fpsimd.vregs, &(user->fpsimd->vregs),
-                              sizeof(fpsimd.vregs));
-       __get_user_error(fpsimd.fpsr, &(user->fpsimd->fpsr), err);
-       __get_user_error(fpsimd.fpcr, &(user->fpsimd->fpcr), err);
+       err = __copy_from_user(fpsimd->vregs, &(user->fpsimd->vregs),
+                              sizeof(fpsimd->vregs));
+       __get_user_error(fpsimd->fpsr, &(user->fpsimd->fpsr), err);
+       __get_user_error(fpsimd->fpcr, &(user->fpsimd->fpcr), err);
+
+       return err ? -EFAULT : 0;
+}
+
+static int restore_fpsimd_context(struct user_ctxs *user)
+{
+       struct user_fpsimd_state fpsimd;
+       int err;
+
+       err = read_fpsimd_context(&fpsimd, user);
+       if (err)
+               return err;
 
        clear_thread_flag(TIF_SVE);
        current->thread.svcr &= ~SVCR_SM_MASK;
        current->thread.fp_type = FP_STATE_FPSIMD;
 
        /* load the hardware registers from the fpsimd_state structure */
-       if (!err)
-               fpsimd_update_current_state(&fpsimd);
-
-       return err ? -EFAULT : 0;
+       fpsimd_update_current_state(&fpsimd);
+       return 0;
 }
 
 static int preserve_fpmr_context(struct fpmr_context __user *ctx)
@@ -427,12 +437,8 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
         * consistency and robustness, reject restoring streaming SVE state
         * without an SVE payload.
         */
-       if (!sm && user->sve_size == sizeof(*user->sve)) {
-               clear_thread_flag(TIF_SVE);
-               current->thread.svcr &= ~SVCR_SM_MASK;
-               current->thread.fp_type = FP_STATE_FPSIMD;
-               goto fpsimd_only;
-       }
+       if (!sm && user->sve_size == sizeof(*user->sve))
+               return restore_fpsimd_context(user);
 
        vq = sve_vq_from_vl(vl);
 
@@ -458,19 +464,14 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
                set_thread_flag(TIF_SVE);
        current->thread.fp_type = FP_STATE_SVE;
 
-fpsimd_only:
-       /* copy the FP and status/control registers */
-       /* restore_sigframe() already checked that user->fpsimd != NULL. */
-       err = __copy_from_user(fpsimd.vregs, user->fpsimd->vregs,
-                              sizeof(fpsimd.vregs));
-       __get_user_error(fpsimd.fpsr, &user->fpsimd->fpsr, err);
-       __get_user_error(fpsimd.fpcr, &user->fpsimd->fpcr, err);
+       err = read_fpsimd_context(&fpsimd, user);
+       if (err)
+               return err;
 
-       /* load the hardware registers from the fpsimd_state structure */
-       if (!err)
-               fpsimd_update_current_state(&fpsimd);
+       /* Merge the FPSIMD registers into the SVE state */
+       fpsimd_update_current_state(&fpsimd);
 
-       return err ? -EFAULT : 0;
+       return 0;
 }
 
 #else /* ! CONFIG_ARM64_SVE */