Skip to content

Commit 929fa99

Browse files
mrutland-armctmarinas
authored andcommitted
arm64/fpsimd: signal: Always save+flush state early
There are several issues with the way the native signal handling code manipulates FPSIMD/SVE/SME state, described in detail below. These issues largely result from races with preemption and inconsistent handling of live state vs saved state. Known issues with native FPSIMD/SVE/SME state management include: * On systems with FPMR, the code to save/restore the FPMR accesses the register while it is not owned by the current task. Consequently, this may corrupt the FPMR of the current task and/or may corrupt the FPMR of an unrelated task. The FPMR save/restore has been broken since it was introduced in commit: 8c46def ("arm64/signal: Add FPMR signal handling") * On systems with SME, setup_return() modifies both the live register state and the saved state register state regardless of whether the task's state is live, and without holding the cpu fpsimd context. Consequently: - This may corrupt the state an unrelated task which has PSTATE.SM set and/or PSTATE.ZA set. - The task may enter the signal handler in streaming mode, and or with ZA storage enabled unexpectedly. - The task may enter the signal handler in non-streaming SVE mode with stale SVE register state, which may have been inherited from streaming SVE mode unexpectedly. Where the streaming and non-streaming vector lengths differ, this may be packed into registers arbitrarily. This logic has been broken since it was introduced in commit: 40a8e87 ("arm64/sme: Disable ZA and streaming mode when handling signals") Further incorrect manipulation of state was added in commits: ea64baa ("arm64/signal: Flush FPSIMD register state when disabling streaming mode") baa8515 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE") * Several restoration functions use fpsimd_flush_task_state() to discard the live FPSIMD/SVE/SME while the in-memory copy is stale. When a subset of the FPSIMD/SVE/SME state is restored, the remainder may be non-deterministically reset to a stale snapshot from some arbitrary point in the past. This non-deterministic discarding was introduced in commit: 8cd969d ("arm64/sve: Signal handling support") As of that commit, when TIF_SVE was initially clear, failure to restore the SVE signal frame could reset the FPSIMD registers to a stale snapshot. The pattern of discarding unsaved state was subsequently copied into restoration functions for some new state in commits: 3978221 ("arm64/sme: Implement ZA signal handling") ee072cf ("arm64/sme: Implement signal handling for ZT") * On systems with SME/SME2, the entire FPSIMD/SVE/SME state may be loaded onto the CPU redundantly. Either restore_fpsimd_context() or restore_sve_fpsimd_context() will load the entire FPSIMD/SVE/SME state via fpsimd_update_current_state() before restore_za_context() and restore_zt_context() each discard the state via fpsimd_flush_task_state(). This is purely redundant work, and not a functional bug. To fix these issues, rework the native signal handling code to always save+flush the current task's FPSIMD/SVE/SME state before manipulating that state. This avoids races with preemption and ensures that state is manipulated consistently regardless of whether it happened to be live prior to manipulation. This largely involes: * Using fpsimd_save_and_flush_current_state() to save+flush the state for both signal delivery and signal return, before the state is manipulated in any way. * Removing fpsimd_signal_preserve_current_state() and updating preserve_fpsimd_context() to explicitly ensure that the FPSIMD state is up-to-date, as preserve_fpsimd_context() is the only consumer of the FPSIMD state during signal delivery. * Modifying fpsimd_update_current_state() to not reload the FPSIMD state onto the CPU. Ideally we'd remove fpsimd_update_current_state() entirely, but I've left that for subsequent patches as there are a number of of other problems with the FPSIMD<->SVE conversion helpers that should be addressed at the same time. For now I've removed the misleading comment. For setup_return(), we need to decide (for ABI reasons) whether signal delivery should have all the side-effects of an SMSTOP. For now I've left a TODO comment, as there are other questions in this area that I'll address with subsequent patches. Fixes: 8c46def ("arm64/signal: Add FPMR signal handling") Fixes: 40a8e87 ("arm64/sme: Disable ZA and streaming mode when handling signals") Fixes: ea64baa ("arm64/signal: Flush FPSIMD register state when disabling streaming mode") Fixes: baa8515 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE") Fixes: 8cd969d ("arm64/sve: Signal handling support") Fixes: 3978221 ("arm64/sme: Implement ZA signal handling") Fixes: ee072cf ("arm64/sme: Implement signal handling for ZT") Signed-off-by: Mark Rutland <mark.rutland@arm.com> Cc: Marc Zyngier <maz@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Will Deacon <will@kernel.org> Reviewed-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20250409164010.3480271-13-mark.rutland@arm.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
1 parent 3aa4d74 commit 929fa99

File tree

3 files changed

+12
-83
lines changed

3 files changed

+12
-83
lines changed

arch/arm64/include/asm/fpsimd.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ extern void fpsimd_load_state(struct user_fpsimd_state *state);
7676
extern void fpsimd_thread_switch(struct task_struct *next);
7777
extern void fpsimd_flush_thread(void);
7878

79-
extern void fpsimd_signal_preserve_current_state(void);
8079
extern void fpsimd_preserve_current_state(void);
8180
extern void fpsimd_restore_current_state(void);
8281
extern void fpsimd_update_current_state(struct user_fpsimd_state const *state);

arch/arm64/kernel/fpsimd.c

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,18 +1662,6 @@ void fpsimd_preserve_current_state(void)
16621662
put_cpu_fpsimd_context();
16631663
}
16641664

1665-
/*
1666-
* Like fpsimd_preserve_current_state(), but ensure that
1667-
* current->thread.uw.fpsimd_state is updated so that it can be copied to
1668-
* the signal frame.
1669-
*/
1670-
void fpsimd_signal_preserve_current_state(void)
1671-
{
1672-
fpsimd_preserve_current_state();
1673-
if (current->thread.fp_type == FP_STATE_SVE)
1674-
sve_to_fpsimd(current);
1675-
}
1676-
16771665
/*
16781666
* Associate current's FPSIMD context with this cpu
16791667
* The caller must have ownership of the cpu FPSIMD context before calling
@@ -1766,30 +1754,14 @@ void fpsimd_restore_current_state(void)
17661754
put_cpu_fpsimd_context();
17671755
}
17681756

1769-
/*
1770-
* Load an updated userland FPSIMD state for 'current' from memory and set the
1771-
* flag that indicates that the FPSIMD register contents are the most recent
1772-
* FPSIMD state of 'current'. This is used by the signal code to restore the
1773-
* register state when returning from a signal handler in FPSIMD only cases,
1774-
* any SVE context will be discarded.
1775-
*/
17761757
void fpsimd_update_current_state(struct user_fpsimd_state const *state)
17771758
{
17781759
if (WARN_ON(!system_supports_fpsimd()))
17791760
return;
17801761

1781-
get_cpu_fpsimd_context();
1782-
17831762
current->thread.uw.fpsimd_state = *state;
17841763
if (current->thread.fp_type == FP_STATE_SVE)
17851764
fpsimd_to_sve(current);
1786-
1787-
task_fpsimd_load();
1788-
fpsimd_bind_task_to_cpu();
1789-
1790-
clear_thread_flag(TIF_FOREIGN_FPSTATE);
1791-
1792-
put_cpu_fpsimd_context();
17931765
}
17941766

17951767
/*

arch/arm64/kernel/signal.c

Lines changed: 12 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
250250
&current->thread.uw.fpsimd_state;
251251
int err;
252252

253+
sve_sync_to_fpsimd(current);
254+
253255
/* copy the FP and status/control registers */
254256
err = __copy_to_user(ctx->vregs, fpsimd->vregs, sizeof(fpsimd->vregs));
255257
__put_user_error(fpsimd->fpsr, &ctx->fpsr, err);
@@ -291,8 +293,6 @@ static int preserve_fpmr_context(struct fpmr_context __user *ctx)
291293
{
292294
int err = 0;
293295

294-
current->thread.uw.fpmr = read_sysreg_s(SYS_FPMR);
295-
296296
__put_user_error(FPMR_MAGIC, &ctx->head.magic, err);
297297
__put_user_error(sizeof(*ctx), &ctx->head.size, err);
298298
__put_user_error(current->thread.uw.fpmr, &ctx->fpmr, err);
@@ -310,7 +310,7 @@ static int restore_fpmr_context(struct user_ctxs *user)
310310

311311
__get_user_error(fpmr, &user->fpmr->fpmr, err);
312312
if (!err)
313-
write_sysreg_s(fpmr, SYS_FPMR);
313+
current->thread.uw.fpmr = fpmr;
314314

315315
return err;
316316
}
@@ -372,11 +372,6 @@ static int preserve_sve_context(struct sve_context __user *ctx)
372372
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
373373

374374
if (vq) {
375-
/*
376-
* This assumes that the SVE state has already been saved to
377-
* the task struct by calling the function
378-
* fpsimd_signal_preserve_current_state().
379-
*/
380375
err |= __copy_to_user((char __user *)ctx + SVE_SIG_REGS_OFFSET,
381376
current->thread.sve_state,
382377
SVE_SIG_REGS_SIZE(vq));
@@ -432,16 +427,6 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
432427
if (user->sve_size < SVE_SIG_CONTEXT_SIZE(vq))
433428
return -EINVAL;
434429

435-
/*
436-
* Careful: we are about __copy_from_user() directly into
437-
* thread.sve_state with preemption enabled, so protection is
438-
* needed to prevent a racing context switch from writing stale
439-
* registers back over the new data.
440-
*/
441-
442-
fpsimd_flush_task_state(current);
443-
/* From now, fpsimd_thread_switch() won't touch thread.sve_state */
444-
445430
sve_alloc(current, true);
446431
if (!current->thread.sve_state) {
447432
clear_thread_flag(TIF_SVE);
@@ -541,11 +526,6 @@ static int preserve_za_context(struct za_context __user *ctx)
541526
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
542527

543528
if (vq) {
544-
/*
545-
* This assumes that the ZA state has already been saved to
546-
* the task struct by calling the function
547-
* fpsimd_signal_preserve_current_state().
548-
*/
549529
err |= __copy_to_user((char __user *)ctx + ZA_SIG_REGS_OFFSET,
550530
current->thread.sme_state,
551531
ZA_SIG_REGS_SIZE(vq));
@@ -580,16 +560,6 @@ static int restore_za_context(struct user_ctxs *user)
580560
if (user->za_size < ZA_SIG_CONTEXT_SIZE(vq))
581561
return -EINVAL;
582562

583-
/*
584-
* Careful: we are about __copy_from_user() directly into
585-
* thread.sme_state with preemption enabled, so protection is
586-
* needed to prevent a racing context switch from writing stale
587-
* registers back over the new data.
588-
*/
589-
590-
fpsimd_flush_task_state(current);
591-
/* From now, fpsimd_thread_switch() won't touch thread.sve_state */
592-
593563
sme_alloc(current, true);
594564
if (!current->thread.sme_state) {
595565
current->thread.svcr &= ~SVCR_ZA_MASK;
@@ -627,11 +597,6 @@ static int preserve_zt_context(struct zt_context __user *ctx)
627597
BUILD_BUG_ON(sizeof(ctx->__reserved) != sizeof(reserved));
628598
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
629599

630-
/*
631-
* This assumes that the ZT state has already been saved to
632-
* the task struct by calling the function
633-
* fpsimd_signal_preserve_current_state().
634-
*/
635600
err |= __copy_to_user((char __user *)ctx + ZT_SIG_REGS_OFFSET,
636601
thread_zt_state(&current->thread),
637602
ZT_SIG_REGS_SIZE(1));
@@ -657,16 +622,6 @@ static int restore_zt_context(struct user_ctxs *user)
657622
if (nregs != 1)
658623
return -EINVAL;
659624

660-
/*
661-
* Careful: we are about __copy_from_user() directly into
662-
* thread.zt_state with preemption enabled, so protection is
663-
* needed to prevent a racing context switch from writing stale
664-
* registers back over the new data.
665-
*/
666-
667-
fpsimd_flush_task_state(current);
668-
/* From now, fpsimd_thread_switch() won't touch ZT in thread state */
669-
670625
err = __copy_from_user(thread_zt_state(&current->thread),
671626
(char __user const *)user->zt +
672627
ZT_SIG_REGS_OFFSET,
@@ -1017,6 +972,8 @@ static int restore_sigframe(struct pt_regs *regs,
1017972
*/
1018973
forget_syscall(regs);
1019974

975+
fpsimd_save_and_flush_current_state();
976+
1020977
err |= !valid_user_regs(&regs->user_regs, current);
1021978
if (err == 0)
1022979
err = parse_user_sigframe(&user, sf);
@@ -1510,18 +1467,19 @@ static int setup_return(struct pt_regs *regs, struct ksignal *ksig,
15101467
/*
15111468
* If we were in streaming mode the saved register
15121469
* state was SVE but we will exit SM and use the
1513-
* FPSIMD register state - flush the saved FPSIMD
1514-
* register state in case it gets loaded.
1470+
* FPSIMD register state.
1471+
*
1472+
* TODO: decide if this should behave as SMSTOP (e.g. reset
1473+
* FPSR + FPMR), or whether this should only clear the scalable
1474+
* registers + ZA state.
15151475
*/
15161476
if (current->thread.svcr & SVCR_SM_MASK) {
15171477
memset(&current->thread.uw.fpsimd_state, 0,
15181478
sizeof(current->thread.uw.fpsimd_state));
15191479
current->thread.fp_type = FP_STATE_FPSIMD;
15201480
}
15211481

1522-
current->thread.svcr &= ~(SVCR_ZA_MASK |
1523-
SVCR_SM_MASK);
1524-
sme_smstop();
1482+
current->thread.svcr &= ~(SVCR_ZA_MASK | SVCR_SM_MASK);
15251483
}
15261484

15271485
return 0;
@@ -1535,7 +1493,7 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
15351493
struct user_access_state ua_state;
15361494
int err = 0;
15371495

1538-
fpsimd_signal_preserve_current_state();
1496+
fpsimd_save_and_flush_current_state();
15391497

15401498
if (get_sigframe(&user, ksig, regs))
15411499
return 1;

0 commit comments

Comments
 (0)