.. | .. |
---|
42 | 42 | #define CREATE_TRACE_POINTS |
---|
43 | 43 | #include "vsyscall_trace.h" |
---|
44 | 44 | |
---|
45 | | -static enum { EMULATE, NONE } vsyscall_mode = |
---|
| 45 | +static enum { EMULATE, XONLY, NONE } vsyscall_mode __ro_after_init = |
---|
46 | 46 | #ifdef CONFIG_LEGACY_VSYSCALL_NONE |
---|
47 | 47 | NONE; |
---|
| 48 | +#elif defined(CONFIG_LEGACY_VSYSCALL_XONLY) |
---|
| 49 | + XONLY; |
---|
48 | 50 | #else |
---|
49 | 51 | EMULATE; |
---|
50 | 52 | #endif |
---|
.. | .. |
---|
54 | 56 | if (str) { |
---|
55 | 57 | if (!strcmp("emulate", str)) |
---|
56 | 58 | vsyscall_mode = EMULATE; |
---|
| 59 | + else if (!strcmp("xonly", str)) |
---|
| 60 | + vsyscall_mode = XONLY; |
---|
57 | 61 | else if (!strcmp("none", str)) |
---|
58 | 62 | vsyscall_mode = NONE; |
---|
59 | 63 | else |
---|
.. | .. |
---|
99 | 103 | * sig_on_uaccess_err, this could go away. |
---|
100 | 104 | */ |
---|
101 | 105 | |
---|
102 | | - if (!access_ok(VERIFY_WRITE, (void __user *)ptr, size)) { |
---|
103 | | - siginfo_t info; |
---|
| 106 | + if (!access_ok((void __user *)ptr, size)) { |
---|
104 | 107 | struct thread_struct *thread = ¤t->thread; |
---|
105 | 108 | |
---|
106 | | - thread->error_code = 6; /* user fault, no page, write */ |
---|
| 109 | + thread->error_code = X86_PF_USER | X86_PF_WRITE; |
---|
107 | 110 | thread->cr2 = ptr; |
---|
108 | 111 | thread->trap_nr = X86_TRAP_PF; |
---|
109 | 112 | |
---|
110 | | - clear_siginfo(&info); |
---|
111 | | - info.si_signo = SIGSEGV; |
---|
112 | | - info.si_errno = 0; |
---|
113 | | - info.si_code = SEGV_MAPERR; |
---|
114 | | - info.si_addr = (void __user *)ptr; |
---|
115 | | - |
---|
116 | | - force_sig_info(SIGSEGV, &info, current); |
---|
| 113 | + force_sig_fault(SIGSEGV, SEGV_MAPERR, (void __user *)ptr); |
---|
117 | 114 | return false; |
---|
118 | 115 | } else { |
---|
119 | 116 | return true; |
---|
120 | 117 | } |
---|
121 | 118 | } |
---|
122 | 119 | |
---|
123 | | -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) |
---|
124 | 122 | { |
---|
125 | 123 | struct task_struct *tsk; |
---|
126 | 124 | unsigned long caller; |
---|
.. | .. |
---|
128 | 126 | int prev_sig_on_uaccess_err; |
---|
129 | 127 | long ret; |
---|
130 | 128 | unsigned long orig_dx; |
---|
| 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 | + } |
---|
131 | 145 | |
---|
132 | 146 | /* |
---|
133 | 147 | * No point in checking CS -- the only way to get here is a user mode |
---|
.. | .. |
---|
170 | 184 | */ |
---|
171 | 185 | switch (vsyscall_nr) { |
---|
172 | 186 | case 0: |
---|
173 | | - if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || |
---|
| 187 | + if (!write_ok_or_segv(regs->di, sizeof(struct __kernel_old_timeval)) || |
---|
174 | 188 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) { |
---|
175 | 189 | ret = -EFAULT; |
---|
176 | 190 | goto check_fault; |
---|
.. | .. |
---|
180 | 194 | break; |
---|
181 | 195 | |
---|
182 | 196 | case 1: |
---|
183 | | - if (!write_ok_or_segv(regs->di, sizeof(time_t))) { |
---|
| 197 | + if (!write_ok_or_segv(regs->di, sizeof(__kernel_old_time_t))) { |
---|
184 | 198 | ret = -EFAULT; |
---|
185 | 199 | goto check_fault; |
---|
186 | 200 | } |
---|
.. | .. |
---|
208 | 222 | */ |
---|
209 | 223 | regs->orig_ax = syscall_nr; |
---|
210 | 224 | regs->ax = -ENOSYS; |
---|
211 | | - tmp = secure_computing(NULL); |
---|
| 225 | + tmp = secure_computing(); |
---|
212 | 226 | if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) { |
---|
213 | 227 | warn_bad_vsyscall(KERN_DEBUG, regs, |
---|
214 | 228 | "seccomp tried to change syscall nr or ip"); |
---|
.. | .. |
---|
275 | 289 | return true; |
---|
276 | 290 | |
---|
277 | 291 | sigsegv: |
---|
278 | | - force_sig(SIGSEGV, current); |
---|
| 292 | + force_sig(SIGSEGV); |
---|
279 | 293 | return true; |
---|
280 | 294 | } |
---|
281 | 295 | |
---|
.. | .. |
---|
291 | 305 | static const struct vm_operations_struct gate_vma_ops = { |
---|
292 | 306 | .name = gate_vma_name, |
---|
293 | 307 | }; |
---|
294 | | -static struct vm_area_struct gate_vma = { |
---|
| 308 | +static struct vm_area_struct gate_vma __ro_after_init = { |
---|
295 | 309 | .vm_start = VSYSCALL_ADDR, |
---|
296 | 310 | .vm_end = VSYSCALL_ADDR + PAGE_SIZE, |
---|
297 | 311 | .vm_page_prot = PAGE_READONLY_EXEC, |
---|
.. | .. |
---|
364 | 378 | extern char __vsyscall_page; |
---|
365 | 379 | unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page); |
---|
366 | 380 | |
---|
367 | | - if (vsyscall_mode != NONE) { |
---|
| 381 | + /* |
---|
| 382 | + * For full emulation, the page needs to exist for real. In |
---|
| 383 | + * execute-only mode, there is no PTE at all backing the vsyscall |
---|
| 384 | + * page. |
---|
| 385 | + */ |
---|
| 386 | + if (vsyscall_mode == EMULATE) { |
---|
368 | 387 | __set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall, |
---|
369 | 388 | PAGE_KERNEL_VVAR); |
---|
370 | 389 | set_vsyscall_pgtable_user_bits(swapper_pg_dir); |
---|
371 | 390 | } |
---|
372 | 391 | |
---|
| 392 | + if (vsyscall_mode == XONLY) |
---|
| 393 | + gate_vma.vm_flags = VM_EXEC; |
---|
| 394 | + |
---|
373 | 395 | BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) != |
---|
374 | 396 | (unsigned long)VSYSCALL_ADDR); |
---|
375 | 397 | } |
---|