diff options
Diffstat (limited to 'arch/sh/kernel/ptrace.c')
-rw-r--r-- | arch/sh/kernel/ptrace.c | 45 |
1 files changed, 33 insertions, 12 deletions
diff --git a/arch/sh/kernel/ptrace.c b/arch/sh/kernel/ptrace.c index 04ca13a041c1..855f7246cfff 100644 --- a/arch/sh/kernel/ptrace.c +++ b/arch/sh/kernel/ptrace.c | |||
@@ -8,7 +8,6 @@ | |||
8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka | 8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka |
9 | * | 9 | * |
10 | */ | 10 | */ |
11 | |||
12 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
13 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
14 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
@@ -20,8 +19,7 @@ | |||
20 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
21 | #include <linux/security.h> | 20 | #include <linux/security.h> |
22 | #include <linux/signal.h> | 21 | #include <linux/signal.h> |
23 | 22 | #include <linux/io.h> | |
24 | #include <asm/io.h> | ||
25 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
26 | #include <asm/pgtable.h> | 24 | #include <asm/pgtable.h> |
27 | #include <asm/system.h> | 25 | #include <asm/system.h> |
@@ -59,6 +57,23 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
59 | return 0; | 57 | return 0; |
60 | } | 58 | } |
61 | 59 | ||
60 | static void ptrace_disable_singlestep(struct task_struct *child) | ||
61 | { | ||
62 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
63 | |||
64 | /* | ||
65 | * Ensure the UBC is not programmed at the next context switch. | ||
66 | * | ||
67 | * Normally this is not needed but there are sequences such as | ||
68 | * singlestep, signal delivery, and continue that leave the | ||
69 | * ubc_pc non-zero leading to spurious SIGTRAPs. | ||
70 | */ | ||
71 | if (child->thread.ubc_pc != 0) { | ||
72 | ubc_usercnt -= 1; | ||
73 | child->thread.ubc_pc = 0; | ||
74 | } | ||
75 | } | ||
76 | |||
62 | /* | 77 | /* |
63 | * Called by kernel/ptrace.c when detaching.. | 78 | * Called by kernel/ptrace.c when detaching.. |
64 | * | 79 | * |
@@ -66,7 +81,7 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
66 | */ | 81 | */ |
67 | void ptrace_disable(struct task_struct *child) | 82 | void ptrace_disable(struct task_struct *child) |
68 | { | 83 | { |
69 | /* nothing to do.. */ | 84 | ptrace_disable_singlestep(child); |
70 | } | 85 | } |
71 | 86 | ||
72 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 87 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
@@ -76,7 +91,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
76 | 91 | ||
77 | switch (request) { | 92 | switch (request) { |
78 | /* when I and D space are separate, these will need to be fixed. */ | 93 | /* when I and D space are separate, these will need to be fixed. */ |
79 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 94 | case PTRACE_PEEKTEXT: /* read word at location addr. */ |
80 | case PTRACE_PEEKDATA: { | 95 | case PTRACE_PEEKDATA: { |
81 | unsigned long tmp; | 96 | unsigned long tmp; |
82 | int copied; | 97 | int copied; |
@@ -94,7 +109,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
94 | unsigned long tmp; | 109 | unsigned long tmp; |
95 | 110 | ||
96 | ret = -EIO; | 111 | ret = -EIO; |
97 | if ((addr & 3) || addr < 0 || | 112 | if ((addr & 3) || addr < 0 || |
98 | addr > sizeof(struct user) - 3) | 113 | addr > sizeof(struct user) - 3) |
99 | break; | 114 | break; |
100 | 115 | ||
@@ -129,7 +144,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
129 | 144 | ||
130 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | 145 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ |
131 | ret = -EIO; | 146 | ret = -EIO; |
132 | if ((addr & 3) || addr < 0 || | 147 | if ((addr & 3) || addr < 0 || |
133 | addr > sizeof(struct user) - 3) | 148 | addr > sizeof(struct user) - 3) |
134 | break; | 149 | break; |
135 | 150 | ||
@@ -156,6 +171,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
156 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 171 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
157 | else | 172 | else |
158 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 173 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
174 | |||
175 | ptrace_disable_singlestep(child); | ||
176 | |||
159 | child->exit_code = data; | 177 | child->exit_code = data; |
160 | wake_up_process(child); | 178 | wake_up_process(child); |
161 | ret = 0; | 179 | ret = 0; |
@@ -163,14 +181,15 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
163 | } | 181 | } |
164 | 182 | ||
165 | /* | 183 | /* |
166 | * make the child exit. Best I can do is send it a sigkill. | 184 | * make the child exit. Best I can do is send it a sigkill. |
167 | * perhaps it should be put in the status that it wants to | 185 | * perhaps it should be put in the status that it wants to |
168 | * exit. | 186 | * exit. |
169 | */ | 187 | */ |
170 | case PTRACE_KILL: { | 188 | case PTRACE_KILL: { |
171 | ret = 0; | 189 | ret = 0; |
172 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | 190 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ |
173 | break; | 191 | break; |
192 | ptrace_disable_singlestep(child); | ||
174 | child->exit_code = SIGKILL; | 193 | child->exit_code = SIGKILL; |
175 | wake_up_process(child); | 194 | wake_up_process(child); |
176 | break; | 195 | break; |
@@ -196,6 +215,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
196 | ubc_usercnt += 1; | 215 | ubc_usercnt += 1; |
197 | child->thread.ubc_pc = pc; | 216 | child->thread.ubc_pc = pc; |
198 | 217 | ||
218 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
199 | child->exit_code = data; | 219 | child->exit_code = data; |
200 | /* give it a chance to run. */ | 220 | /* give it a chance to run. */ |
201 | wake_up_process(child); | 221 | wake_up_process(child); |
@@ -248,14 +268,15 @@ asmlinkage void do_syscall_trace(void) | |||
248 | { | 268 | { |
249 | struct task_struct *tsk = current; | 269 | struct task_struct *tsk = current; |
250 | 270 | ||
251 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | 271 | if (!test_thread_flag(TIF_SYSCALL_TRACE) && |
272 | !test_thread_flag(TIF_SINGLESTEP)) | ||
252 | return; | 273 | return; |
253 | if (!(tsk->ptrace & PT_PTRACED)) | 274 | if (!(tsk->ptrace & PT_PTRACED)) |
254 | return; | 275 | return; |
255 | /* the 0x80 provides a way for the tracing parent to distinguish | 276 | /* the 0x80 provides a way for the tracing parent to distinguish |
256 | between a syscall stop and SIGTRAP delivery */ | 277 | between a syscall stop and SIGTRAP delivery */ |
257 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | 278 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) && |
258 | ? 0x80 : 0)); | 279 | !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0)); |
259 | 280 | ||
260 | /* | 281 | /* |
261 | * this isn't the same as continuing with a signal, but it will do | 282 | * this isn't the same as continuing with a signal, but it will do |