[PATCH] arm64: fpsimd: Prevent registers leaking from dead tasks

Ard Biesheuvel ard.biesheuvel at linaro.org
Wed Dec 6 01:57:15 PST 2017


On 5 December 2017 at 14:56, Dave Martin <Dave.Martin at arm.com> wrote:
> Currently, loading of a task's fpsimd state into the CPU registers
> is skipped if that task's state is already present in the registers
> of that CPU.
>
> However, the code relies on the struct fpsimd_state * (and by
> extension struct task_struct *) to unambiguously identify a task.
>
> There is a particular case in which this doesn't work reliably:
> when a task exits, its task_struct may be recycled to describe a
> new task.
>
> Consider the following scenario:
>
>  1) Task P loads its fpsimd state onto cpu C.
>         per_cpu(fpsimd_last_state, C) := P;
>         P->thread.fpsimd_state.cpu := C;
>
>  2) Task X is scheduled onto C and loads its fpsimd state on C.
>         per_cpu(fpsimd_last_state, C) := X;
>         X->thread.fpsimd_state.cpu := C;
>
>  3) X exits, causing X's task_struct to be freed.
>
>  4) P forks a new child T, which obtains X's recycled task_struct.
>         T == X.
>         T->thread.fpsimd_state.cpu == C (inherited from P).
>
>  5) T is scheduled on C.
>         T's fpsimd state is not loaded, because
>         per_cpu(fpsimd_last_state, C) == T (== X) &&
>         T->thread.fpsimd_state.cpu == C.
>
>         (This is the check performed by fpsimd_thread_switch().)
>
> So, T gets X's registers because the last registers loaded onto C
> were those of X, in (2).
>
> This patch fixes the problem by ensuring that the sched-in check
> fails in (5): fpsimd_flush_task_state(T) is called when T is
> forked, so that T->thread.fpsimd_state.cpu == C cannot be true.
> This relies on the fact that T is not schedulable until after
> copy_thread() completes.
>
> Once T's fpsimd state has been loaded on some CPU C there may still
> be other cpus D for which per_cpu(fpsimd_last_state, D) ==
> &X->thread.fpsimd_state.  But D is necessarily != C in this case,
> and the check in (5) must fail.
>
> An alternative fix would be to do refcounting on task_struct.  This
> would result in each CPU holding a reference to the last task whose
> fpsimd state was loaded there.  It's not clear whether this is
> preferable, and it involves higher overhead than the fix proposed
> in this patch.  It would also moves all the task_struct freeing

move

> work into the context switch critical section, or otherwise some
> deferred cleanup mechanism would need to be introduced, neither of
> which seems obviously justified.
>
> Fixes: 005f78cd8849 ("arm64: defer reloading a task's FPSIMD state to userland resume")
> Signed-off-by: Dave Martin <Dave.Martin at arm.com>
> Cc: Ard Biesheuvel <ard.biesheuvel at linaro.org>
> ---
>  arch/arm64/kernel/process.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index b2adcce..5387d15 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -313,6 +313,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>          */
>         clear_tsk_thread_flag(p, TIF_SVE);
>         p->thread.sve_state = NULL;
> +       fpsimd_flush_task_state(p);
>
>         if (likely(!(p->flags & PF_KTHREAD))) {
>                 *childregs = *current_pt_regs();
> --
> 2.1.4
>

Reviewed-by: Ard Biesheuvel <ard.biesheuvel at linaro.org>



More information about the linux-arm-kernel mailing list