]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/sh/kernel/ptrace.c | |
3 | * | |
4 | * Original x86 implementation: | |
5 | * By Ross Biro 1/23/92 | |
6 | * edited by Linus Torvalds | |
7 | * | |
8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka | |
1322b9de | 9 | * Audit support: Yuichi Nakamura <ynakam@hitachisoft.jp> |
1da177e4 | 10 | */ |
1da177e4 LT |
11 | #include <linux/kernel.h> |
12 | #include <linux/sched.h> | |
13 | #include <linux/mm.h> | |
14 | #include <linux/smp.h> | |
1da177e4 LT |
15 | #include <linux/errno.h> |
16 | #include <linux/ptrace.h> | |
17 | #include <linux/user.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/security.h> | |
7ed20e1a | 20 | #include <linux/signal.h> |
9432f968 | 21 | #include <linux/io.h> |
1322b9de | 22 | #include <linux/audit.h> |
c4637d47 | 23 | #include <linux/seccomp.h> |
ab99c733 | 24 | #include <linux/tracehook.h> |
1da177e4 LT |
25 | #include <asm/uaccess.h> |
26 | #include <asm/pgtable.h> | |
27 | #include <asm/system.h> | |
28 | #include <asm/processor.h> | |
29 | #include <asm/mmu_context.h> | |
30 | ||
31 | /* | |
32 | * does not yet catch signals sent when the child dies. | |
33 | * in exit.c or in signal.c. | |
34 | */ | |
35 | ||
36 | /* | |
37 | * This routine will get a word off of the process kernel stack. | |
38 | */ | |
39 | static inline int get_stack_long(struct task_struct *task, int offset) | |
40 | { | |
41 | unsigned char *stack; | |
42 | ||
3cf0f4ec | 43 | stack = (unsigned char *)task_pt_regs(task); |
1da177e4 LT |
44 | stack += offset; |
45 | return (*((int *)stack)); | |
46 | } | |
47 | ||
48 | /* | |
49 | * This routine will put a word on the process kernel stack. | |
50 | */ | |
51 | static inline int put_stack_long(struct task_struct *task, int offset, | |
52 | unsigned long data) | |
53 | { | |
54 | unsigned char *stack; | |
55 | ||
3cf0f4ec | 56 | stack = (unsigned char *)task_pt_regs(task); |
1da177e4 LT |
57 | stack += offset; |
58 | *(unsigned long *) stack = data; | |
59 | return 0; | |
60 | } | |
61 | ||
c459dbf2 PM |
62 | void user_enable_single_step(struct task_struct *child) |
63 | { | |
64 | struct pt_regs *regs = task_pt_regs(child); | |
65 | long pc; | |
66 | ||
67 | pc = get_stack_long(child, (long)®s->pc); | |
68 | ||
69 | /* Next scheduling will set up UBC */ | |
70 | if (child->thread.ubc_pc == 0) | |
71 | ubc_usercnt += 1; | |
72 | ||
73 | child->thread.ubc_pc = pc; | |
74 | ||
75 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | |
76 | } | |
77 | ||
78 | void user_disable_single_step(struct task_struct *child) | |
9432f968 SM |
79 | { |
80 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | |
81 | ||
82 | /* | |
83 | * Ensure the UBC is not programmed at the next context switch. | |
84 | * | |
85 | * Normally this is not needed but there are sequences such as | |
86 | * singlestep, signal delivery, and continue that leave the | |
87 | * ubc_pc non-zero leading to spurious SIGTRAPs. | |
88 | */ | |
89 | if (child->thread.ubc_pc != 0) { | |
90 | ubc_usercnt -= 1; | |
91 | child->thread.ubc_pc = 0; | |
92 | } | |
93 | } | |
94 | ||
1da177e4 LT |
95 | /* |
96 | * Called by kernel/ptrace.c when detaching.. | |
97 | * | |
98 | * Make sure single step bits etc are not set. | |
99 | */ | |
100 | void ptrace_disable(struct task_struct *child) | |
101 | { | |
c459dbf2 | 102 | user_disable_single_step(child); |
1da177e4 LT |
103 | } |
104 | ||
481bed45 | 105 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
1da177e4 | 106 | { |
1da177e4 LT |
107 | struct user * dummy = NULL; |
108 | int ret; | |
109 | ||
1da177e4 | 110 | switch (request) { |
1da177e4 LT |
111 | /* read the word at location addr in the USER area. */ |
112 | case PTRACE_PEEKUSR: { | |
113 | unsigned long tmp; | |
114 | ||
115 | ret = -EIO; | |
9432f968 | 116 | if ((addr & 3) || addr < 0 || |
1da177e4 LT |
117 | addr > sizeof(struct user) - 3) |
118 | break; | |
119 | ||
120 | if (addr < sizeof(struct pt_regs)) | |
121 | tmp = get_stack_long(child, addr); | |
122 | else if (addr >= (long) &dummy->fpu && | |
123 | addr < (long) &dummy->u_fpvalid) { | |
124 | if (!tsk_used_math(child)) { | |
125 | if (addr == (long)&dummy->fpu.fpscr) | |
126 | tmp = FPSCR_INIT; | |
127 | else | |
128 | tmp = 0; | |
129 | } else | |
130 | tmp = ((long *)&child->thread.fpu) | |
131 | [(addr - (long)&dummy->fpu) >> 2]; | |
132 | } else if (addr == (long) &dummy->u_fpvalid) | |
133 | tmp = !!tsk_used_math(child); | |
134 | else | |
135 | tmp = 0; | |
e08f457c | 136 | ret = put_user(tmp, (unsigned long __user *)data); |
1da177e4 LT |
137 | break; |
138 | } | |
139 | ||
1da177e4 LT |
140 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ |
141 | ret = -EIO; | |
9432f968 | 142 | if ((addr & 3) || addr < 0 || |
1da177e4 LT |
143 | addr > sizeof(struct user) - 3) |
144 | break; | |
145 | ||
146 | if (addr < sizeof(struct pt_regs)) | |
147 | ret = put_stack_long(child, addr, data); | |
148 | else if (addr >= (long) &dummy->fpu && | |
149 | addr < (long) &dummy->u_fpvalid) { | |
150 | set_stopped_child_used_math(child); | |
151 | ((long *)&child->thread.fpu) | |
152 | [(addr - (long)&dummy->fpu) >> 2] = data; | |
153 | ret = 0; | |
154 | } else if (addr == (long) &dummy->u_fpvalid) { | |
155 | conditional_stopped_child_used_math(data, child); | |
156 | ret = 0; | |
157 | } | |
158 | break; | |
159 | ||
1da177e4 LT |
160 | #ifdef CONFIG_SH_DSP |
161 | case PTRACE_GETDSPREGS: { | |
162 | unsigned long dp; | |
163 | ||
164 | ret = -EIO; | |
165 | dp = ((unsigned long) child) + THREAD_SIZE - | |
166 | sizeof(struct pt_dspregs); | |
167 | if (*((int *) (dp - 4)) == SR_FD) { | |
09061850 | 168 | copy_to_user((void *)addr, (void *) dp, |
1da177e4 LT |
169 | sizeof(struct pt_dspregs)); |
170 | ret = 0; | |
171 | } | |
172 | break; | |
173 | } | |
174 | ||
175 | case PTRACE_SETDSPREGS: { | |
176 | unsigned long dp; | |
1da177e4 LT |
177 | |
178 | ret = -EIO; | |
179 | dp = ((unsigned long) child) + THREAD_SIZE - | |
180 | sizeof(struct pt_dspregs); | |
181 | if (*((int *) (dp - 4)) == SR_FD) { | |
09061850 | 182 | copy_from_user((void *) dp, (void *)addr, |
1da177e4 LT |
183 | sizeof(struct pt_dspregs)); |
184 | ret = 0; | |
185 | } | |
186 | break; | |
187 | } | |
3bc24a1a PM |
188 | #endif |
189 | #ifdef CONFIG_BINFMT_ELF_FDPIC | |
190 | case PTRACE_GETFDPIC: { | |
191 | unsigned long tmp = 0; | |
192 | ||
193 | switch (addr) { | |
194 | case PTRACE_GETFDPIC_EXEC: | |
195 | tmp = child->mm->context.exec_fdpic_loadmap; | |
196 | break; | |
197 | case PTRACE_GETFDPIC_INTERP: | |
198 | tmp = child->mm->context.interp_fdpic_loadmap; | |
199 | break; | |
200 | default: | |
201 | break; | |
202 | } | |
203 | ||
204 | ret = 0; | |
205 | if (put_user(tmp, (unsigned long *) data)) { | |
206 | ret = -EFAULT; | |
207 | break; | |
208 | } | |
209 | break; | |
210 | } | |
1da177e4 LT |
211 | #endif |
212 | default: | |
213 | ret = ptrace_request(child, request, addr, data); | |
214 | break; | |
215 | } | |
481bed45 | 216 | |
1da177e4 LT |
217 | return ret; |
218 | } | |
219 | ||
9e5e2117 PM |
220 | static inline int audit_arch(void) |
221 | { | |
222 | int arch = EM_SH; | |
223 | ||
224 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | |
225 | arch |= __AUDIT_ARCH_LE; | |
226 | #endif | |
227 | ||
228 | return arch; | |
229 | } | |
230 | ||
ab99c733 | 231 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) |
1da177e4 | 232 | { |
ab99c733 | 233 | long ret = 0; |
1da177e4 | 234 | |
c4637d47 PM |
235 | secure_computing(regs->regs[0]); |
236 | ||
ab99c733 PM |
237 | if (test_thread_flag(TIF_SYSCALL_TRACE) && |
238 | tracehook_report_syscall_entry(regs)) | |
239 | /* | |
240 | * Tracing decided this syscall should not happen. | |
241 | * We'll return a bogus call number to get an ENOSYS | |
242 | * error, but leave the original number in regs->regs[0]. | |
243 | */ | |
244 | ret = -1L; | |
1322b9de | 245 | |
ab99c733 | 246 | if (unlikely(current->audit_context)) |
9e5e2117 | 247 | audit_syscall_entry(audit_arch(), regs->regs[3], |
1322b9de YN |
248 | regs->regs[4], regs->regs[5], |
249 | regs->regs[6], regs->regs[7]); | |
250 | ||
ab99c733 PM |
251 | return ret ?: regs->regs[0]; |
252 | } | |
253 | ||
254 | asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) | |
255 | { | |
256 | int step; | |
257 | ||
258 | if (unlikely(current->audit_context)) | |
259 | audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]), | |
260 | regs->regs[0]); | |
261 | ||
262 | step = test_thread_flag(TIF_SINGLESTEP); | |
263 | if (step || test_thread_flag(TIF_SYSCALL_TRACE)) | |
264 | tracehook_report_syscall_exit(regs, step); | |
1da177e4 | 265 | } |