diff options
author | Mark Rutland <mark.rutland@arm.com> | 2016-06-13 06:15:14 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-06-14 10:02:33 -0400 |
commit | c5cea06be060f38e5400d796e61cfc8c36e52924 (patch) | |
tree | 6674a1d439e46f034d5f26b86bef4998076714fa | |
parent | 5edb56491d4812c42175980759da53388e5d86f5 (diff) |
arm64: fix dump_instr when PAN and UAO are in use
If the kernel is set to show unhandled signals, and a user task does not
handle a SIGILL as a result of an instruction abort, we will attempt to
log the offending instruction with dump_instr before killing the task.
We use dump_instr to log the encoding of the offending userspace
instruction. However, dump_instr is also used to dump instructions from
kernel space, and internally always switches to KERNEL_DS before dumping
the instruction with get_user. When both PAN and UAO are in use, reading
a user instruction via get_user while in KERNEL_DS will result in a
permission fault, which leads to an Oops.
As we have regs corresponding to the context of the original instruction
abort, we can inspect this and only flip to KERNEL_DS if the original
abort was taken from the kernel, avoiding this issue. At the same time,
remove the redundant (and incorrect) comments regarding the order
dump_mem and dump_instr are called in.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: <stable@vger.kernel.org> #4.6+
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reported-by: Vladimir Murzin <vladimir.murzin@arm.com>
Tested-by: Vladimir Murzin <vladimir.murzin@arm.com>
Fixes: 57f4959bad0a154a ("arm64: kernel: Add support for User Access Override")
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm64/kernel/traps.c | 26 |
1 files changed, 13 insertions, 13 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index f7cf463107df..2a43012616b7 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c | |||
@@ -64,8 +64,7 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom, | |||
64 | 64 | ||
65 | /* | 65 | /* |
66 | * We need to switch to kernel mode so that we can use __get_user | 66 | * We need to switch to kernel mode so that we can use __get_user |
67 | * to safely read from kernel space. Note that we now dump the | 67 | * to safely read from kernel space. |
68 | * code first, just in case the backtrace kills us. | ||
69 | */ | 68 | */ |
70 | fs = get_fs(); | 69 | fs = get_fs(); |
71 | set_fs(KERNEL_DS); | 70 | set_fs(KERNEL_DS); |
@@ -111,21 +110,12 @@ static void dump_backtrace_entry(unsigned long where) | |||
111 | print_ip_sym(where); | 110 | print_ip_sym(where); |
112 | } | 111 | } |
113 | 112 | ||
114 | static void dump_instr(const char *lvl, struct pt_regs *regs) | 113 | static void __dump_instr(const char *lvl, struct pt_regs *regs) |
115 | { | 114 | { |
116 | unsigned long addr = instruction_pointer(regs); | 115 | unsigned long addr = instruction_pointer(regs); |
117 | mm_segment_t fs; | ||
118 | char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; | 116 | char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; |
119 | int i; | 117 | int i; |
120 | 118 | ||
121 | /* | ||
122 | * We need to switch to kernel mode so that we can use __get_user | ||
123 | * to safely read from kernel space. Note that we now dump the | ||
124 | * code first, just in case the backtrace kills us. | ||
125 | */ | ||
126 | fs = get_fs(); | ||
127 | set_fs(KERNEL_DS); | ||
128 | |||
129 | for (i = -4; i < 1; i++) { | 119 | for (i = -4; i < 1; i++) { |
130 | unsigned int val, bad; | 120 | unsigned int val, bad; |
131 | 121 | ||
@@ -139,8 +129,18 @@ static void dump_instr(const char *lvl, struct pt_regs *regs) | |||
139 | } | 129 | } |
140 | } | 130 | } |
141 | printk("%sCode: %s\n", lvl, str); | 131 | printk("%sCode: %s\n", lvl, str); |
132 | } | ||
142 | 133 | ||
143 | set_fs(fs); | 134 | static void dump_instr(const char *lvl, struct pt_regs *regs) |
135 | { | ||
136 | if (!user_mode(regs)) { | ||
137 | mm_segment_t fs = get_fs(); | ||
138 | set_fs(KERNEL_DS); | ||
139 | __dump_instr(lvl, regs); | ||
140 | set_fs(fs); | ||
141 | } else { | ||
142 | __dump_instr(lvl, regs); | ||
143 | } | ||
144 | } | 144 | } |
145 | 145 | ||
146 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | 146 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |