aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/vsyscall_64.c
diff options
context:
space:
mode:
authorWill Drewry <wad@chromium.org>2012-07-13 13:06:35 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-13 17:25:55 -0400
commit5651721edec25bf73cee060150e684044eac42dc (patch)
treedc43e7c23455cd61fa2bafad7faffe45b44d6885 /arch/x86/kernel/vsyscall_64.c
parentac7d181e323a888015e19e2e4d776095a0433787 (diff)
x86/vsyscall: allow seccomp filter in vsyscall=emulate
If a seccomp filter program is installed, older static binaries and distributions with older libc implementations (glibc 2.13 and earlier) that rely on vsyscall use will be terminated regardless of the filter program policy when executing time, gettimeofday, or getcpu. This is only the case when vsyscall emulation is in use (vsyscall=emulate is the default). This patch emulates system call entry inside a vsyscall=emulate by populating regs->ax and regs->orig_ax with the system call number prior to calling into seccomp such that all seccomp-dependencies function normally. Additionally, system call return behavior is emulated in line with other vsyscall entrypoints for the trace/trap cases. [ v2: fixed ip and sp on SECCOMP_RET_TRAP/TRACE (thanks to luto@mit.edu) ] Reported-and-tested-by: Owen Kibel <qmewlo@gmail.com> Signed-off-by: Will Drewry <wad@chromium.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/x86/kernel/vsyscall_64.c')
-rw-r--r--arch/x86/kernel/vsyscall_64.c35
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
142static 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
142static bool write_ok_or_segv(unsigned long ptr, size_t size) 151static 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
300do_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 304done:
278 return true; 305 return true;
279 306
280sigsegv: 307sigsegv: