| .. | .. |
|---|
| 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 | } |
|---|