arm64: kernel: Save and restore UAO and addr_limit on exception entry
If we take an exception while at EL1, the exception handler inherits the original context's addr_limit and PSTATE.UAO values. To be consistent always reset addr_limit and PSTATE.UAO on (re-)entry to EL1. This prevents accidental re-use of the original context's addr_limit. Based on a similar patch for arm from Russell King. Cc: <stable@vger.kernel.org> # 4.6- Acked-by: Will Deacon <will.deacon@arm.com> Reviewed-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: James Morse <james.morse@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
parent
4c2e07c6a2
commit
e19a6ee246
@ -117,6 +117,8 @@ struct pt_regs {
|
|||||||
};
|
};
|
||||||
u64 orig_x0;
|
u64 orig_x0;
|
||||||
u64 syscallno;
|
u64 syscallno;
|
||||||
|
u64 orig_addr_limit;
|
||||||
|
u64 unused; // maintain 16 byte alignment
|
||||||
};
|
};
|
||||||
|
|
||||||
#define arch_has_single_step() (1)
|
#define arch_has_single_step() (1)
|
||||||
|
@ -60,6 +60,7 @@ int main(void)
|
|||||||
DEFINE(S_PC, offsetof(struct pt_regs, pc));
|
DEFINE(S_PC, offsetof(struct pt_regs, pc));
|
||||||
DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0));
|
DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0));
|
||||||
DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno));
|
DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno));
|
||||||
|
DEFINE(S_ORIG_ADDR_LIMIT, offsetof(struct pt_regs, orig_addr_limit));
|
||||||
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
|
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
|
||||||
BLANK();
|
BLANK();
|
||||||
DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id.counter));
|
DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id.counter));
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <asm/errno.h>
|
#include <asm/errno.h>
|
||||||
#include <asm/esr.h>
|
#include <asm/esr.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
#include <asm/memory.h>
|
||||||
#include <asm/thread_info.h>
|
#include <asm/thread_info.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
@ -97,7 +98,14 @@
|
|||||||
mov x29, xzr // fp pointed to user-space
|
mov x29, xzr // fp pointed to user-space
|
||||||
.else
|
.else
|
||||||
add x21, sp, #S_FRAME_SIZE
|
add x21, sp, #S_FRAME_SIZE
|
||||||
.endif
|
get_thread_info tsk
|
||||||
|
/* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */
|
||||||
|
ldr x20, [tsk, #TI_ADDR_LIMIT]
|
||||||
|
str x20, [sp, #S_ORIG_ADDR_LIMIT]
|
||||||
|
mov x20, #TASK_SIZE_64
|
||||||
|
str x20, [tsk, #TI_ADDR_LIMIT]
|
||||||
|
ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO)
|
||||||
|
.endif /* \el == 0 */
|
||||||
mrs x22, elr_el1
|
mrs x22, elr_el1
|
||||||
mrs x23, spsr_el1
|
mrs x23, spsr_el1
|
||||||
stp lr, x21, [sp, #S_LR]
|
stp lr, x21, [sp, #S_LR]
|
||||||
@ -128,6 +136,14 @@
|
|||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro kernel_exit, el
|
.macro kernel_exit, el
|
||||||
|
.if \el != 0
|
||||||
|
/* Restore the task's original addr_limit. */
|
||||||
|
ldr x20, [sp, #S_ORIG_ADDR_LIMIT]
|
||||||
|
str x20, [tsk, #TI_ADDR_LIMIT]
|
||||||
|
|
||||||
|
/* No need to restore UAO, it will be restored from SPSR_EL1 */
|
||||||
|
.endif
|
||||||
|
|
||||||
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
|
||||||
.if \el == 0
|
.if \el == 0
|
||||||
ct_user_enter
|
ct_user_enter
|
||||||
@ -406,7 +422,6 @@ el1_irq:
|
|||||||
bl trace_hardirqs_off
|
bl trace_hardirqs_off
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
get_thread_info tsk
|
|
||||||
irq_handler
|
irq_handler
|
||||||
|
|
||||||
#ifdef CONFIG_PREEMPT
|
#ifdef CONFIG_PREEMPT
|
||||||
|
@ -280,7 +280,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (permission_fault(esr) && (addr < USER_DS)) {
|
if (permission_fault(esr) && (addr < USER_DS)) {
|
||||||
if (get_fs() == KERNEL_DS)
|
/* regs->orig_addr_limit may be 0 if we entered from EL0 */
|
||||||
|
if (regs->orig_addr_limit == KERNEL_DS)
|
||||||
die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
|
die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
|
||||||
|
|
||||||
if (!search_exception_tables(regs->pc))
|
if (!search_exception_tables(regs->pc))
|
||||||
|
Loading…
Reference in New Issue
Block a user