diff options
author | James Morse <james.morse@arm.com> | 2016-06-20 13:28:01 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-07-07 10:55:37 -0400 |
commit | e19a6ee2460bdd0d0055a6029383422773f9999a (patch) | |
tree | f8e7898101568c82617d7afd66e712b9f0538a0e | |
parent | 4c2e07c6a29e0129e975727b9f57eede813eea85 (diff) |
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>
-rw-r--r-- | arch/arm64/include/asm/ptrace.h | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/asm-offsets.c | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/entry.S | 19 | ||||
-rw-r--r-- | arch/arm64/mm/fault.c | 3 |
4 files changed, 22 insertions, 3 deletions
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index a307eb6e7fa8..7f94755089e2 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h | |||
@@ -117,6 +117,8 @@ struct pt_regs { | |||
117 | }; | 117 | }; |
118 | u64 orig_x0; | 118 | u64 orig_x0; |
119 | u64 syscallno; | 119 | u64 syscallno; |
120 | u64 orig_addr_limit; | ||
121 | u64 unused; // maintain 16 byte alignment | ||
120 | }; | 122 | }; |
121 | 123 | ||
122 | #define arch_has_single_step() (1) | 124 | #define arch_has_single_step() (1) |
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index f8e5d47f0880..2f4ba774488a 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c | |||
@@ -60,6 +60,7 @@ int main(void) | |||
60 | DEFINE(S_PC, offsetof(struct pt_regs, pc)); | 60 | DEFINE(S_PC, offsetof(struct pt_regs, pc)); |
61 | DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0)); | 61 | DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0)); |
62 | DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno)); | 62 | DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno)); |
63 | DEFINE(S_ORIG_ADDR_LIMIT, offsetof(struct pt_regs, orig_addr_limit)); | ||
63 | DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs)); | 64 | DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs)); |
64 | BLANK(); | 65 | BLANK(); |
65 | DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id.counter)); | 66 | DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id.counter)); |
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 12e8d2bcb3f9..6c3b7345a6c4 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <asm/errno.h> | 28 | #include <asm/errno.h> |
29 | #include <asm/esr.h> | 29 | #include <asm/esr.h> |
30 | #include <asm/irq.h> | 30 | #include <asm/irq.h> |
31 | #include <asm/memory.h> | ||
31 | #include <asm/thread_info.h> | 32 | #include <asm/thread_info.h> |
32 | #include <asm/unistd.h> | 33 | #include <asm/unistd.h> |
33 | 34 | ||
@@ -97,7 +98,14 @@ | |||
97 | mov x29, xzr // fp pointed to user-space | 98 | mov x29, xzr // fp pointed to user-space |
98 | .else | 99 | .else |
99 | add x21, sp, #S_FRAME_SIZE | 100 | add x21, sp, #S_FRAME_SIZE |
100 | .endif | 101 | get_thread_info tsk |
102 | /* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */ | ||
103 | ldr x20, [tsk, #TI_ADDR_LIMIT] | ||
104 | str x20, [sp, #S_ORIG_ADDR_LIMIT] | ||
105 | mov x20, #TASK_SIZE_64 | ||
106 | str x20, [tsk, #TI_ADDR_LIMIT] | ||
107 | ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO) | ||
108 | .endif /* \el == 0 */ | ||
101 | mrs x22, elr_el1 | 109 | mrs x22, elr_el1 |
102 | mrs x23, spsr_el1 | 110 | mrs x23, spsr_el1 |
103 | stp lr, x21, [sp, #S_LR] | 111 | stp lr, x21, [sp, #S_LR] |
@@ -128,6 +136,14 @@ | |||
128 | .endm | 136 | .endm |
129 | 137 | ||
130 | .macro kernel_exit, el | 138 | .macro kernel_exit, el |
139 | .if \el != 0 | ||
140 | /* Restore the task's original addr_limit. */ | ||
141 | ldr x20, [sp, #S_ORIG_ADDR_LIMIT] | ||
142 | str x20, [tsk, #TI_ADDR_LIMIT] | ||
143 | |||
144 | /* No need to restore UAO, it will be restored from SPSR_EL1 */ | ||
145 | .endif | ||
146 | |||
131 | ldp x21, x22, [sp, #S_PC] // load ELR, SPSR | 147 | ldp x21, x22, [sp, #S_PC] // load ELR, SPSR |
132 | .if \el == 0 | 148 | .if \el == 0 |
133 | ct_user_enter | 149 | ct_user_enter |
@@ -406,7 +422,6 @@ el1_irq: | |||
406 | bl trace_hardirqs_off | 422 | bl trace_hardirqs_off |
407 | #endif | 423 | #endif |
408 | 424 | ||
409 | get_thread_info tsk | ||
410 | irq_handler | 425 | irq_handler |
411 | 426 | ||
412 | #ifdef CONFIG_PREEMPT | 427 | #ifdef CONFIG_PREEMPT |
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 013e2cbe7924..b1166d1e5955 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -280,7 +280,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, | |||
280 | } | 280 | } |
281 | 281 | ||
282 | if (permission_fault(esr) && (addr < USER_DS)) { | 282 | if (permission_fault(esr) && (addr < USER_DS)) { |
283 | if (get_fs() == KERNEL_DS) | 283 | /* regs->orig_addr_limit may be 0 if we entered from EL0 */ |
284 | if (regs->orig_addr_limit == KERNEL_DS) | ||
284 | die("Accessing user space memory with fs=KERNEL_DS", regs, esr); | 285 | die("Accessing user space memory with fs=KERNEL_DS", regs, esr); |
285 | 286 | ||
286 | if (!search_exception_tables(regs->pc)) | 287 | if (!search_exception_tables(regs->pc)) |