| From d5e56fff82e61f16458e43b8ada8ccef1edc5696 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 9 Apr 2025 17:40:06 +0100 |
| Subject: arm64/fpsimd: Fix merging of FPSIMD state during signal return |
| |
| From: Mark Rutland <mark.rutland@arm.com> |
| |
| [ Upstream commit c94f2f326146a34066a0070ed90b8bc656b1842f ] |
| |
| For backwards compatibility reasons, when a signal return occurs which |
| restores SVE state, the effective lower 128 bits of each of the SVE |
| vector registers are restored from the corresponding FPSIMD vector |
| register in the FPSIMD signal frame, overriding the values in the SVE |
| signal frame. This is intended to be the case regardless of streaming |
| mode. |
| |
| To make this happen, restore_sve_fpsimd_context() uses |
| fpsimd_update_current_state() to merge the lower 128 bits from the |
| FPSIMD signal frame into the SVE register state. Unfortunately, |
| fpsimd_update_current_state() performs this merging dependent upon |
| TIF_SVE, which is not always correct for streaming SVE register state: |
| |
| * When restoring non-streaming SVE register state there is no observable |
| problem, as the signal return code configures TIF_SVE and the saved |
| fp_type to match before calling fpsimd_update_current_state(), which |
| observes either: |
| |
| - TIF_SVE set AND fp_type == FP_STATE_SVE |
| - TIF_SVE clear AND fp_type == FP_STATE_FPSIMD |
| |
| * On systems which have SME but not SVE, TIF_SVE cannot be set. Thus the |
| merging will never happen for the streaming SVE register state. |
| |
| * On systems which have SVE and SME, TIF_SVE can be set and cleared |
| independently of PSTATE.SM. Thus the merging may or may not happen for |
| streaming SVE register state. |
| |
| As TIF_SVE can be cleared non-deterministically during syscalls |
| (including at the start of sigreturn()), the merging may occur |
| non-deterministically from the perspective of userspace. |
| |
| This logic has been broken since its introduction in commit: |
| |
| 85ed24dad2904f7c ("arm64/sme: Implement streaming SVE signal handling") |
| |
| ... at which point both fpsimd_signal_preserve_current_state() and |
| fpsimd_update_current_state() only checked TIF SVE. When PSTATE.SM==1 |
| and TIF_SVE was clear, signal delivery would place stale FPSIMD state |
| into the FPSIMD signal frame, and signal return would not merge this |
| into the restored register state. |
| |
| Subsequently, signal delivery was fixed as part of commit: |
| |
| 61da7c8e2a602f66 ("arm64/signal: Don't assume that TIF_SVE means we saved SVE state") |
| |
| ... but signal restore was not given a corresponding fix, and when |
| TIF_SVE was clear, signal restore would still fail to merge the FPSIMD |
| state into the restored SVE register state. The 'Fixes' tag did not |
| indicate that this had been broken since its introduction. |
| |
| Fix this by merging the FPSIMD state dependent upon the saved fp_type, |
| matching what we (currently) do during signal delivery. |
| |
| As described above, when backporting this commit, it will also be |
| necessary to backport commit: |
| |
| 61da7c8e2a602f66 ("arm64/signal: Don't assume that TIF_SVE means we saved SVE state") |
| |
| ... and prior to commit: |
| |
| baa8515281b30861 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE") |
| |
| ... it will be necessary for fpsimd_signal_preserve_current_state() and |
| fpsimd_update_current_state() to consider both TIF_SVE and |
| thread_sm_enabled(¤t->thread), in place of the saved fp_type. |
| |
| Fixes: 85ed24dad290 ("arm64/sme: Implement streaming SVE signal handling") |
| 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-10-mark.rutland@arm.com |
| Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/arm64/kernel/fpsimd.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c |
| index 235131db8b8db..837d1937300a5 100644 |
| --- a/arch/arm64/kernel/fpsimd.c |
| +++ b/arch/arm64/kernel/fpsimd.c |
| @@ -1783,7 +1783,7 @@ void fpsimd_update_current_state(struct user_fpsimd_state const *state) |
| get_cpu_fpsimd_context(); |
| |
| current->thread.uw.fpsimd_state = *state; |
| - if (test_thread_flag(TIF_SVE)) |
| + if (current->thread.fp_type == FP_STATE_SVE) |
| fpsimd_to_sve(current); |
| |
| task_fpsimd_load(); |
| -- |
| 2.39.5 |
| |