diff mbox series

[2/3] arm64: smp: Don't enter kernel with NULL stack pointer or task struct

Message ID 20190827151815.2160-3-will@kernel.org (mailing list archive)
State Mainlined
Commit 5b1cfe3a0ba74c1f2b83b607712a217b9f9463a2
Headers show
Series Try to make SMP booting slightly less fragile | expand

Commit Message

Will Deacon Aug. 27, 2019, 3:18 p.m. UTC
Although SMP bringup is inherently racy, we can significantly reduce
the window during which secondary CPUs can unexpectedly enter the
kernel by sanity checking the 'stack' and 'task' fields of the
'secondary_data' structure. If the booting CPU gave up waiting for us,
then they will have been cleared to NULL and we should spin in a WFE; WFI
loop instead.

Signed-off-by: Will Deacon <will@kernel.org>
---
 arch/arm64/kernel/head.S | 8 ++++++++
 arch/arm64/kernel/smp.c  | 1 +
 2 files changed, 9 insertions(+)

Comments

Mark Rutland Aug. 27, 2019, 4:04 p.m. UTC | #1
On Tue, Aug 27, 2019 at 04:18:14PM +0100, Will Deacon wrote:
> Although SMP bringup is inherently racy, we can significantly reduce
> the window during which secondary CPUs can unexpectedly enter the
> kernel by sanity checking the 'stack' and 'task' fields of the
> 'secondary_data' structure. If the booting CPU gave up waiting for us,
> then they will have been cleared to NULL and we should spin in a WFE; WFI
> loop instead.
> 
> Signed-off-by: Will Deacon <will@kernel.org>

In future I think that we can handle this better using the MPIDR hash
and atomics to flip the values and cater for races, but this is better
than what we have, so FWIW:

Reviewed-by: Mark Rutland <mark.rutland@arm.com>

Thanks,
Mark.

> ---
>  arch/arm64/kernel/head.S | 8 ++++++++
>  arch/arm64/kernel/smp.c  | 1 +
>  2 files changed, 9 insertions(+)
> 
> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
> index 2cdacd1c141b..0baadf335172 100644
> --- a/arch/arm64/kernel/head.S
> +++ b/arch/arm64/kernel/head.S
> @@ -724,14 +724,22 @@ __secondary_switched:
>  
>  	adr_l	x0, secondary_data
>  	ldr	x1, [x0, #CPU_BOOT_STACK]	// get secondary_data.stack
> +	cbz	x1, __secondary_too_slow
>  	mov	sp, x1
>  	ldr	x2, [x0, #CPU_BOOT_TASK]
> +	cbz	x2, __secondary_too_slow
>  	msr	sp_el0, x2
>  	mov	x29, #0
>  	mov	x30, #0
>  	b	secondary_start_kernel
>  ENDPROC(__secondary_switched)
>  
> +__secondary_too_slow:
> +	wfe
> +	wfi
> +	b	__secondary_too_slow
> +ENDPROC(__secondary_too_slow)
> +
>  /*
>   * The booting CPU updates the failed status @__early_cpu_boot_status,
>   * with MMU turned off.
> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> index 63c7a7682e93..1f8aeb77cba5 100644
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -136,6 +136,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
>  
>  	secondary_data.task = NULL;
>  	secondary_data.stack = NULL;
> +	__flush_dcache_area(&secondary_data, sizeof(secondary_data));
>  	status = READ_ONCE(secondary_data.status);
>  	if (ret && status) {
>  
> -- 
> 2.11.0
>
diff mbox series

Patch

diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 2cdacd1c141b..0baadf335172 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -724,14 +724,22 @@  __secondary_switched:
 
 	adr_l	x0, secondary_data
 	ldr	x1, [x0, #CPU_BOOT_STACK]	// get secondary_data.stack
+	cbz	x1, __secondary_too_slow
 	mov	sp, x1
 	ldr	x2, [x0, #CPU_BOOT_TASK]
+	cbz	x2, __secondary_too_slow
 	msr	sp_el0, x2
 	mov	x29, #0
 	mov	x30, #0
 	b	secondary_start_kernel
 ENDPROC(__secondary_switched)
 
+__secondary_too_slow:
+	wfe
+	wfi
+	b	__secondary_too_slow
+ENDPROC(__secondary_too_slow)
+
 /*
  * The booting CPU updates the failed status @__early_cpu_boot_status,
  * with MMU turned off.
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 63c7a7682e93..1f8aeb77cba5 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -136,6 +136,7 @@  int __cpu_up(unsigned int cpu, struct task_struct *idle)
 
 	secondary_data.task = NULL;
 	secondary_data.stack = NULL;
+	__flush_dcache_area(&secondary_data, sizeof(secondary_data));
 	status = READ_ONCE(secondary_data.status);
 	if (ret && status) {