aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@mit.edu>2011-07-13 09:24:09 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2011-07-13 14:22:55 -0400
commitc9712944b2a12373cb6ff8059afcfb7e826a6c54 (patch)
tree73b58eddce6f56c32b90b5056032a504f3ae4f00
parent5cec93c216db77c45f7ce970d46283bcb1933884 (diff)
x86-64: Improve vsyscall emulation CS and RIP handling
Three fixes here: - Send SIGSEGV if called from compat code or with a funny CS. - Don't BUG on impossible addresses. - Add a missing local_irq_disable. This patch also removes an unused variable. Signed-off-by: Andy Lutomirski <luto@mit.edu> Link: http://lkml.kernel.org/r/6fb2b13ab39b743d1e4f466eef13425854912f7f.1310563276.git.luto@mit.edu Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/include/asm/vsyscall.h12
-rw-r--r--arch/x86/kernel/vsyscall_64.c61
2 files changed, 41 insertions, 32 deletions
diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
index bb710cb0cdc1..d55597351f6a 100644
--- a/arch/x86/include/asm/vsyscall.h
+++ b/arch/x86/include/asm/vsyscall.h
@@ -31,18 +31,6 @@ extern struct timezone sys_tz;
31 31
32extern void map_vsyscall(void); 32extern void map_vsyscall(void);
33 33
34/* Emulation */
35
36static inline bool is_vsyscall_entry(unsigned long addr)
37{
38 return (addr & ~0xC00UL) == VSYSCALL_START;
39}
40
41static inline int vsyscall_entry_nr(unsigned long addr)
42{
43 return (addr & 0xC00UL) >> 10;
44}
45
46#endif /* __KERNEL__ */ 34#endif /* __KERNEL__ */
47 35
48#endif /* _ASM_X86_VSYSCALL_H */ 36#endif /* _ASM_X86_VSYSCALL_H */
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index 10cd8ac3395a..a262400c3479 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -38,6 +38,7 @@
38 38
39#include <asm/vsyscall.h> 39#include <asm/vsyscall.h>
40#include <asm/pgtable.h> 40#include <asm/pgtable.h>
41#include <asm/compat.h>
41#include <asm/page.h> 42#include <asm/page.h>
42#include <asm/unistd.h> 43#include <asm/unistd.h>
43#include <asm/fixmap.h> 44#include <asm/fixmap.h>
@@ -97,33 +98,63 @@ static void warn_bad_vsyscall(const char *level, struct pt_regs *regs,
97 98
98 tsk = current; 99 tsk = current;
99 100
100 printk("%s%s[%d] %s ip:%lx sp:%lx ax:%lx si:%lx di:%lx\n", 101 printk("%s%s[%d] %s ip:%lx cs:%lx sp:%lx ax:%lx si:%lx di:%lx\n",
101 level, tsk->comm, task_pid_nr(tsk), 102 level, tsk->comm, task_pid_nr(tsk),
102 message, regs->ip - 2, regs->sp, regs->ax, regs->si, regs->di); 103 message, regs->ip - 2, regs->cs,
104 regs->sp, regs->ax, regs->si, regs->di);
105}
106
107static int addr_to_vsyscall_nr(unsigned long addr)
108{
109 int nr;
110
111 if ((addr & ~0xC00UL) != VSYSCALL_START)
112 return -EINVAL;
113
114 nr = (addr & 0xC00UL) >> 10;
115 if (nr >= 3)
116 return -EINVAL;
117
118 return nr;
103} 119}
104 120
105void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code) 121void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code)
106{ 122{
107 const char *vsyscall_name;
108 struct task_struct *tsk; 123 struct task_struct *tsk;
109 unsigned long caller; 124 unsigned long caller;
110 int vsyscall_nr; 125 int vsyscall_nr;
111 long ret; 126 long ret;
112 127
113 /* Kernel code must never get here. */
114 BUG_ON(!user_mode(regs));
115
116 local_irq_enable(); 128 local_irq_enable();
117 129
118 /* 130 /*
131 * Real 64-bit user mode code has cs == __USER_CS. Anything else
132 * is bogus.
133 */
134 if (regs->cs != __USER_CS) {
135 /*
136 * If we trapped from kernel mode, we might as well OOPS now
137 * instead of returning to some random address and OOPSing
138 * then.
139 */
140 BUG_ON(!user_mode(regs));
141
142 /* Compat mode and non-compat 32-bit CS should both segfault. */
143 warn_bad_vsyscall(KERN_WARNING, regs,
144 "illegal int 0xcc from 32-bit mode");
145 goto sigsegv;
146 }
147
148 /*
119 * x86-ism here: regs->ip points to the instruction after the int 0xcc, 149 * x86-ism here: regs->ip points to the instruction after the int 0xcc,
120 * and int 0xcc is two bytes long. 150 * and int 0xcc is two bytes long.
121 */ 151 */
122 if (!is_vsyscall_entry(regs->ip - 2)) { 152 vsyscall_nr = addr_to_vsyscall_nr(regs->ip - 2);
123 warn_bad_vsyscall(KERN_WARNING, regs, "illegal int 0xcc (exploit attempt?)"); 153 if (vsyscall_nr < 0) {
154 warn_bad_vsyscall(KERN_WARNING, regs,
155 "illegal int 0xcc (exploit attempt?)");
124 goto sigsegv; 156 goto sigsegv;
125 } 157 }
126 vsyscall_nr = vsyscall_entry_nr(regs->ip - 2);
127 158
128 if (get_user(caller, (unsigned long __user *)regs->sp) != 0) { 159 if (get_user(caller, (unsigned long __user *)regs->sp) != 0) {
129 warn_bad_vsyscall(KERN_WARNING, regs, "int 0xcc with bad stack (exploit attempt?)"); 160 warn_bad_vsyscall(KERN_WARNING, regs, "int 0xcc with bad stack (exploit attempt?)");
@@ -136,31 +167,20 @@ void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code)
136 167
137 switch (vsyscall_nr) { 168 switch (vsyscall_nr) {
138 case 0: 169 case 0:
139 vsyscall_name = "gettimeofday";
140 ret = sys_gettimeofday( 170 ret = sys_gettimeofday(
141 (struct timeval __user *)regs->di, 171 (struct timeval __user *)regs->di,
142 (struct timezone __user *)regs->si); 172 (struct timezone __user *)regs->si);
143 break; 173 break;
144 174
145 case 1: 175 case 1:
146 vsyscall_name = "time";
147 ret = sys_time((time_t __user *)regs->di); 176 ret = sys_time((time_t __user *)regs->di);
148 break; 177 break;
149 178
150 case 2: 179 case 2:
151 vsyscall_name = "getcpu";
152 ret = sys_getcpu((unsigned __user *)regs->di, 180 ret = sys_getcpu((unsigned __user *)regs->di,
153 (unsigned __user *)regs->si, 181 (unsigned __user *)regs->si,
154 0); 182 0);
155 break; 183 break;
156
157 default:
158 /*
159 * If we get here, then vsyscall_nr indicates that int 0xcc
160 * happened at an address in the vsyscall page that doesn't
161 * contain int 0xcc. That can't happen.
162 */
163 BUG();
164 } 184 }
165 185
166 if (ret == -EFAULT) { 186 if (ret == -EFAULT) {
@@ -188,6 +208,7 @@ void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code)
188sigsegv: 208sigsegv:
189 regs->ip -= 2; /* The faulting instruction should be the int 0xcc. */ 209 regs->ip -= 2; /* The faulting instruction should be the int 0xcc. */
190 force_sig(SIGSEGV, current); 210 force_sig(SIGSEGV, current);
211 local_irq_disable();
191} 212}
192 213
193/* 214/*