diff mbox series

ARM: mm: harden branch predictor before opening interrupts during fault

Message ID 20190319203239.gl46fxnfz6gzeeic@linutronix.de (mailing list archive)
State New, archived
Headers show
Series ARM: mm: harden branch predictor before opening interrupts during fault | expand

Commit Message

Sebastian Andrzej Siewior March 19, 2019, 8:32 p.m. UTC
On non-LPAE systems a write to 0xbffffff0 (modules area) from userland
results in:
| BUG: using smp_processor_id() in preemptible [00000000] code: mem-tc/521
| caller is __do_user_fault.constprop.2+0x4c/0x74
| CPU: 1 PID: 521 Comm: mem-tc Not tainted 5.1.0-rc1 #4
| [<c04614e4>] (debug_smp_processor_id) from [<c0116378>] (__do_user_fault.constprop.2+0x4c/0x74)
| [<c0116378>] (__do_user_fault.constprop.2) from [<c011668c>] (do_page_fault+0x278/0x37c)
| [<c011668c>] (do_page_fault) from [<c0116904>] (do_DataAbort+0x3c/0xa8)
| [<c0116904>] (do_DataAbort) from [<c0101e1c>] (__dabt_usr+0x3c/0x40)

Move harden_branch_predictor() from __do_user_fault() to its both
callers (do_bad_area() and do_page_fault()). The invocation in
do_page_fault() is added before interrupst are enabled. The invocation
in do_bad_area() is added just before __do_user_fault() is invoked.

Fixes: f5fe12b1eaee2 ("ARM: spectre-v2: harden user aborts in kernel space")
Reported-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 arch/arm/mm/fault.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

Comments

Sebastian Andrzej Siewior April 15, 2019, 11:08 a.m. UTC | #1
On 2019-03-19 21:32:39 [+0100], To linux-arm-kernel@lists.infradead.org wrote:
> On non-LPAE systems a write to 0xbffffff0 (modules area) from userland
> results in:
> | BUG: using smp_processor_id() in preemptible [00000000] code: mem-tc/521
> | caller is __do_user_fault.constprop.2+0x4c/0x74
> | CPU: 1 PID: 521 Comm: mem-tc Not tainted 5.1.0-rc1 #4
> | [<c04614e4>] (debug_smp_processor_id) from [<c0116378>] (__do_user_fault.constprop.2+0x4c/0x74)
> | [<c0116378>] (__do_user_fault.constprop.2) from [<c011668c>] (do_page_fault+0x278/0x37c)
> | [<c011668c>] (do_page_fault) from [<c0116904>] (do_DataAbort+0x3c/0xa8)
> | [<c0116904>] (do_DataAbort) from [<c0101e1c>] (__dabt_usr+0x3c/0x40)
> 
> Move harden_branch_predictor() from __do_user_fault() to its both
> callers (do_bad_area() and do_page_fault()). The invocation in
> do_page_fault() is added before interrupst are enabled. The invocation
> in do_bad_area() is added just before __do_user_fault() is invoked.

In 20190216113338.irr5j4ukhpwngval@shell.armlinux.org.uk Russel
complained that I am opening a window for branch predictor attacks that
he tried to close. This is no longer the case because
harden_branch_predictor() is now in do_page_fault() and do_bad_area().

So is this still obviously wrong and I don't see it?

> Fixes: f5fe12b1eaee2 ("ARM: spectre-v2: harden user aborts in kernel space")
> Reported-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
>  arch/arm/mm/fault.c | 12 +++++++-----
>  1 file changed, 7 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
> index 58f69fa07df95..7adff8eb8f3d2 100644
> --- a/arch/arm/mm/fault.c
> +++ b/arch/arm/mm/fault.c
> @@ -161,9 +161,6 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr,
>  		unsigned int fsr, unsigned int sig, int code,
>  		struct pt_regs *regs)
>  {
> -	if (addr > TASK_SIZE)
> -		harden_branch_predictor();
> -
>  #ifdef CONFIG_DEBUG_USER
>  	if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||
>  	    ((user_debug & UDBG_BUS)  && (sig == SIGBUS))) {
> @@ -195,10 +192,13 @@ void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
>  	 * If we are in kernel mode at this point, we
>  	 * have no context to handle this fault with.
>  	 */
> -	if (user_mode(regs))
> +	if (user_mode(regs)) {
> +		if (addr > TASK_SIZE)
> +			harden_branch_predictor();
>  		__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
> -	else
> +	} else {
>  		__do_kernel_fault(mm, addr, fsr, regs);
> +	}
>  }
>  
>  #ifdef CONFIG_MMU
> @@ -272,6 +272,8 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
>  	tsk = current;
>  	mm  = tsk->mm;
>  
> +	if (addr > TASK_SIZE && user_mode(regs))
> +		harden_branch_predictor();
>  	/* Enable interrupts if they were enabled in the parent context. */
>  	if (interrupts_enabled(regs))
>  		local_irq_enable();

Sebastian
diff mbox series

Patch

diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 58f69fa07df95..7adff8eb8f3d2 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -161,9 +161,6 @@  __do_user_fault(struct task_struct *tsk, unsigned long addr,
 		unsigned int fsr, unsigned int sig, int code,
 		struct pt_regs *regs)
 {
-	if (addr > TASK_SIZE)
-		harden_branch_predictor();
-
 #ifdef CONFIG_DEBUG_USER
 	if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||
 	    ((user_debug & UDBG_BUS)  && (sig == SIGBUS))) {
@@ -195,10 +192,13 @@  void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 	 * If we are in kernel mode at this point, we
 	 * have no context to handle this fault with.
 	 */
-	if (user_mode(regs))
+	if (user_mode(regs)) {
+		if (addr > TASK_SIZE)
+			harden_branch_predictor();
 		__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
-	else
+	} else {
 		__do_kernel_fault(mm, addr, fsr, regs);
+	}
 }
 
 #ifdef CONFIG_MMU
@@ -272,6 +272,8 @@  do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 	tsk = current;
 	mm  = tsk->mm;
 
+	if (addr > TASK_SIZE && user_mode(regs))
+		harden_branch_predictor();
 	/* Enable interrupts if they were enabled in the parent context. */
 	if (interrupts_enabled(regs))
 		local_irq_enable();