diff options
Diffstat (limited to 'arch/x86/kernel/vsyscall_64.c')
-rw-r--r-- | arch/x86/kernel/vsyscall_64.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 7515cf0e1805..8d141b309046 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c | |||
@@ -18,6 +18,8 @@ | |||
18 | * use the vDSO. | 18 | * use the vDSO. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
22 | |||
21 | #include <linux/time.h> | 23 | #include <linux/time.h> |
22 | #include <linux/init.h> | 24 | #include <linux/init.h> |
23 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
@@ -111,18 +113,13 @@ void update_vsyscall(struct timespec *wall_time, struct timespec *wtm, | |||
111 | static void warn_bad_vsyscall(const char *level, struct pt_regs *regs, | 113 | static void warn_bad_vsyscall(const char *level, struct pt_regs *regs, |
112 | const char *message) | 114 | const char *message) |
113 | { | 115 | { |
114 | static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); | 116 | if (!show_unhandled_signals) |
115 | struct task_struct *tsk; | ||
116 | |||
117 | if (!show_unhandled_signals || !__ratelimit(&rs)) | ||
118 | return; | 117 | return; |
119 | 118 | ||
120 | tsk = current; | 119 | pr_notice_ratelimited("%s%s[%d] %s ip:%lx cs:%lx sp:%lx ax:%lx si:%lx di:%lx\n", |
121 | 120 | level, current->comm, task_pid_nr(current), | |
122 | printk("%s%s[%d] %s ip:%lx cs:%lx sp:%lx ax:%lx si:%lx di:%lx\n", | 121 | message, regs->ip, regs->cs, |
123 | level, tsk->comm, task_pid_nr(tsk), | 122 | regs->sp, regs->ax, regs->si, regs->di); |
124 | message, regs->ip, regs->cs, | ||
125 | regs->sp, regs->ax, regs->si, regs->di); | ||
126 | } | 123 | } |
127 | 124 | ||
128 | static int addr_to_vsyscall_nr(unsigned long addr) | 125 | static int addr_to_vsyscall_nr(unsigned long addr) |
@@ -139,6 +136,19 @@ static int addr_to_vsyscall_nr(unsigned long addr) | |||
139 | return nr; | 136 | return nr; |
140 | } | 137 | } |
141 | 138 | ||
139 | #ifdef CONFIG_SECCOMP | ||
140 | static int vsyscall_seccomp(struct task_struct *tsk, int syscall_nr) | ||
141 | { | ||
142 | if (!seccomp_mode(&tsk->seccomp)) | ||
143 | return 0; | ||
144 | task_pt_regs(tsk)->orig_ax = syscall_nr; | ||
145 | task_pt_regs(tsk)->ax = syscall_nr; | ||
146 | return __secure_computing(syscall_nr); | ||
147 | } | ||
148 | #else | ||
149 | #define vsyscall_seccomp(_tsk, _nr) 0 | ||
150 | #endif | ||
151 | |||
142 | static bool write_ok_or_segv(unsigned long ptr, size_t size) | 152 | static bool write_ok_or_segv(unsigned long ptr, size_t size) |
143 | { | 153 | { |
144 | /* | 154 | /* |
@@ -174,6 +184,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
174 | int vsyscall_nr; | 184 | int vsyscall_nr; |
175 | int prev_sig_on_uaccess_error; | 185 | int prev_sig_on_uaccess_error; |
176 | long ret; | 186 | long ret; |
187 | int skip; | ||
177 | 188 | ||
178 | /* | 189 | /* |
179 | * No point in checking CS -- the only way to get here is a user mode | 190 | * No point in checking CS -- the only way to get here is a user mode |
@@ -205,9 +216,6 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
205 | } | 216 | } |
206 | 217 | ||
207 | tsk = current; | 218 | tsk = current; |
208 | if (seccomp_mode(&tsk->seccomp)) | ||
209 | do_exit(SIGKILL); | ||
210 | |||
211 | /* | 219 | /* |
212 | * With a real vsyscall, page faults cause SIGSEGV. We want to | 220 | * With a real vsyscall, page faults cause SIGSEGV. We want to |
213 | * preserve that behavior to make writing exploits harder. | 221 | * preserve that behavior to make writing exploits harder. |
@@ -222,8 +230,13 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
222 | * address 0". | 230 | * address 0". |
223 | */ | 231 | */ |
224 | ret = -EFAULT; | 232 | ret = -EFAULT; |
233 | skip = 0; | ||
225 | switch (vsyscall_nr) { | 234 | switch (vsyscall_nr) { |
226 | case 0: | 235 | case 0: |
236 | skip = vsyscall_seccomp(tsk, __NR_gettimeofday); | ||
237 | if (skip) | ||
238 | break; | ||
239 | |||
227 | if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || | 240 | if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || |
228 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) | 241 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) |
229 | break; | 242 | break; |
@@ -234,6 +247,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
234 | break; | 247 | break; |
235 | 248 | ||
236 | case 1: | 249 | case 1: |
250 | skip = vsyscall_seccomp(tsk, __NR_time); | ||
251 | if (skip) | ||
252 | break; | ||
253 | |||
237 | if (!write_ok_or_segv(regs->di, sizeof(time_t))) | 254 | if (!write_ok_or_segv(regs->di, sizeof(time_t))) |
238 | break; | 255 | break; |
239 | 256 | ||
@@ -241,6 +258,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
241 | break; | 258 | break; |
242 | 259 | ||
243 | case 2: | 260 | case 2: |
261 | skip = vsyscall_seccomp(tsk, __NR_getcpu); | ||
262 | if (skip) | ||
263 | break; | ||
264 | |||
244 | if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || | 265 | if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || |
245 | !write_ok_or_segv(regs->si, sizeof(unsigned))) | 266 | !write_ok_or_segv(regs->si, sizeof(unsigned))) |
246 | break; | 267 | break; |
@@ -253,6 +274,12 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
253 | 274 | ||
254 | current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; | 275 | current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; |
255 | 276 | ||
277 | if (skip) { | ||
278 | if ((long)regs->ax <= 0L) /* seccomp errno emulation */ | ||
279 | goto do_ret; | ||
280 | goto done; /* seccomp trace/trap */ | ||
281 | } | ||
282 | |||
256 | if (ret == -EFAULT) { | 283 | if (ret == -EFAULT) { |
257 | /* Bad news -- userspace fed a bad pointer to a vsyscall. */ | 284 | /* Bad news -- userspace fed a bad pointer to a vsyscall. */ |
258 | warn_bad_vsyscall(KERN_INFO, regs, | 285 | warn_bad_vsyscall(KERN_INFO, regs, |
@@ -271,10 +298,11 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
271 | 298 | ||
272 | regs->ax = ret; | 299 | regs->ax = ret; |
273 | 300 | ||
301 | do_ret: | ||
274 | /* Emulate a ret instruction. */ | 302 | /* Emulate a ret instruction. */ |
275 | regs->ip = caller; | 303 | regs->ip = caller; |
276 | regs->sp += 8; | 304 | regs->sp += 8; |
277 | 305 | done: | |
278 | return true; | 306 | return true; |
279 | 307 | ||
280 | sigsegv: | 308 | sigsegv: |