Commit 76e60d5c authored by Breno Leitao's avatar Breno Leitao Committed by Kleber Sacilotto de Souza

powerpc/ptrace: Mitigate potential Spectre v1

'regno' is directly controlled by user space, hence leading to a potential
exploitation of the Spectre variant 1 vulnerability.

On PTRACE_SETREGS and PTRACE_GETREGS requests, user space passes the
register number that would be read or written. This register number is
called 'regno' which is part of the 'addr' syscall parameter.

This 'regno' value is checked against the maximum pt_regs structure size,
and then used to dereference it, which matches the initial part of a
Spectre v1 (and Spectre v1.1) attack. The dereferenced value, then,
is returned to userspace in the GETREGS case.

This patch sanitizes 'regno' before using it to dereference pt_reg.

Notice that given that speculation windows are large, the policy is
to kill the speculation on the first load and not worry if it can be
completed with a dependent load/store [1].

[1] https://marc.info/?l=linux-kernel&m=152449131114778&w=2Signed-off-by: default avatarBreno Leitao <leitao@debian.org>
Acked-by: default avatarGustavo A. R. Silva <gustavo@embeddedor.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>

CVE-2017-5753

(backported from commit ebb0e13e)
[juergh: Adjusted context.]
Signed-off-by: default avatarJuerg Haefliger <juergh@canonical.com>
Acked-by: default avatarStefan Bader <stefan.bader@canonical.com>
Acked-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent ecf3b922
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/hw_breakpoint.h> #include <linux/hw_breakpoint.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/context_tracking.h> #include <linux/context_tracking.h>
#include <linux/nospec.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -220,6 +221,8 @@ static int set_user_trap(struct task_struct *task, unsigned long trap) ...@@ -220,6 +221,8 @@ static int set_user_trap(struct task_struct *task, unsigned long trap)
*/ */
int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data) int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data)
{ {
unsigned int regs_max;
if ((task->thread.regs == NULL) || !data) if ((task->thread.regs == NULL) || !data)
return -EIO; return -EIO;
...@@ -231,7 +234,9 @@ int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data) ...@@ -231,7 +234,9 @@ int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data)
if (regno == PT_DSCR) if (regno == PT_DSCR)
return get_user_dscr(task, data); return get_user_dscr(task, data);
if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) { regs_max = sizeof(struct pt_regs) / sizeof(unsigned long);
if (regno < regs_max) {
regno = array_index_nospec(regno, regs_max);
*data = ((unsigned long *)task->thread.regs)[regno]; *data = ((unsigned long *)task->thread.regs)[regno];
return 0; return 0;
} }
...@@ -255,6 +260,7 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) ...@@ -255,6 +260,7 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data)
return set_user_dscr(task, data); return set_user_dscr(task, data);
if (regno <= PT_MAX_PUT_REG) { if (regno <= PT_MAX_PUT_REG) {
regno = array_index_nospec(regno, PT_MAX_PUT_REG + 1);
((unsigned long *)task->thread.regs)[regno] = data; ((unsigned long *)task->thread.regs)[regno] = data;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment