Skip to content

Commit

Permalink
arm64/sve: Avoid dereference of dead task_struct in KVM guest entry
Browse files Browse the repository at this point in the history
When deciding whether to invalidate FPSIMD state cached in the cpu,
the backend function sve_flush_cpu_state() attempts to dereference
__this_cpu_read(fpsimd_last_state).  However, this is not safe:
there is no guarantee that this task_struct pointer is still valid,
because the task could have exited in the meantime.

This means that we need another means to get the appropriate value
of TIF_SVE for the associated task.

This patch solves this issue by adding a cached copy of the TIF_SVE
flag in fpsimd_last_state, which we can check without dereferencing
the task pointer.

In particular, although this patch is not a KVM fix per se, this
means that this check is now done safely in the KVM world switch
path (which is currently the only user of this code).

Signed-off-by: Dave Martin <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Cc: Christoffer Dall <[email protected]>
Cc: Marc Zyngier <[email protected]>
Signed-off-by: Will Deacon <[email protected]>
  • Loading branch information
Dave Martin authored and wildea01 committed Dec 6, 2017
1 parent d96cc49 commit cb968af
Showing 1 changed file with 16 additions and 12 deletions.
28 changes: 16 additions & 12 deletions arch/arm64/kernel/fpsimd.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@
* returned from the 2nd syscall yet, TIF_FOREIGN_FPSTATE is still set so
* whatever is in the FPSIMD registers is not saved to memory, but discarded.
*/
static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state);
struct fpsimd_last_state_struct {
struct fpsimd_state *st;
bool sve_in_use;
};

static DEFINE_PER_CPU(struct fpsimd_last_state_struct, fpsimd_last_state);

/* Default VL for tasks that don't set it explicitly: */
static int sve_default_vl = -1;
Expand Down Expand Up @@ -905,7 +910,7 @@ void fpsimd_thread_switch(struct task_struct *next)
*/
struct fpsimd_state *st = &next->thread.fpsimd_state;

if (__this_cpu_read(fpsimd_last_state) == st
if (__this_cpu_read(fpsimd_last_state.st) == st
&& st->cpu == smp_processor_id())
clear_tsk_thread_flag(next, TIF_FOREIGN_FPSTATE);
else
Expand Down Expand Up @@ -997,9 +1002,12 @@ void fpsimd_signal_preserve_current_state(void)
*/
static void fpsimd_bind_to_cpu(void)
{
struct fpsimd_last_state_struct *last =
this_cpu_ptr(&fpsimd_last_state);
struct fpsimd_state *st = &current->thread.fpsimd_state;

__this_cpu_write(fpsimd_last_state, st);
last->st = st;
last->sve_in_use = test_thread_flag(TIF_SVE);
st->cpu = smp_processor_id();
}

Expand Down Expand Up @@ -1057,7 +1065,7 @@ void fpsimd_flush_task_state(struct task_struct *t)

static inline void fpsimd_flush_cpu_state(void)
{
__this_cpu_write(fpsimd_last_state, NULL);
__this_cpu_write(fpsimd_last_state.st, NULL);
}

/*
Expand All @@ -1070,14 +1078,10 @@ static inline void fpsimd_flush_cpu_state(void)
#ifdef CONFIG_ARM64_SVE
void sve_flush_cpu_state(void)
{
struct fpsimd_state *const fpstate = __this_cpu_read(fpsimd_last_state);
struct task_struct *tsk;

if (!fpstate)
return;
struct fpsimd_last_state_struct const *last =
this_cpu_ptr(&fpsimd_last_state);

tsk = container_of(fpstate, struct task_struct, thread.fpsimd_state);
if (test_tsk_thread_flag(tsk, TIF_SVE))
if (last->st && last->sve_in_use)
fpsimd_flush_cpu_state();
}
#endif /* CONFIG_ARM64_SVE */
Expand Down Expand Up @@ -1272,7 +1276,7 @@ static inline void fpsimd_pm_init(void) { }
#ifdef CONFIG_HOTPLUG_CPU
static int fpsimd_cpu_dead(unsigned int cpu)
{
per_cpu(fpsimd_last_state, cpu) = NULL;
per_cpu(fpsimd_last_state.st, cpu) = NULL;
return 0;
}

Expand Down

0 comments on commit cb968af

Please sign in to comment.