]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * PowerPC version |
3 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) | |
4 | * | |
5 | * Derived from "arch/m68k/kernel/ptrace.c" | |
6 | * Copyright (C) 1994 by Hamish Macdonald | |
7 | * Taken from linux/kernel/ptrace.c and modified for M680x0. | |
8 | * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds | |
9 | * | |
10 | * Modified by Cort Dougan (cort@hq.fsmlabs.com) | |
b123923d | 11 | * and Paul Mackerras (paulus@samba.org). |
1da177e4 LT |
12 | * |
13 | * This file is subject to the terms and conditions of the GNU General | |
14 | * Public License. See the file README.legal in the main directory of | |
15 | * this archive for more details. | |
16 | */ | |
17 | ||
f65255e8 | 18 | #include <linux/regset.h> |
153474ba | 19 | #include <linux/ptrace.h> |
ea9c102c | 20 | #include <linux/audit.h> |
22ecbe8d | 21 | #include <linux/context_tracking.h> |
b3138536 | 22 | #include <linux/syscalls.h> |
b3138536 | 23 | |
ae3a197e | 24 | #include <asm/switch_to.h> |
85ce9a5d | 25 | #include <asm/debug.h> |
21a62902 | 26 | |
02424d89 IM |
27 | #define CREATE_TRACE_POINTS |
28 | #include <trace/events/syscalls.h> | |
29 | ||
7b99ed4e CL |
30 | #include "ptrace-decl.h" |
31 | ||
1da177e4 LT |
32 | /* |
33 | * Called by kernel/ptrace.c when detaching.. | |
34 | * | |
35 | * Make sure single step bits etc are not set. | |
36 | */ | |
37 | void ptrace_disable(struct task_struct *child) | |
38 | { | |
39 | /* make sure the single step bit is not set. */ | |
2a84b0d7 | 40 | user_disable_single_step(child); |
1da177e4 LT |
41 | } |
42 | ||
9b05a69e NK |
43 | long arch_ptrace(struct task_struct *child, long request, |
44 | unsigned long addr, unsigned long data) | |
1da177e4 | 45 | { |
1da177e4 | 46 | int ret = -EPERM; |
f68d2048 NK |
47 | void __user *datavp = (void __user *) data; |
48 | unsigned long __user *datalp = datavp; | |
1da177e4 | 49 | |
1da177e4 | 50 | switch (request) { |
1da177e4 | 51 | /* read the word at location addr in the USER area. */ |
1da177e4 LT |
52 | case PTRACE_PEEKUSR: { |
53 | unsigned long index, tmp; | |
54 | ||
55 | ret = -EIO; | |
56 | /* convert to index and check */ | |
e009fa43 | 57 | index = addr / sizeof(long); |
4d90eb97 | 58 | if ((addr & (sizeof(long) - 1)) || !child->thread.regs) |
1da177e4 LT |
59 | break; |
60 | ||
4d90eb97 | 61 | if (index < PT_FPR0) |
ee4a3916 | 62 | ret = ptrace_get_reg(child, (int) index, &tmp); |
4d90eb97 CL |
63 | else |
64 | ret = ptrace_get_fpr(child, index, &tmp); | |
65 | ||
66 | if (ret) | |
67 | break; | |
f68d2048 | 68 | ret = put_user(tmp, datalp); |
1da177e4 LT |
69 | break; |
70 | } | |
71 | ||
1da177e4 LT |
72 | /* write the word at location addr in the USER area */ |
73 | case PTRACE_POKEUSR: { | |
74 | unsigned long index; | |
75 | ||
76 | ret = -EIO; | |
77 | /* convert to index and check */ | |
e009fa43 | 78 | index = addr / sizeof(long); |
4d90eb97 | 79 | if ((addr & (sizeof(long) - 1)) || !child->thread.regs) |
1da177e4 LT |
80 | break; |
81 | ||
4d90eb97 | 82 | if (index < PT_FPR0) |
865418d8 | 83 | ret = ptrace_put_reg(child, index, data); |
4d90eb97 CL |
84 | else |
85 | ret = ptrace_put_fpr(child, index, data); | |
1da177e4 LT |
86 | break; |
87 | } | |
88 | ||
3162d92d DK |
89 | case PPC_PTRACE_GETHWDBGINFO: { |
90 | struct ppc_debug_info dbginfo; | |
91 | ||
da529d47 | 92 | ppc_gethwdinfo(&dbginfo); |
3162d92d | 93 | |
6bcdd297 AV |
94 | if (copy_to_user(datavp, &dbginfo, |
95 | sizeof(struct ppc_debug_info))) | |
3162d92d | 96 | return -EFAULT; |
6bcdd297 | 97 | return 0; |
3162d92d DK |
98 | } |
99 | ||
100 | case PPC_PTRACE_SETHWDEBUG: { | |
101 | struct ppc_hw_breakpoint bp_info; | |
102 | ||
6bcdd297 AV |
103 | if (copy_from_user(&bp_info, datavp, |
104 | sizeof(struct ppc_hw_breakpoint))) | |
3162d92d | 105 | return -EFAULT; |
6bcdd297 | 106 | return ppc_set_hwdebug(child, &bp_info); |
3162d92d DK |
107 | } |
108 | ||
109 | case PPC_PTRACE_DELHWDEBUG: { | |
ec1b33dc | 110 | ret = ppc_del_hwdebug(child, data); |
3162d92d DK |
111 | break; |
112 | } | |
113 | ||
e08227d2 CL |
114 | case PTRACE_GET_DEBUGREG: |
115 | ret = ptrace_get_debugreg(child, addr, datalp); | |
e8a30302 | 116 | break; |
e8a30302 SR |
117 | |
118 | case PTRACE_SET_DEBUGREG: | |
119 | ret = ptrace_set_debugreg(child, addr, data); | |
120 | break; | |
e8a30302 | 121 | |
e17666ba BH |
122 | #ifdef CONFIG_PPC64 |
123 | case PTRACE_GETREGS64: | |
124 | #endif | |
c391cd00 RM |
125 | case PTRACE_GETREGS: /* Get all pt_regs from the child. */ |
126 | return copy_regset_to_user(child, &user_ppc_native_view, | |
127 | REGSET_GPR, | |
3eeacd9f | 128 | 0, sizeof(struct user_pt_regs), |
f68d2048 | 129 | datavp); |
e8a30302 | 130 | |
e17666ba BH |
131 | #ifdef CONFIG_PPC64 |
132 | case PTRACE_SETREGS64: | |
133 | #endif | |
c391cd00 RM |
134 | case PTRACE_SETREGS: /* Set all gp regs in the child. */ |
135 | return copy_regset_from_user(child, &user_ppc_native_view, | |
136 | REGSET_GPR, | |
3eeacd9f | 137 | 0, sizeof(struct user_pt_regs), |
f68d2048 | 138 | datavp); |
c391cd00 RM |
139 | |
140 | case PTRACE_GETFPREGS: /* Get the child FPU state (FPR0...31 + FPSCR) */ | |
141 | return copy_regset_to_user(child, &user_ppc_native_view, | |
142 | REGSET_FPR, | |
143 | 0, sizeof(elf_fpregset_t), | |
f68d2048 | 144 | datavp); |
c391cd00 RM |
145 | |
146 | case PTRACE_SETFPREGS: /* Set the child FPU state (FPR0...31 + FPSCR) */ | |
147 | return copy_regset_from_user(child, &user_ppc_native_view, | |
148 | REGSET_FPR, | |
149 | 0, sizeof(elf_fpregset_t), | |
f68d2048 | 150 | datavp); |
e8a30302 | 151 | |
1da177e4 LT |
152 | #ifdef CONFIG_ALTIVEC |
153 | case PTRACE_GETVRREGS: | |
c391cd00 RM |
154 | return copy_regset_to_user(child, &user_ppc_native_view, |
155 | REGSET_VMX, | |
156 | 0, (33 * sizeof(vector128) + | |
157 | sizeof(u32)), | |
f68d2048 | 158 | datavp); |
1da177e4 LT |
159 | |
160 | case PTRACE_SETVRREGS: | |
c391cd00 RM |
161 | return copy_regset_from_user(child, &user_ppc_native_view, |
162 | REGSET_VMX, | |
163 | 0, (33 * sizeof(vector128) + | |
164 | sizeof(u32)), | |
f68d2048 | 165 | datavp); |
1da177e4 | 166 | #endif |
ce48b210 MN |
167 | #ifdef CONFIG_VSX |
168 | case PTRACE_GETVSRREGS: | |
169 | return copy_regset_to_user(child, &user_ppc_native_view, | |
170 | REGSET_VSX, | |
1ac42ef8 | 171 | 0, 32 * sizeof(double), |
f68d2048 | 172 | datavp); |
ce48b210 MN |
173 | |
174 | case PTRACE_SETVSRREGS: | |
175 | return copy_regset_from_user(child, &user_ppc_native_view, | |
176 | REGSET_VSX, | |
1ac42ef8 | 177 | 0, 32 * sizeof(double), |
f68d2048 | 178 | datavp); |
ce48b210 | 179 | #endif |
1da177e4 LT |
180 | #ifdef CONFIG_SPE |
181 | case PTRACE_GETEVRREGS: | |
182 | /* Get the child spe register state. */ | |
c391cd00 RM |
183 | return copy_regset_to_user(child, &user_ppc_native_view, |
184 | REGSET_SPE, 0, 35 * sizeof(u32), | |
f68d2048 | 185 | datavp); |
1da177e4 LT |
186 | |
187 | case PTRACE_SETEVRREGS: | |
188 | /* Set the child spe register state. */ | |
c391cd00 RM |
189 | return copy_regset_from_user(child, &user_ppc_native_view, |
190 | REGSET_SPE, 0, 35 * sizeof(u32), | |
f68d2048 | 191 | datavp); |
1da177e4 LT |
192 | #endif |
193 | ||
194 | default: | |
195 | ret = ptrace_request(child, request, addr, data); | |
196 | break; | |
197 | } | |
1da177e4 LT |
198 | return ret; |
199 | } | |
200 | ||
2449acc5 ME |
201 | #ifdef CONFIG_SECCOMP |
202 | static int do_seccomp(struct pt_regs *regs) | |
203 | { | |
204 | if (!test_thread_flag(TIF_SECCOMP)) | |
205 | return 0; | |
206 | ||
207 | /* | |
208 | * The ABI we present to seccomp tracers is that r3 contains | |
209 | * the syscall return value and orig_gpr3 contains the first | |
210 | * syscall parameter. This is different to the ptrace ABI where | |
211 | * both r3 and orig_gpr3 contain the first syscall parameter. | |
212 | */ | |
213 | regs->gpr[3] = -ENOSYS; | |
214 | ||
215 | /* | |
216 | * We use the __ version here because we have already checked | |
217 | * TIF_SECCOMP. If this fails, there is nothing left to do, we | |
218 | * have already loaded -ENOSYS into r3, or seccomp has put | |
219 | * something else in r3 (via SECCOMP_RET_ERRNO/TRACE). | |
220 | */ | |
2f275de5 | 221 | if (__secure_computing(NULL)) |
2449acc5 ME |
222 | return -1; |
223 | ||
224 | /* | |
225 | * The syscall was allowed by seccomp, restore the register | |
1addc57e | 226 | * state to what audit expects. |
2449acc5 ME |
227 | * Note that we use orig_gpr3, which means a seccomp tracer can |
228 | * modify the first syscall parameter (in orig_gpr3) and also | |
229 | * allow the syscall to proceed. | |
230 | */ | |
231 | regs->gpr[3] = regs->orig_gpr3; | |
232 | ||
233 | return 0; | |
234 | } | |
235 | #else | |
236 | static inline int do_seccomp(struct pt_regs *regs) { return 0; } | |
237 | #endif /* CONFIG_SECCOMP */ | |
238 | ||
d3837414 ME |
239 | /** |
240 | * do_syscall_trace_enter() - Do syscall tracing on kernel entry. | |
241 | * @regs: the pt_regs of the task to trace (current) | |
242 | * | |
243 | * Performs various types of tracing on syscall entry. This includes seccomp, | |
244 | * ptrace, syscall tracepoints and audit. | |
245 | * | |
246 | * The pt_regs are potentially visible to userspace via ptrace, so their | |
247 | * contents is ABI. | |
248 | * | |
249 | * One or more of the tracers may modify the contents of pt_regs, in particular | |
250 | * to modify arguments or even the syscall number itself. | |
251 | * | |
252 | * It's also possible that a tracer can choose to reject the system call. In | |
253 | * that case this function will return an illegal syscall number, and will put | |
254 | * an appropriate return value in regs->r3. | |
255 | * | |
256 | * Return: the (possibly changed) syscall number. | |
4f72c427 RM |
257 | */ |
258 | long do_syscall_trace_enter(struct pt_regs *regs) | |
1da177e4 | 259 | { |
8dbdec0b DL |
260 | u32 flags; |
261 | ||
985faa78 | 262 | flags = read_thread_flags() & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE); |
5521eb4b | 263 | |
8dbdec0b | 264 | if (flags) { |
153474ba | 265 | int rc = ptrace_report_syscall_entry(regs); |
8dbdec0b DL |
266 | |
267 | if (unlikely(flags & _TIF_SYSCALL_EMU)) { | |
268 | /* | |
269 | * A nonzero return code from | |
153474ba | 270 | * ptrace_report_syscall_entry() tells us to prevent |
8dbdec0b DL |
271 | * the syscall execution, but we are not going to |
272 | * execute it anyway. | |
273 | * | |
274 | * Returning -1 will skip the syscall execution. We want | |
275 | * to avoid clobbering any registers, so we don't goto | |
276 | * the skip label below. | |
277 | */ | |
278 | return -1; | |
279 | } | |
280 | ||
281 | if (rc) { | |
282 | /* | |
283 | * The tracer decided to abort the syscall. Note that | |
284 | * the tracer may also just change regs->gpr[0] to an | |
285 | * invalid syscall number, that is handled below on the | |
286 | * exit path. | |
287 | */ | |
288 | goto skip; | |
289 | } | |
290 | } | |
1addc57e KC |
291 | |
292 | /* Run seccomp after ptrace; allow it to set gpr[3]. */ | |
2449acc5 ME |
293 | if (do_seccomp(regs)) |
294 | return -1; | |
e8a30302 | 295 | |
1addc57e KC |
296 | /* Avoid trace and audit when syscall is invalid. */ |
297 | if (regs->gpr[0] >= NR_syscalls) | |
298 | goto skip; | |
ea9c102c | 299 | |
02424d89 IM |
300 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
301 | trace_sys_enter(regs, regs->gpr[0]); | |
302 | ||
b05d8447 | 303 | if (!is_32bit_task()) |
91397401 | 304 | audit_syscall_entry(regs->gpr[0], regs->gpr[3], regs->gpr[4], |
b05d8447 EP |
305 | regs->gpr[5], regs->gpr[6]); |
306 | else | |
91397401 | 307 | audit_syscall_entry(regs->gpr[0], |
b05d8447 EP |
308 | regs->gpr[3] & 0xffffffff, |
309 | regs->gpr[4] & 0xffffffff, | |
310 | regs->gpr[5] & 0xffffffff, | |
311 | regs->gpr[6] & 0xffffffff); | |
4f72c427 | 312 | |
d3837414 ME |
313 | /* Return the possibly modified but valid syscall number */ |
314 | return regs->gpr[0]; | |
1addc57e KC |
315 | |
316 | skip: | |
317 | /* | |
318 | * If we are aborting explicitly, or if the syscall number is | |
319 | * now invalid, set the return value to -ENOSYS. | |
320 | */ | |
321 | regs->gpr[3] = -ENOSYS; | |
322 | return -1; | |
ea9c102c DW |
323 | } |
324 | ||
325 | void do_syscall_trace_leave(struct pt_regs *regs) | |
326 | { | |
4f72c427 RM |
327 | int step; |
328 | ||
d7e7528b | 329 | audit_syscall_exit(regs); |
ea9c102c | 330 | |
02424d89 IM |
331 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
332 | trace_sys_exit(regs, regs->result); | |
333 | ||
4f72c427 RM |
334 | step = test_thread_flag(TIF_SINGLESTEP); |
335 | if (step || test_thread_flag(TIF_SYSCALL_TRACE)) | |
153474ba | 336 | ptrace_report_syscall_exit(regs, step); |
ea9c102c | 337 | } |
002af939 | 338 | |
5c74f799 MM |
339 | void __init pt_regs_check(void); |
340 | ||
341 | /* | |
342 | * Dummy function, its purpose is to break the build if struct pt_regs and | |
343 | * struct user_pt_regs don't match. | |
344 | */ | |
002af939 ME |
345 | void __init pt_regs_check(void) |
346 | { | |
347 | BUILD_BUG_ON(offsetof(struct pt_regs, gpr) != | |
348 | offsetof(struct user_pt_regs, gpr)); | |
349 | BUILD_BUG_ON(offsetof(struct pt_regs, nip) != | |
350 | offsetof(struct user_pt_regs, nip)); | |
351 | BUILD_BUG_ON(offsetof(struct pt_regs, msr) != | |
352 | offsetof(struct user_pt_regs, msr)); | |
002af939 ME |
353 | BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != |
354 | offsetof(struct user_pt_regs, orig_gpr3)); | |
355 | BUILD_BUG_ON(offsetof(struct pt_regs, ctr) != | |
356 | offsetof(struct user_pt_regs, ctr)); | |
357 | BUILD_BUG_ON(offsetof(struct pt_regs, link) != | |
358 | offsetof(struct user_pt_regs, link)); | |
359 | BUILD_BUG_ON(offsetof(struct pt_regs, xer) != | |
360 | offsetof(struct user_pt_regs, xer)); | |
361 | BUILD_BUG_ON(offsetof(struct pt_regs, ccr) != | |
362 | offsetof(struct user_pt_regs, ccr)); | |
363 | #ifdef __powerpc64__ | |
364 | BUILD_BUG_ON(offsetof(struct pt_regs, softe) != | |
365 | offsetof(struct user_pt_regs, softe)); | |
366 | #else | |
367 | BUILD_BUG_ON(offsetof(struct pt_regs, mq) != | |
368 | offsetof(struct user_pt_regs, mq)); | |
369 | #endif | |
370 | BUILD_BUG_ON(offsetof(struct pt_regs, trap) != | |
371 | offsetof(struct user_pt_regs, trap)); | |
372 | BUILD_BUG_ON(offsetof(struct pt_regs, dar) != | |
373 | offsetof(struct user_pt_regs, dar)); | |
4872cbd0 XS |
374 | BUILD_BUG_ON(offsetof(struct pt_regs, dear) != |
375 | offsetof(struct user_pt_regs, dar)); | |
002af939 ME |
376 | BUILD_BUG_ON(offsetof(struct pt_regs, dsisr) != |
377 | offsetof(struct user_pt_regs, dsisr)); | |
4f8e78c0 XS |
378 | BUILD_BUG_ON(offsetof(struct pt_regs, esr) != |
379 | offsetof(struct user_pt_regs, dsisr)); | |
002af939 ME |
380 | BUILD_BUG_ON(offsetof(struct pt_regs, result) != |
381 | offsetof(struct user_pt_regs, result)); | |
382 | ||
383 | BUILD_BUG_ON(sizeof(struct user_pt_regs) > sizeof(struct pt_regs)); | |
b9e0805a ME |
384 | |
385 | // Now check that the pt_regs offsets match the uapi #defines | |
386 | #define CHECK_REG(_pt, _reg) \ | |
387 | BUILD_BUG_ON(_pt != (offsetof(struct user_pt_regs, _reg) / \ | |
388 | sizeof(unsigned long))); | |
389 | ||
390 | CHECK_REG(PT_R0, gpr[0]); | |
391 | CHECK_REG(PT_R1, gpr[1]); | |
392 | CHECK_REG(PT_R2, gpr[2]); | |
393 | CHECK_REG(PT_R3, gpr[3]); | |
394 | CHECK_REG(PT_R4, gpr[4]); | |
395 | CHECK_REG(PT_R5, gpr[5]); | |
396 | CHECK_REG(PT_R6, gpr[6]); | |
397 | CHECK_REG(PT_R7, gpr[7]); | |
398 | CHECK_REG(PT_R8, gpr[8]); | |
399 | CHECK_REG(PT_R9, gpr[9]); | |
400 | CHECK_REG(PT_R10, gpr[10]); | |
401 | CHECK_REG(PT_R11, gpr[11]); | |
402 | CHECK_REG(PT_R12, gpr[12]); | |
403 | CHECK_REG(PT_R13, gpr[13]); | |
404 | CHECK_REG(PT_R14, gpr[14]); | |
405 | CHECK_REG(PT_R15, gpr[15]); | |
406 | CHECK_REG(PT_R16, gpr[16]); | |
407 | CHECK_REG(PT_R17, gpr[17]); | |
408 | CHECK_REG(PT_R18, gpr[18]); | |
409 | CHECK_REG(PT_R19, gpr[19]); | |
410 | CHECK_REG(PT_R20, gpr[20]); | |
411 | CHECK_REG(PT_R21, gpr[21]); | |
412 | CHECK_REG(PT_R22, gpr[22]); | |
413 | CHECK_REG(PT_R23, gpr[23]); | |
414 | CHECK_REG(PT_R24, gpr[24]); | |
415 | CHECK_REG(PT_R25, gpr[25]); | |
416 | CHECK_REG(PT_R26, gpr[26]); | |
417 | CHECK_REG(PT_R27, gpr[27]); | |
418 | CHECK_REG(PT_R28, gpr[28]); | |
419 | CHECK_REG(PT_R29, gpr[29]); | |
420 | CHECK_REG(PT_R30, gpr[30]); | |
421 | CHECK_REG(PT_R31, gpr[31]); | |
422 | CHECK_REG(PT_NIP, nip); | |
423 | CHECK_REG(PT_MSR, msr); | |
424 | CHECK_REG(PT_ORIG_R3, orig_gpr3); | |
425 | CHECK_REG(PT_CTR, ctr); | |
426 | CHECK_REG(PT_LNK, link); | |
427 | CHECK_REG(PT_XER, xer); | |
428 | CHECK_REG(PT_CCR, ccr); | |
429 | #ifdef CONFIG_PPC64 | |
430 | CHECK_REG(PT_SOFTE, softe); | |
431 | #else | |
432 | CHECK_REG(PT_MQ, mq); | |
433 | #endif | |
434 | CHECK_REG(PT_TRAP, trap); | |
435 | CHECK_REG(PT_DAR, dar); | |
436 | CHECK_REG(PT_DSISR, dsisr); | |
437 | CHECK_REG(PT_RESULT, result); | |
438 | #undef CHECK_REG | |
439 | ||
440 | BUILD_BUG_ON(PT_REGS_COUNT != sizeof(struct user_pt_regs) / sizeof(unsigned long)); | |
441 | ||
442 | /* | |
443 | * PT_DSCR isn't a real reg, but it's important that it doesn't overlap the | |
444 | * real registers. | |
445 | */ | |
446 | BUILD_BUG_ON(PT_DSCR < sizeof(struct user_pt_regs) / sizeof(unsigned long)); | |
8e127844 ME |
447 | |
448 | // ptrace_get/put_fpr() rely on PPC32 and VSX being incompatible | |
449 | BUILD_BUG_ON(IS_ENABLED(CONFIG_PPC32) && IS_ENABLED(CONFIG_VSX)); | |
002af939 | 450 | } |