diff options
author | Andy Lutomirski <luto@kernel.org> | 2019-06-27 00:45:04 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2019-06-27 18:04:39 -0400 |
commit | 918ce325098a4eef99daad7b6796da33cebaf03a (patch) | |
tree | 350e3c5a1bae0686cb0050a466b85917792e4b7f | |
parent | bd49e16e3339f052fae05fb3e955c5db0c9c6445 (diff) |
x86/vsyscall: Show something useful on a read fault
Just segfaulting the application when it tries to read the vsyscall page in
xonly mode is not helpful for those who need to debug it.
Emit a hint.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Jann Horn <jannh@google.com>
Link: https://lkml.kernel.org/r/8016afffe0eab497be32017ad7f6f7030dc3ba66.1561610354.git.luto@kernel.org
-rw-r--r-- | arch/x86/entry/vsyscall/vsyscall_64.c | 19 | ||||
-rw-r--r-- | arch/x86/include/asm/vsyscall.h | 6 | ||||
-rw-r--r-- | arch/x86/mm/fault.c | 11 |
3 files changed, 27 insertions, 9 deletions
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c index fedd7628f3a6..9c58ab807aeb 100644 --- a/arch/x86/entry/vsyscall/vsyscall_64.c +++ b/arch/x86/entry/vsyscall/vsyscall_64.c | |||
@@ -117,7 +117,8 @@ static bool write_ok_or_segv(unsigned long ptr, size_t size) | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | 120 | bool emulate_vsyscall(unsigned long error_code, |
121 | struct pt_regs *regs, unsigned long address) | ||
121 | { | 122 | { |
122 | struct task_struct *tsk; | 123 | struct task_struct *tsk; |
123 | unsigned long caller; | 124 | unsigned long caller; |
@@ -126,6 +127,22 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
126 | long ret; | 127 | long ret; |
127 | unsigned long orig_dx; | 128 | unsigned long orig_dx; |
128 | 129 | ||
130 | /* Write faults or kernel-privilege faults never get fixed up. */ | ||
131 | if ((error_code & (X86_PF_WRITE | X86_PF_USER)) != X86_PF_USER) | ||
132 | return false; | ||
133 | |||
134 | if (!(error_code & X86_PF_INSTR)) { | ||
135 | /* Failed vsyscall read */ | ||
136 | if (vsyscall_mode == EMULATE) | ||
137 | return false; | ||
138 | |||
139 | /* | ||
140 | * User code tried and failed to read the vsyscall page. | ||
141 | */ | ||
142 | warn_bad_vsyscall(KERN_INFO, regs, "vsyscall read attempt denied -- look up the vsyscall kernel parameter if you need a workaround"); | ||
143 | return false; | ||
144 | } | ||
145 | |||
129 | /* | 146 | /* |
130 | * No point in checking CS -- the only way to get here is a user mode | 147 | * No point in checking CS -- the only way to get here is a user mode |
131 | * trap to a high address, which means that we're in 64-bit user code. | 148 | * trap to a high address, which means that we're in 64-bit user code. |
diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h index b986b2ca688a..ab60a71a8dcb 100644 --- a/arch/x86/include/asm/vsyscall.h +++ b/arch/x86/include/asm/vsyscall.h | |||
@@ -13,10 +13,12 @@ extern void set_vsyscall_pgtable_user_bits(pgd_t *root); | |||
13 | * Called on instruction fetch fault in vsyscall page. | 13 | * Called on instruction fetch fault in vsyscall page. |
14 | * Returns true if handled. | 14 | * Returns true if handled. |
15 | */ | 15 | */ |
16 | extern bool emulate_vsyscall(struct pt_regs *regs, unsigned long address); | 16 | extern bool emulate_vsyscall(unsigned long error_code, |
17 | struct pt_regs *regs, unsigned long address); | ||
17 | #else | 18 | #else |
18 | static inline void map_vsyscall(void) {} | 19 | static inline void map_vsyscall(void) {} |
19 | static inline bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | 20 | static inline bool emulate_vsyscall(unsigned long error_code, |
21 | struct pt_regs *regs, unsigned long address) | ||
20 | { | 22 | { |
21 | return false; | 23 | return false; |
22 | } | 24 | } |
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 46df4c6aae46..288a5462076f 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -1369,16 +1369,15 @@ void do_user_addr_fault(struct pt_regs *regs, | |||
1369 | 1369 | ||
1370 | #ifdef CONFIG_X86_64 | 1370 | #ifdef CONFIG_X86_64 |
1371 | /* | 1371 | /* |
1372 | * Instruction fetch faults in the vsyscall page might need | 1372 | * Faults in the vsyscall page might need emulation. The |
1373 | * emulation. The vsyscall page is at a high address | 1373 | * vsyscall page is at a high address (>PAGE_OFFSET), but is |
1374 | * (>PAGE_OFFSET), but is considered to be part of the user | 1374 | * considered to be part of the user address space. |
1375 | * address space. | ||
1376 | * | 1375 | * |
1377 | * The vsyscall page does not have a "real" VMA, so do this | 1376 | * The vsyscall page does not have a "real" VMA, so do this |
1378 | * emulation before we go searching for VMAs. | 1377 | * emulation before we go searching for VMAs. |
1379 | */ | 1378 | */ |
1380 | if ((hw_error_code & X86_PF_INSTR) && is_vsyscall_vaddr(address)) { | 1379 | if (is_vsyscall_vaddr(address)) { |
1381 | if (emulate_vsyscall(regs, address)) | 1380 | if (emulate_vsyscall(hw_error_code, regs, address)) |
1382 | return; | 1381 | return; |
1383 | } | 1382 | } |
1384 | #endif | 1383 | #endif |