Commit | Line | Data |
---|---|---|
5de15d42 | 1 | /* |
1da177e4 LT |
2 | * 32bit ptrace for x86-64. |
3 | * | |
4 | * Copyright 2001,2002 Andi Kleen, SuSE Labs. | |
5de15d42 | 5 | * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier |
1da177e4 | 6 | * copyright. |
5de15d42 TG |
7 | * |
8 | * This allows to access 64bit processes too; but there is no way to | |
9 | * see the extended register contents. | |
10 | */ | |
1da177e4 LT |
11 | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/stddef.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/syscalls.h> | |
16 | #include <linux/unistd.h> | |
17 | #include <linux/mm.h> | |
4e950f6f | 18 | #include <linux/err.h> |
1da177e4 LT |
19 | #include <linux/ptrace.h> |
20 | #include <asm/ptrace.h> | |
21 | #include <asm/compat.h> | |
22 | #include <asm/uaccess.h> | |
23 | #include <asm/user32.h> | |
24 | #include <asm/user.h> | |
25 | #include <asm/errno.h> | |
26 | #include <asm/debugreg.h> | |
27 | #include <asm/i387.h> | |
28 | #include <asm/fpu32.h> | |
f0f2d653 | 29 | #include <asm/ia32.h> |
1da177e4 | 30 | |
60923df3 CE |
31 | /* |
32 | * Determines which flags the user has access to [1 = access, 0 = no access]. | |
33 | * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), IOPL(12-13), IF(9). | |
34 | * Also masks reserved bits (31-22, 15, 5, 3, 1). | |
35 | */ | |
36 | #define FLAG_MASK 0x54dd5UL | |
1da177e4 | 37 | |
5de15d42 TG |
38 | #define R32(l,q) \ |
39 | case offsetof(struct user32, regs.l): \ | |
ff14c616 | 40 | regs->q = val; break; |
1da177e4 LT |
41 | |
42 | static int putreg32(struct task_struct *child, unsigned regno, u32 val) | |
43 | { | |
ff14c616 | 44 | struct pt_regs *regs = task_pt_regs(child); |
1da177e4 LT |
45 | |
46 | switch (regno) { | |
47 | case offsetof(struct user32, regs.fs): | |
5de15d42 TG |
48 | if (val && (val & 3) != 3) |
49 | return -EIO; | |
e8ed11b9 | 50 | child->thread.fsindex = val & 0xffff; |
1da177e4 LT |
51 | break; |
52 | case offsetof(struct user32, regs.gs): | |
5de15d42 TG |
53 | if (val && (val & 3) != 3) |
54 | return -EIO; | |
e8ed11b9 | 55 | child->thread.gsindex = val & 0xffff; |
1da177e4 LT |
56 | break; |
57 | case offsetof(struct user32, regs.ds): | |
5de15d42 TG |
58 | if (val && (val & 3) != 3) |
59 | return -EIO; | |
1da177e4 LT |
60 | child->thread.ds = val & 0xffff; |
61 | break; | |
62 | case offsetof(struct user32, regs.es): | |
63 | child->thread.es = val & 0xffff; | |
64 | break; | |
5de15d42 TG |
65 | case offsetof(struct user32, regs.ss): |
66 | if ((val & 3) != 3) | |
67 | return -EIO; | |
ff14c616 | 68 | regs->ss = val & 0xffff; |
1da177e4 | 69 | break; |
5de15d42 TG |
70 | case offsetof(struct user32, regs.cs): |
71 | if ((val & 3) != 3) | |
72 | return -EIO; | |
ff14c616 | 73 | regs->cs = val & 0xffff; |
1da177e4 LT |
74 | break; |
75 | ||
65ea5b03 PA |
76 | R32(ebx, bx); |
77 | R32(ecx, cx); | |
78 | R32(edx, dx); | |
79 | R32(edi, di); | |
80 | R32(esi, si); | |
81 | R32(ebp, bp); | |
82 | R32(eax, ax); | |
83 | R32(orig_eax, orig_ax); | |
84 | R32(eip, ip); | |
85 | R32(esp, sp); | |
1da177e4 | 86 | |
ff14c616 | 87 | case offsetof(struct user32, regs.eflags): |
1da177e4 | 88 | val &= FLAG_MASK; |
e1f28773 RM |
89 | /* |
90 | * If the user value contains TF, mark that | |
91 | * it was not "us" (the debugger) that set it. | |
92 | * If not, make sure it stays set if we had. | |
93 | */ | |
94 | if (val & X86_EFLAGS_TF) | |
95 | clear_tsk_thread_flag(child, TIF_FORCED_TF); | |
96 | else if (test_tsk_thread_flag(child, TIF_FORCED_TF)) | |
97 | val |= X86_EFLAGS_TF; | |
ff14c616 | 98 | regs->flags = val | (regs->flags & ~FLAG_MASK); |
1da177e4 | 99 | break; |
1da177e4 | 100 | |
d0f08175 RM |
101 | case offsetof(struct user32, u_debugreg[0]) ... |
102 | offsetof(struct user32, u_debugreg[7]): | |
103 | regno -= offsetof(struct user32, u_debugreg[0]); | |
104 | return ptrace_set_debugreg(child, regno / 4, val); | |
5de15d42 | 105 | |
1da177e4 LT |
106 | default: |
107 | if (regno > sizeof(struct user32) || (regno & 3)) | |
108 | return -EIO; | |
5de15d42 TG |
109 | |
110 | /* | |
111 | * Other dummy fields in the virtual user structure | |
112 | * are ignored | |
113 | */ | |
114 | break; | |
1da177e4 LT |
115 | } |
116 | return 0; | |
117 | } | |
118 | ||
119 | #undef R32 | |
120 | ||
5de15d42 TG |
121 | #define R32(l,q) \ |
122 | case offsetof(struct user32, regs.l): \ | |
ff14c616 | 123 | *val = regs->q; break |
1da177e4 LT |
124 | |
125 | static int getreg32(struct task_struct *child, unsigned regno, u32 *val) | |
126 | { | |
ff14c616 | 127 | struct pt_regs *regs = task_pt_regs(child); |
1da177e4 LT |
128 | |
129 | switch (regno) { | |
130 | case offsetof(struct user32, regs.fs): | |
5de15d42 | 131 | *val = child->thread.fsindex; |
1da177e4 LT |
132 | break; |
133 | case offsetof(struct user32, regs.gs): | |
e8ed11b9 | 134 | *val = child->thread.gsindex; |
1da177e4 LT |
135 | break; |
136 | case offsetof(struct user32, regs.ds): | |
137 | *val = child->thread.ds; | |
138 | break; | |
139 | case offsetof(struct user32, regs.es): | |
140 | *val = child->thread.es; | |
141 | break; | |
142 | ||
143 | R32(cs, cs); | |
144 | R32(ss, ss); | |
65ea5b03 PA |
145 | R32(ebx, bx); |
146 | R32(ecx, cx); | |
147 | R32(edx, dx); | |
148 | R32(edi, di); | |
149 | R32(esi, si); | |
150 | R32(ebp, bp); | |
151 | R32(eax, ax); | |
152 | R32(orig_eax, orig_ax); | |
153 | R32(eip, ip); | |
154 | R32(esp, sp); | |
1da177e4 | 155 | |
e1f28773 RM |
156 | case offsetof(struct user32, regs.eflags): |
157 | /* | |
158 | * If the debugger set TF, hide it from the readout. | |
159 | */ | |
ff14c616 | 160 | *val = regs->flags; |
e1f28773 RM |
161 | if (test_tsk_thread_flag(child, TIF_FORCED_TF)) |
162 | *val &= ~X86_EFLAGS_TF; | |
163 | break; | |
164 | ||
d0f08175 RM |
165 | case offsetof(struct user32, u_debugreg[0]) ... |
166 | offsetof(struct user32, u_debugreg[7]): | |
167 | regno -= offsetof(struct user32, u_debugreg[0]); | |
168 | *val = ptrace_get_debugreg(child, regno / 4); | |
5de15d42 TG |
169 | break; |
170 | ||
1da177e4 LT |
171 | default: |
172 | if (regno > sizeof(struct user32) || (regno & 3)) | |
173 | return -EIO; | |
174 | ||
5de15d42 TG |
175 | /* |
176 | * Other dummy fields in the virtual user structure | |
177 | * are ignored | |
178 | */ | |
1da177e4 | 179 | *val = 0; |
5de15d42 | 180 | break; |
1da177e4 LT |
181 | } |
182 | return 0; | |
183 | } | |
184 | ||
185 | #undef R32 | |
186 | ||
f0f2d653 AK |
187 | static long ptrace32_siginfo(unsigned request, u32 pid, u32 addr, u32 data) |
188 | { | |
86f93336 | 189 | siginfo_t __user *si = compat_alloc_user_space(sizeof(siginfo_t)); |
5de15d42 TG |
190 | compat_siginfo_t __user *si32 = compat_ptr(data); |
191 | siginfo_t ssi; | |
192 | int ret; | |
193 | ||
f0f2d653 | 194 | if (request == PTRACE_SETSIGINFO) { |
2c87e2cd AK |
195 | memset(&ssi, 0, sizeof(siginfo_t)); |
196 | ret = copy_siginfo_from_user32(&ssi, si32); | |
f0f2d653 AK |
197 | if (ret) |
198 | return ret; | |
2c87e2cd AK |
199 | if (copy_to_user(si, &ssi, sizeof(siginfo_t))) |
200 | return -EFAULT; | |
f0f2d653 AK |
201 | } |
202 | ret = sys_ptrace(request, pid, addr, (unsigned long)si); | |
203 | if (ret) | |
204 | return ret; | |
2c87e2cd AK |
205 | if (request == PTRACE_GETSIGINFO) { |
206 | if (copy_from_user(&ssi, si, sizeof(siginfo_t))) | |
207 | return -EFAULT; | |
208 | ret = copy_siginfo_to_user32(si32, &ssi); | |
209 | } | |
f0f2d653 AK |
210 | return ret; |
211 | } | |
212 | ||
1da177e4 LT |
213 | asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) |
214 | { | |
215 | struct task_struct *child; | |
5de15d42 | 216 | struct pt_regs *childregs; |
1da177e4 LT |
217 | void __user *datap = compat_ptr(data); |
218 | int ret; | |
219 | __u32 val; | |
220 | ||
5de15d42 | 221 | switch (request) { |
f0f2d653 AK |
222 | case PTRACE_TRACEME: |
223 | case PTRACE_ATTACH: | |
224 | case PTRACE_KILL: | |
225 | case PTRACE_CONT: | |
226 | case PTRACE_SINGLESTEP: | |
0fa376e0 | 227 | case PTRACE_SINGLEBLOCK: |
f0f2d653 AK |
228 | case PTRACE_DETACH: |
229 | case PTRACE_SYSCALL: | |
47c51dff | 230 | case PTRACE_OLDSETOPTIONS: |
f0f2d653 | 231 | case PTRACE_SETOPTIONS: |
fd181c72 RM |
232 | case PTRACE_SET_THREAD_AREA: |
233 | case PTRACE_GET_THREAD_AREA: | |
5de15d42 | 234 | return sys_ptrace(request, pid, addr, data); |
1da177e4 | 235 | |
f0f2d653 AK |
236 | default: |
237 | return -EINVAL; | |
238 | ||
1da177e4 LT |
239 | case PTRACE_PEEKTEXT: |
240 | case PTRACE_PEEKDATA: | |
241 | case PTRACE_POKEDATA: | |
242 | case PTRACE_POKETEXT: | |
5de15d42 | 243 | case PTRACE_POKEUSR: |
1da177e4 LT |
244 | case PTRACE_PEEKUSR: |
245 | case PTRACE_GETREGS: | |
246 | case PTRACE_SETREGS: | |
247 | case PTRACE_SETFPREGS: | |
248 | case PTRACE_GETFPREGS: | |
249 | case PTRACE_SETFPXREGS: | |
250 | case PTRACE_GETFPXREGS: | |
251 | case PTRACE_GETEVENTMSG: | |
252 | break; | |
1da177e4 | 253 | |
f0f2d653 AK |
254 | case PTRACE_SETSIGINFO: |
255 | case PTRACE_GETSIGINFO: | |
256 | return ptrace32_siginfo(request, pid, addr, data); | |
257 | } | |
6b9c7ed8 CH |
258 | |
259 | child = ptrace_get_task_struct(pid); | |
260 | if (IS_ERR(child)) | |
261 | return PTR_ERR(child); | |
262 | ||
263 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | |
264 | if (ret < 0) | |
265 | goto out; | |
1da177e4 | 266 | |
bb049232 | 267 | childregs = task_pt_regs(child); |
1da177e4 LT |
268 | |
269 | switch (request) { | |
270 | case PTRACE_PEEKDATA: | |
271 | case PTRACE_PEEKTEXT: | |
272 | ret = 0; | |
5de15d42 TG |
273 | if (access_process_vm(child, addr, &val, sizeof(u32), 0) != |
274 | sizeof(u32)) | |
1da177e4 LT |
275 | ret = -EIO; |
276 | else | |
5de15d42 TG |
277 | ret = put_user(val, (unsigned int __user *)datap); |
278 | break; | |
1da177e4 LT |
279 | |
280 | case PTRACE_POKEDATA: | |
281 | case PTRACE_POKETEXT: | |
282 | ret = 0; | |
5de15d42 TG |
283 | if (access_process_vm(child, addr, &data, sizeof(u32), 1) != |
284 | sizeof(u32)) | |
285 | ret = -EIO; | |
1da177e4 LT |
286 | break; |
287 | ||
288 | case PTRACE_PEEKUSR: | |
289 | ret = getreg32(child, addr, &val); | |
290 | if (ret == 0) | |
291 | ret = put_user(val, (__u32 __user *)datap); | |
292 | break; | |
293 | ||
294 | case PTRACE_POKEUSR: | |
295 | ret = putreg32(child, addr, data); | |
296 | break; | |
297 | ||
298 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | |
299 | int i; | |
5de15d42 TG |
300 | |
301 | if (!access_ok(VERIFY_WRITE, datap, 16*4)) { | |
1da177e4 LT |
302 | ret = -EIO; |
303 | break; | |
304 | } | |
305 | ret = 0; | |
5de15d42 | 306 | for (i = 0; i <= 16*4; i += sizeof(__u32)) { |
1da177e4 | 307 | getreg32(child, i, &val); |
5de15d42 | 308 | ret |= __put_user(val, (u32 __user *)datap); |
1da177e4 LT |
309 | datap += sizeof(u32); |
310 | } | |
311 | break; | |
312 | } | |
313 | ||
314 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | |
315 | unsigned long tmp; | |
316 | int i; | |
5de15d42 TG |
317 | |
318 | if (!access_ok(VERIFY_READ, datap, 16*4)) { | |
1da177e4 LT |
319 | ret = -EIO; |
320 | break; | |
321 | } | |
5de15d42 TG |
322 | ret = 0; |
323 | for (i = 0; i <= 16*4; i += sizeof(u32)) { | |
1da177e4 LT |
324 | ret |= __get_user(tmp, (u32 __user *)datap); |
325 | putreg32(child, i, tmp); | |
326 | datap += sizeof(u32); | |
327 | } | |
328 | break; | |
329 | } | |
330 | ||
331 | case PTRACE_GETFPREGS: | |
5de15d42 TG |
332 | ret = -EIO; |
333 | if (!access_ok(VERIFY_READ, compat_ptr(data), | |
1da177e4 LT |
334 | sizeof(struct user_i387_struct))) |
335 | break; | |
336 | save_i387_ia32(child, datap, childregs, 1); | |
5de15d42 | 337 | ret = 0; |
1da177e4 LT |
338 | break; |
339 | ||
340 | case PTRACE_SETFPREGS: | |
341 | ret = -EIO; | |
5de15d42 | 342 | if (!access_ok(VERIFY_WRITE, datap, |
1da177e4 LT |
343 | sizeof(struct user_i387_struct))) |
344 | break; | |
345 | ret = 0; | |
346 | /* don't check EFAULT to be bug-to-bug compatible to i386 */ | |
347 | restore_i387_ia32(child, datap, 1); | |
348 | break; | |
349 | ||
5de15d42 | 350 | case PTRACE_GETFPXREGS: { |
1da177e4 | 351 | struct user32_fxsr_struct __user *u = datap; |
5de15d42 TG |
352 | |
353 | init_fpu(child); | |
1da177e4 LT |
354 | ret = -EIO; |
355 | if (!access_ok(VERIFY_WRITE, u, sizeof(*u))) | |
356 | break; | |
357 | ret = -EFAULT; | |
358 | if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u))) | |
359 | break; | |
360 | ret = __put_user(childregs->cs, &u->fcs); | |
5de15d42 TG |
361 | ret |= __put_user(child->thread.ds, &u->fos); |
362 | break; | |
363 | } | |
364 | case PTRACE_SETFPXREGS: { | |
1da177e4 | 365 | struct user32_fxsr_struct __user *u = datap; |
5de15d42 | 366 | |
1da177e4 LT |
367 | unlazy_fpu(child); |
368 | ret = -EIO; | |
369 | if (!access_ok(VERIFY_READ, u, sizeof(*u))) | |
370 | break; | |
5de15d42 TG |
371 | /* |
372 | * no checking to be bug-to-bug compatible with i386. | |
373 | * but silence warning | |
374 | */ | |
95912008 AK |
375 | if (__copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u))) |
376 | ; | |
1da177e4 LT |
377 | set_stopped_child_used_math(child); |
378 | child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | |
5de15d42 | 379 | ret = 0; |
1da177e4 LT |
380 | break; |
381 | } | |
382 | ||
383 | case PTRACE_GETEVENTMSG: | |
5de15d42 TG |
384 | ret = put_user(child->ptrace_message, |
385 | (unsigned int __user *)compat_ptr(data)); | |
1da177e4 LT |
386 | break; |
387 | ||
388 | default: | |
f0f2d653 | 389 | BUG(); |
1da177e4 LT |
390 | } |
391 | ||
6b9c7ed8 | 392 | out: |
1da177e4 LT |
393 | put_task_struct(child); |
394 | return ret; | |
395 | } |