Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * 32bit ptrace for x86-64. | |
3 | * | |
4 | * Copyright 2001,2002 Andi Kleen, SuSE Labs. | |
5 | * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier | |
6 | * copyright. | |
7 | * | |
8 | * This allows to access 64bit processes too; but there is no way to see the extended | |
9 | * register contents. | |
10 | * | |
11 | * $Id: ptrace32.c,v 1.16 2003/03/14 16:06:35 ak Exp $ | |
12 | */ | |
13 | ||
14 | #include <linux/kernel.h> | |
15 | #include <linux/stddef.h> | |
16 | #include <linux/sched.h> | |
17 | #include <linux/syscalls.h> | |
18 | #include <linux/unistd.h> | |
19 | #include <linux/mm.h> | |
20 | #include <linux/ptrace.h> | |
21 | #include <asm/ptrace.h> | |
22 | #include <asm/compat.h> | |
23 | #include <asm/uaccess.h> | |
24 | #include <asm/user32.h> | |
25 | #include <asm/user.h> | |
26 | #include <asm/errno.h> | |
27 | #include <asm/debugreg.h> | |
28 | #include <asm/i387.h> | |
29 | #include <asm/fpu32.h> | |
30 | ||
31 | /* determines which flags the user has access to. */ | |
32 | /* 1 = access 0 = no access */ | |
33 | #define FLAG_MASK 0x44dd5UL | |
34 | ||
35 | #define R32(l,q) \ | |
36 | case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break | |
37 | ||
38 | static int putreg32(struct task_struct *child, unsigned regno, u32 val) | |
39 | { | |
40 | int i; | |
41 | __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); | |
42 | ||
43 | switch (regno) { | |
44 | case offsetof(struct user32, regs.fs): | |
45 | if (val && (val & 3) != 3) return -EIO; | |
e8ed11b9 | 46 | child->thread.fsindex = val & 0xffff; |
1da177e4 LT |
47 | break; |
48 | case offsetof(struct user32, regs.gs): | |
49 | if (val && (val & 3) != 3) return -EIO; | |
e8ed11b9 | 50 | child->thread.gsindex = val & 0xffff; |
1da177e4 LT |
51 | break; |
52 | case offsetof(struct user32, regs.ds): | |
53 | if (val && (val & 3) != 3) return -EIO; | |
54 | child->thread.ds = val & 0xffff; | |
55 | break; | |
56 | case offsetof(struct user32, regs.es): | |
57 | child->thread.es = val & 0xffff; | |
58 | break; | |
59 | case offsetof(struct user32, regs.ss): | |
60 | if ((val & 3) != 3) return -EIO; | |
61 | stack[offsetof(struct pt_regs, ss)/8] = val & 0xffff; | |
62 | break; | |
63 | case offsetof(struct user32, regs.cs): | |
64 | if ((val & 3) != 3) return -EIO; | |
65 | stack[offsetof(struct pt_regs, cs)/8] = val & 0xffff; | |
66 | break; | |
67 | ||
68 | R32(ebx, rbx); | |
69 | R32(ecx, rcx); | |
70 | R32(edx, rdx); | |
71 | R32(edi, rdi); | |
72 | R32(esi, rsi); | |
73 | R32(ebp, rbp); | |
74 | R32(eax, rax); | |
75 | R32(orig_eax, orig_rax); | |
76 | R32(eip, rip); | |
77 | R32(esp, rsp); | |
78 | ||
79 | case offsetof(struct user32, regs.eflags): { | |
80 | __u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8]; | |
81 | val &= FLAG_MASK; | |
82 | *flags = val | (*flags & ~FLAG_MASK); | |
83 | break; | |
84 | } | |
85 | ||
86 | case offsetof(struct user32, u_debugreg[4]): | |
87 | case offsetof(struct user32, u_debugreg[5]): | |
88 | return -EIO; | |
89 | ||
90 | case offsetof(struct user32, u_debugreg[0]): | |
91 | child->thread.debugreg0 = val; | |
92 | break; | |
93 | ||
94 | case offsetof(struct user32, u_debugreg[1]): | |
95 | child->thread.debugreg1 = val; | |
96 | break; | |
97 | ||
98 | case offsetof(struct user32, u_debugreg[2]): | |
99 | child->thread.debugreg2 = val; | |
100 | break; | |
101 | ||
102 | case offsetof(struct user32, u_debugreg[3]): | |
103 | child->thread.debugreg3 = val; | |
104 | break; | |
105 | ||
106 | case offsetof(struct user32, u_debugreg[6]): | |
107 | child->thread.debugreg6 = val; | |
108 | break; | |
109 | ||
110 | case offsetof(struct user32, u_debugreg[7]): | |
111 | val &= ~DR_CONTROL_RESERVED; | |
112 | /* See arch/i386/kernel/ptrace.c for an explanation of | |
113 | * this awkward check.*/ | |
114 | for(i=0; i<4; i++) | |
115 | if ((0x5454 >> ((val >> (16 + 4*i)) & 0xf)) & 1) | |
116 | return -EIO; | |
117 | child->thread.debugreg7 = val; | |
118 | break; | |
119 | ||
120 | default: | |
121 | if (regno > sizeof(struct user32) || (regno & 3)) | |
122 | return -EIO; | |
123 | ||
124 | /* Other dummy fields in the virtual user structure are ignored */ | |
125 | break; | |
126 | } | |
127 | return 0; | |
128 | } | |
129 | ||
130 | #undef R32 | |
131 | ||
132 | #define R32(l,q) \ | |
133 | case offsetof(struct user32, regs.l): *val = stack[offsetof(struct pt_regs, q)/8]; break | |
134 | ||
135 | static int getreg32(struct task_struct *child, unsigned regno, u32 *val) | |
136 | { | |
137 | __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); | |
138 | ||
139 | switch (regno) { | |
140 | case offsetof(struct user32, regs.fs): | |
e8ed11b9 | 141 | *val = child->thread.fsindex; |
1da177e4 LT |
142 | break; |
143 | case offsetof(struct user32, regs.gs): | |
e8ed11b9 | 144 | *val = child->thread.gsindex; |
1da177e4 LT |
145 | break; |
146 | case offsetof(struct user32, regs.ds): | |
147 | *val = child->thread.ds; | |
148 | break; | |
149 | case offsetof(struct user32, regs.es): | |
150 | *val = child->thread.es; | |
151 | break; | |
152 | ||
153 | R32(cs, cs); | |
154 | R32(ss, ss); | |
155 | R32(ebx, rbx); | |
156 | R32(ecx, rcx); | |
157 | R32(edx, rdx); | |
158 | R32(edi, rdi); | |
159 | R32(esi, rsi); | |
160 | R32(ebp, rbp); | |
161 | R32(eax, rax); | |
162 | R32(orig_eax, orig_rax); | |
163 | R32(eip, rip); | |
164 | R32(eflags, eflags); | |
165 | R32(esp, rsp); | |
166 | ||
167 | case offsetof(struct user32, u_debugreg[0]): | |
168 | *val = child->thread.debugreg0; | |
169 | break; | |
170 | case offsetof(struct user32, u_debugreg[1]): | |
171 | *val = child->thread.debugreg1; | |
172 | break; | |
173 | case offsetof(struct user32, u_debugreg[2]): | |
174 | *val = child->thread.debugreg2; | |
175 | break; | |
176 | case offsetof(struct user32, u_debugreg[3]): | |
177 | *val = child->thread.debugreg3; | |
178 | break; | |
179 | case offsetof(struct user32, u_debugreg[6]): | |
180 | *val = child->thread.debugreg6; | |
181 | break; | |
182 | case offsetof(struct user32, u_debugreg[7]): | |
183 | *val = child->thread.debugreg7; | |
184 | break; | |
185 | ||
186 | default: | |
187 | if (regno > sizeof(struct user32) || (regno & 3)) | |
188 | return -EIO; | |
189 | ||
190 | /* Other dummy fields in the virtual user structure are ignored */ | |
191 | *val = 0; | |
192 | break; | |
193 | } | |
194 | return 0; | |
195 | } | |
196 | ||
197 | #undef R32 | |
198 | ||
199 | static struct task_struct *find_target(int request, int pid, int *err) | |
200 | { | |
201 | struct task_struct *child; | |
202 | ||
203 | *err = -EPERM; | |
204 | if (pid == 1) | |
205 | return NULL; | |
206 | ||
207 | *err = -ESRCH; | |
208 | read_lock(&tasklist_lock); | |
209 | child = find_task_by_pid(pid); | |
210 | if (child) | |
211 | get_task_struct(child); | |
212 | read_unlock(&tasklist_lock); | |
213 | if (child) { | |
214 | *err = -EPERM; | |
215 | if (child->pid == 1) | |
216 | goto out; | |
217 | *err = ptrace_check_attach(child, request == PTRACE_KILL); | |
218 | if (*err < 0) | |
219 | goto out; | |
220 | return child; | |
221 | } | |
222 | out: | |
223 | if (child) | |
224 | put_task_struct(child); | |
225 | return NULL; | |
226 | ||
227 | } | |
228 | ||
229 | asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) | |
230 | { | |
231 | struct task_struct *child; | |
232 | struct pt_regs *childregs; | |
233 | void __user *datap = compat_ptr(data); | |
234 | int ret; | |
235 | __u32 val; | |
236 | ||
237 | switch (request) { | |
238 | default: | |
239 | return sys_ptrace(request, pid, addr, data); | |
240 | ||
241 | case PTRACE_PEEKTEXT: | |
242 | case PTRACE_PEEKDATA: | |
243 | case PTRACE_POKEDATA: | |
244 | case PTRACE_POKETEXT: | |
245 | case PTRACE_POKEUSR: | |
246 | case PTRACE_PEEKUSR: | |
247 | case PTRACE_GETREGS: | |
248 | case PTRACE_SETREGS: | |
249 | case PTRACE_SETFPREGS: | |
250 | case PTRACE_GETFPREGS: | |
251 | case PTRACE_SETFPXREGS: | |
252 | case PTRACE_GETFPXREGS: | |
253 | case PTRACE_GETEVENTMSG: | |
254 | break; | |
255 | } | |
256 | ||
257 | child = find_target(request, pid, &ret); | |
258 | if (!child) | |
259 | return ret; | |
260 | ||
261 | childregs = (struct pt_regs *)(child->thread.rsp0 - sizeof(struct pt_regs)); | |
262 | ||
263 | switch (request) { | |
264 | case PTRACE_PEEKDATA: | |
265 | case PTRACE_PEEKTEXT: | |
266 | ret = 0; | |
267 | if (access_process_vm(child, addr, &val, sizeof(u32), 0)!=sizeof(u32)) | |
268 | ret = -EIO; | |
269 | else | |
270 | ret = put_user(val, (unsigned int __user *)datap); | |
271 | break; | |
272 | ||
273 | case PTRACE_POKEDATA: | |
274 | case PTRACE_POKETEXT: | |
275 | ret = 0; | |
276 | if (access_process_vm(child, addr, &data, sizeof(u32), 1)!=sizeof(u32)) | |
277 | ret = -EIO; | |
278 | break; | |
279 | ||
280 | case PTRACE_PEEKUSR: | |
281 | ret = getreg32(child, addr, &val); | |
282 | if (ret == 0) | |
283 | ret = put_user(val, (__u32 __user *)datap); | |
284 | break; | |
285 | ||
286 | case PTRACE_POKEUSR: | |
287 | ret = putreg32(child, addr, data); | |
288 | break; | |
289 | ||
290 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | |
291 | int i; | |
292 | if (!access_ok(VERIFY_WRITE, datap, 16*4)) { | |
293 | ret = -EIO; | |
294 | break; | |
295 | } | |
296 | ret = 0; | |
297 | for ( i = 0; i <= 16*4 ; i += sizeof(__u32) ) { | |
298 | getreg32(child, i, &val); | |
299 | ret |= __put_user(val,(u32 __user *)datap); | |
300 | datap += sizeof(u32); | |
301 | } | |
302 | break; | |
303 | } | |
304 | ||
305 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | |
306 | unsigned long tmp; | |
307 | int i; | |
308 | if (!access_ok(VERIFY_READ, datap, 16*4)) { | |
309 | ret = -EIO; | |
310 | break; | |
311 | } | |
312 | ret = 0; | |
313 | for ( i = 0; i <= 16*4; i += sizeof(u32) ) { | |
314 | ret |= __get_user(tmp, (u32 __user *)datap); | |
315 | putreg32(child, i, tmp); | |
316 | datap += sizeof(u32); | |
317 | } | |
318 | break; | |
319 | } | |
320 | ||
321 | case PTRACE_GETFPREGS: | |
322 | ret = -EIO; | |
323 | if (!access_ok(VERIFY_READ, compat_ptr(data), | |
324 | sizeof(struct user_i387_struct))) | |
325 | break; | |
326 | save_i387_ia32(child, datap, childregs, 1); | |
327 | ret = 0; | |
328 | break; | |
329 | ||
330 | case PTRACE_SETFPREGS: | |
331 | ret = -EIO; | |
332 | if (!access_ok(VERIFY_WRITE, datap, | |
333 | sizeof(struct user_i387_struct))) | |
334 | break; | |
335 | ret = 0; | |
336 | /* don't check EFAULT to be bug-to-bug compatible to i386 */ | |
337 | restore_i387_ia32(child, datap, 1); | |
338 | break; | |
339 | ||
340 | case PTRACE_GETFPXREGS: { | |
341 | struct user32_fxsr_struct __user *u = datap; | |
342 | init_fpu(child); | |
343 | ret = -EIO; | |
344 | if (!access_ok(VERIFY_WRITE, u, sizeof(*u))) | |
345 | break; | |
346 | ret = -EFAULT; | |
347 | if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u))) | |
348 | break; | |
349 | ret = __put_user(childregs->cs, &u->fcs); | |
350 | ret |= __put_user(child->thread.ds, &u->fos); | |
351 | break; | |
352 | } | |
353 | case PTRACE_SETFPXREGS: { | |
354 | struct user32_fxsr_struct __user *u = datap; | |
355 | unlazy_fpu(child); | |
356 | ret = -EIO; | |
357 | if (!access_ok(VERIFY_READ, u, sizeof(*u))) | |
358 | break; | |
359 | /* no checking to be bug-to-bug compatible with i386 */ | |
360 | __copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u)); | |
361 | set_stopped_child_used_math(child); | |
362 | child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | |
363 | ret = 0; | |
364 | break; | |
365 | } | |
366 | ||
367 | case PTRACE_GETEVENTMSG: | |
368 | ret = put_user(child->ptrace_message,(unsigned int __user *)compat_ptr(data)); | |
369 | break; | |
370 | ||
371 | default: | |
372 | ret = -EINVAL; | |
373 | break; | |
374 | } | |
375 | ||
376 | put_task_struct(child); | |
377 | return ret; | |
378 | } | |
379 |