mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-19 10:11:56 -05:00
arm64/fpsimd: ptrace: Consistently handle partial writes to NT_ARM_(S)SVE
Partial writes to the NT_ARM_SVE and NT_ARM_SSVE regsets using an payload are handled inconsistently and non-deterministically. A comment within sve_set_common() indicates that we intended that a partial write would preserve any effective FPSIMD/SVE state which was not overwritten, but this has never worked consistently, and during syscalls the FPSIMD vector state may be non-deterministically preserved and may be erroneously migrated between streaming and non-streaming SVE modes. The simplest fix is to handle a partial write by consistently zeroing the remaining state. As detailed below I do not believe this will adversely affect any real usage. Neither GDB nor LLDB attempt partial writes to these regsets, and the documentation (in Documentation/arch/arm64/sve.rst) has always indicated that state preservation was not guaranteed, as is says: | The effect of writing a partial, incomplete payload is unspecified. When the logic was originally introduced in commit:43d4da2c45("arm64/sve: ptrace and ELF coredump support") ... there were two potential behaviours, depending on TIF_SVE: * When TIF_SVE was clear, all SVE state would be zeroed, excluding the low 128 bits of vectors shared with FPSIMD, FPSR, and FPCR. * When TIF_SVE was set, all SVE state would be zeroed, including the low 128 bits of vectors shared with FPSIMD, but excluding FPSR and FPCR. Note that as writing to NT_ARM_SVE would set TIF_SVE, partial writes to NT_ARM_SVE would not be idempotent, and if a first write preserved the low 128 bits, a subsequent (potentially identical) partial write would discard the low 128 bits. When support for the NT_ARM_SSVE regset was added in commit:e12310a0d3("arm64/sme: Implement ptrace support for streaming mode SVE registers") ... the above behaviour was retained for writes to the NT_ARM_SVE regset, though writes to the NT_ARM_SSVE would always zero the SVE registers and would not inherit FPSIMD register state. This happened as fpsimd_sync_to_sve() only copied the FPSIMD regs when TIF_SVE was clear and PSTATE.SM==0. Subsequently, when FPSIMD/SVE state tracking was changed across commits:baa8515281("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")a0136be443(arm64/fpsimd: Load FP state based on recorded data type")bbc6172eef("arm64/fpsimd: SME no longer requires SVE register state")8c845e2731("arm64/sve: Leave SVE enabled on syscall if we don't context switch") ... there was no corresponding update to the ptrace code, nor to fpsimd_sync_to_sve(), which stil considers TIF_SVE and PSTATE.SM rather than the saved fp_type. The saved state can be in the FPSIMD format regardless of whether TIF_SVE is set or clear, and the saved type can change non-deterministically during syscalls. Consequently a subsequent partial write to the NT_ARM_SVE or NT_ARM_SSVE regsets may non-deterministically preserve the FPSIMD state, and may migrate this state between streaming and non-streaming modes. Clean this up by never attempting to preserve ANY state when writing an SVE payload to the NT_ARM_SVE/NT_ARM_SSVE regsets, zeroing all relevant state including FPSR and FPCR. This simplifies the code, makes the behaviour deterministic, and avoids migrating state between streaming and non-streaming modes. As above, I do not believe this should adversely affect existing userspace applications. At the same time, remove fpsimd_sync_to_sve(). It is no longer used, doesn't do what its documentation implies, and gets in the way of other cleanups and fixes. Fixes:43d4da2c45("arm64/sve: ptrace and ELF coredump support") Signed-off-by: Mark Rutland <mark.rutland@arm.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: David Spickett <david.spickett@arm.com> Cc: Luis Machado <luis.machado@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-6-mark.rutland@arm.com Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
committed by
Will Deacon
parent
be625d803c
commit
316283f276
@@ -198,7 +198,6 @@ struct vl_info {
|
||||
|
||||
extern void sve_alloc(struct task_struct *task, bool flush);
|
||||
extern void fpsimd_release_task(struct task_struct *task);
|
||||
extern void fpsimd_sync_to_sve(struct task_struct *task);
|
||||
extern void sve_sync_to_fpsimd(struct task_struct *task);
|
||||
extern void sve_sync_from_fpsimd_zeropad(struct task_struct *task);
|
||||
|
||||
|
||||
@@ -759,21 +759,6 @@ void sve_alloc(struct task_struct *task, bool flush)
|
||||
kzalloc(sve_state_size(task), GFP_KERNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that task->thread.sve_state is up to date with respect to
|
||||
* the user task, irrespective of when SVE is in use or not.
|
||||
*
|
||||
* This should only be called by ptrace. task must be non-runnable.
|
||||
* task->thread.sve_state must point to at least sve_state_size(task)
|
||||
* bytes of allocated kernel memory.
|
||||
*/
|
||||
void fpsimd_sync_to_sve(struct task_struct *task)
|
||||
{
|
||||
if (!test_tsk_thread_flag(task, TIF_SVE) &&
|
||||
!thread_sm_enabled(&task->thread))
|
||||
fpsimd_to_sve(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that task->thread.uw.fpsimd_state is up to date with respect to
|
||||
* the user task, irrespective of whether SVE is in use or not.
|
||||
|
||||
@@ -910,8 +910,6 @@ static int sve_set_common(struct task_struct *target,
|
||||
|
||||
/* Enter/exit streaming mode */
|
||||
if (system_supports_sme()) {
|
||||
u64 old_svcr = target->thread.svcr;
|
||||
|
||||
switch (type) {
|
||||
case ARM64_VEC_SVE:
|
||||
target->thread.svcr &= ~SVCR_SM_MASK;
|
||||
@@ -931,23 +929,20 @@ static int sve_set_common(struct task_struct *target,
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we switched then invalidate any existing SVE
|
||||
* state and ensure there's storage.
|
||||
*/
|
||||
if (target->thread.svcr != old_svcr)
|
||||
sve_alloc(target, true);
|
||||
}
|
||||
|
||||
/* Always zero V regs, FPSR, and FPCR */
|
||||
memset(¤t->thread.uw.fpsimd_state, 0,
|
||||
sizeof(current->thread.uw.fpsimd_state));
|
||||
|
||||
/* Registers: FPSIMD-only case */
|
||||
|
||||
BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header));
|
||||
if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) {
|
||||
ret = __fpr_set(target, regset, pos, count, kbuf, ubuf,
|
||||
SVE_PT_FPSIMD_OFFSET);
|
||||
clear_tsk_thread_flag(target, TIF_SVE);
|
||||
target->thread.fp_type = FP_STATE_FPSIMD;
|
||||
ret = __fpr_set(target, regset, pos, count, kbuf, ubuf,
|
||||
SVE_PT_FPSIMD_OFFSET);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -966,6 +961,7 @@ static int sve_set_common(struct task_struct *target,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Always zero SVE state */
|
||||
sve_alloc(target, true);
|
||||
if (!target->thread.sve_state) {
|
||||
ret = -ENOMEM;
|
||||
@@ -975,13 +971,9 @@ static int sve_set_common(struct task_struct *target,
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure target->thread.sve_state is up to date with target's
|
||||
* FPSIMD regs, so that a short copyin leaves trailing
|
||||
* registers unmodified. Only enable SVE if we are
|
||||
* configuring normal SVE, a system with streaming SVE may not
|
||||
* have normal SVE.
|
||||
* Only enable SVE if we are configuring normal SVE, a system with
|
||||
* streaming SVE may not have normal SVE.
|
||||
*/
|
||||
fpsimd_sync_to_sve(target);
|
||||
if (type == ARM64_VEC_SVE)
|
||||
set_tsk_thread_flag(target, TIF_SVE);
|
||||
target->thread.fp_type = FP_STATE_SVE;
|
||||
|
||||
Reference in New Issue
Block a user