diff options
| -rw-r--r-- | arch/x86/kernel/vsyscall_64.c | 35 |
1 files changed, 31 insertions, 4 deletions
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 7515cf0e1805..08a18d0dcc5a 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c | |||
| @@ -139,6 +139,15 @@ static int addr_to_vsyscall_nr(unsigned long addr) | |||
| 139 | return nr; | 139 | return nr; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | static int vsyscall_seccomp(struct task_struct *tsk, int syscall_nr) | ||
| 143 | { | ||
| 144 | if (!seccomp_mode(&tsk->seccomp)) | ||
| 145 | return 0; | ||
| 146 | task_pt_regs(tsk)->orig_ax = syscall_nr; | ||
| 147 | task_pt_regs(tsk)->ax = syscall_nr; | ||
| 148 | return __secure_computing(syscall_nr); | ||
| 149 | } | ||
| 150 | |||
| 142 | static bool write_ok_or_segv(unsigned long ptr, size_t size) | 151 | static bool write_ok_or_segv(unsigned long ptr, size_t size) |
| 143 | { | 152 | { |
| 144 | /* | 153 | /* |
| @@ -174,6 +183,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 174 | int vsyscall_nr; | 183 | int vsyscall_nr; |
| 175 | int prev_sig_on_uaccess_error; | 184 | int prev_sig_on_uaccess_error; |
| 176 | long ret; | 185 | long ret; |
| 186 | int skip; | ||
| 177 | 187 | ||
| 178 | /* | 188 | /* |
| 179 | * No point in checking CS -- the only way to get here is a user mode | 189 | * No point in checking CS -- the only way to get here is a user mode |
| @@ -205,9 +215,6 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 205 | } | 215 | } |
| 206 | 216 | ||
| 207 | tsk = current; | 217 | tsk = current; |
| 208 | if (seccomp_mode(&tsk->seccomp)) | ||
| 209 | do_exit(SIGKILL); | ||
| 210 | |||
| 211 | /* | 218 | /* |
| 212 | * With a real vsyscall, page faults cause SIGSEGV. We want to | 219 | * With a real vsyscall, page faults cause SIGSEGV. We want to |
| 213 | * preserve that behavior to make writing exploits harder. | 220 | * preserve that behavior to make writing exploits harder. |
| @@ -222,8 +229,13 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 222 | * address 0". | 229 | * address 0". |
| 223 | */ | 230 | */ |
| 224 | ret = -EFAULT; | 231 | ret = -EFAULT; |
| 232 | skip = 0; | ||
| 225 | switch (vsyscall_nr) { | 233 | switch (vsyscall_nr) { |
| 226 | case 0: | 234 | case 0: |
| 235 | skip = vsyscall_seccomp(tsk, __NR_gettimeofday); | ||
| 236 | if (skip) | ||
| 237 | break; | ||
| 238 | |||
| 227 | if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || | 239 | if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || |
| 228 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) | 240 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) |
| 229 | break; | 241 | break; |
| @@ -234,6 +246,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 234 | break; | 246 | break; |
| 235 | 247 | ||
| 236 | case 1: | 248 | case 1: |
| 249 | skip = vsyscall_seccomp(tsk, __NR_time); | ||
| 250 | if (skip) | ||
| 251 | break; | ||
| 252 | |||
| 237 | if (!write_ok_or_segv(regs->di, sizeof(time_t))) | 253 | if (!write_ok_or_segv(regs->di, sizeof(time_t))) |
| 238 | break; | 254 | break; |
| 239 | 255 | ||
| @@ -241,6 +257,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 241 | break; | 257 | break; |
| 242 | 258 | ||
| 243 | case 2: | 259 | case 2: |
| 260 | skip = vsyscall_seccomp(tsk, __NR_getcpu); | ||
| 261 | if (skip) | ||
| 262 | break; | ||
| 263 | |||
| 244 | if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || | 264 | if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || |
| 245 | !write_ok_or_segv(regs->si, sizeof(unsigned))) | 265 | !write_ok_or_segv(regs->si, sizeof(unsigned))) |
| 246 | break; | 266 | break; |
| @@ -253,6 +273,12 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 253 | 273 | ||
| 254 | current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; | 274 | current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; |
| 255 | 275 | ||
| 276 | if (skip) { | ||
| 277 | if ((long)regs->ax <= 0L) /* seccomp errno emulation */ | ||
| 278 | goto do_ret; | ||
| 279 | goto done; /* seccomp trace/trap */ | ||
| 280 | } | ||
| 281 | |||
| 256 | if (ret == -EFAULT) { | 282 | if (ret == -EFAULT) { |
| 257 | /* Bad news -- userspace fed a bad pointer to a vsyscall. */ | 283 | /* Bad news -- userspace fed a bad pointer to a vsyscall. */ |
| 258 | warn_bad_vsyscall(KERN_INFO, regs, | 284 | warn_bad_vsyscall(KERN_INFO, regs, |
| @@ -271,10 +297,11 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
| 271 | 297 | ||
| 272 | regs->ax = ret; | 298 | regs->ax = ret; |
| 273 | 299 | ||
| 300 | do_ret: | ||
| 274 | /* Emulate a ret instruction. */ | 301 | /* Emulate a ret instruction. */ |
| 275 | regs->ip = caller; | 302 | regs->ip = caller; |
| 276 | regs->sp += 8; | 303 | regs->sp += 8; |
| 277 | 304 | done: | |
| 278 | return true; | 305 | return true; |
| 279 | 306 | ||
| 280 | sigsegv: | 307 | sigsegv: |
