aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2009-06-17 19:27:33 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-18 16:03:51 -0400
commit4b105cbbaf7c06e01c27391957dc3c446328d087 (patch)
tree4b6a65061a7bf46baea9d8867fdda0c22c8a3d45
parentf2f0b00ad61d53adfecb8bdf8f3cf8f05f6ed548 (diff)
ptrace: do not use task_lock() for attach
Remove the "Nasty, nasty" lock dance in ptrace_attach()/ptrace_traceme() - from now task_lock() has nothing to do with ptrace at all. With the recent changes nobody uses task_lock() to serialize with ptrace, but in fact it was never needed and it was never used consistently. However ptrace_attach() calls __ptrace_may_access() and needs task_lock() to pin task->mm for get_dumpable(). But we can call __ptrace_may_access() before we take tasklist_lock, ->cred_exec_mutex protects us against do_execve() path which can change creds and MMF_DUMP* flags. (ugly, but we can't use ptrace_may_access() because it hides the error code, so we have to take task_lock() and use __ptrace_may_access()). NOTE: this change assumes that LSM hooks, security_ptrace_may_access() and security_ptrace_traceme(), can be called without task_lock() held. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Cc: Chris Wright <chrisw@sous-sol.org> Acked-by: Roland McGrath <roland@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/ptrace.c59
1 files changed, 13 insertions, 46 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 12e21a949db1..38fdfea1a15a 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -167,7 +167,6 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode)
167int ptrace_attach(struct task_struct *task) 167int ptrace_attach(struct task_struct *task)
168{ 168{
169 int retval; 169 int retval;
170 unsigned long flags;
171 170
172 audit_ptrace(task); 171 audit_ptrace(task);
173 172
@@ -185,34 +184,19 @@ int ptrace_attach(struct task_struct *task)
185 retval = mutex_lock_interruptible(&task->cred_guard_mutex); 184 retval = mutex_lock_interruptible(&task->cred_guard_mutex);
186 if (retval < 0) 185 if (retval < 0)
187 goto out; 186 goto out;
188repeat:
189 /*
190 * Nasty, nasty.
191 *
192 * We want to hold both the task-lock and the
193 * tasklist_lock for writing at the same time.
194 * But that's against the rules (tasklist_lock
195 * is taken for reading by interrupts on other
196 * cpu's that may have task_lock).
197 */
198 task_lock(task);
199 if (!write_trylock_irqsave(&tasklist_lock, flags)) {
200 task_unlock(task);
201 do {
202 cpu_relax();
203 } while (!write_can_lock(&tasklist_lock));
204 goto repeat;
205 }
206 187
188 task_lock(task);
207 retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); 189 retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
190 task_unlock(task);
208 if (retval) 191 if (retval)
209 goto bad; 192 goto unlock_creds;
210 193
194 write_lock_irq(&tasklist_lock);
211 retval = -EPERM; 195 retval = -EPERM;
212 if (unlikely(task->exit_state)) 196 if (unlikely(task->exit_state))
213 goto bad; 197 goto unlock_tasklist;
214 if (task->ptrace) 198 if (task->ptrace)
215 goto bad; 199 goto unlock_tasklist;
216 200
217 task->ptrace = PT_PTRACED; 201 task->ptrace = PT_PTRACED;
218 if (capable(CAP_SYS_PTRACE)) 202 if (capable(CAP_SYS_PTRACE))
@@ -222,9 +206,9 @@ repeat:
222 send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); 206 send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
223 207
224 retval = 0; 208 retval = 0;
225bad: 209unlock_tasklist:
226 write_unlock_irqrestore(&tasklist_lock, flags); 210 write_unlock_irq(&tasklist_lock);
227 task_unlock(task); 211unlock_creds:
228 mutex_unlock(&task->cred_guard_mutex); 212 mutex_unlock(&task->cred_guard_mutex);
229out: 213out:
230 return retval; 214 return retval;
@@ -240,26 +224,10 @@ int ptrace_traceme(void)
240{ 224{
241 int ret = -EPERM; 225 int ret = -EPERM;
242 226
243 /* 227 write_lock_irq(&tasklist_lock);
244 * Are we already being traced? 228 /* Are we already being traced? */
245 */
246repeat:
247 task_lock(current);
248 if (!current->ptrace) { 229 if (!current->ptrace) {
249 /*
250 * See ptrace_attach() comments about the locking here.
251 */
252 unsigned long flags;
253 if (!write_trylock_irqsave(&tasklist_lock, flags)) {
254 task_unlock(current);
255 do {
256 cpu_relax();
257 } while (!write_can_lock(&tasklist_lock));
258 goto repeat;
259 }
260
261 ret = security_ptrace_traceme(current->parent); 230 ret = security_ptrace_traceme(current->parent);
262
263 /* 231 /*
264 * Check PF_EXITING to ensure ->real_parent has not passed 232 * Check PF_EXITING to ensure ->real_parent has not passed
265 * exit_ptrace(). Otherwise we don't report the error but 233 * exit_ptrace(). Otherwise we don't report the error but
@@ -269,10 +237,9 @@ repeat:
269 current->ptrace = PT_PTRACED; 237 current->ptrace = PT_PTRACED;
270 __ptrace_link(current, current->real_parent); 238 __ptrace_link(current, current->real_parent);
271 } 239 }
272
273 write_unlock_irqrestore(&tasklist_lock, flags);
274 } 240 }
275 task_unlock(current); 241 write_unlock_irq(&tasklist_lock);
242
276 return ret; 243 return ret;
277} 244}
278 245