Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 139694
b: refs/heads/master
c: 39c626a
h: refs/heads/master
v: v3
  • Loading branch information
Oleg Nesterov authored and Linus Torvalds committed Apr 3, 2009
1 parent 60f0e92 commit 385c602
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 93 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 7f5d3652d469cdf9eb2365dfea7ce3fb9e1409cc
refs/heads/master: 39c626ae47c469abdfd30c6e42eff884931380d6
2 changes: 1 addition & 1 deletion trunk/include/linux/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ extern void ptrace_notify(int exit_code);
extern void __ptrace_link(struct task_struct *child,
struct task_struct *new_parent);
extern void __ptrace_unlink(struct task_struct *child);
extern int __ptrace_detach(struct task_struct *tracer, struct task_struct *p);
extern void exit_ptrace(struct task_struct *tracer);
extern void ptrace_fork(struct task_struct *task, unsigned long clone_flags);
#define PTRACE_MODE_READ 1
#define PTRACE_MODE_ATTACH 2
Expand Down
5 changes: 5 additions & 0 deletions trunk/include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,11 @@ static inline int thread_group_empty(struct task_struct *p)
#define delay_group_leader(p) \
(thread_group_leader(p) && !thread_group_empty(p))

static inline int task_detached(struct task_struct *p)
{
return p->exit_signal == -1;
}

/*
* Protects ->fs, ->files, ->mm, ->group_info, ->comm, keyring
* subscriptions and synchronises with wait4(). Also used in procfs. Also
Expand Down
95 changes: 6 additions & 89 deletions trunk/kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ DEFINE_TRACE(sched_process_wait);

static void exit_mm(struct task_struct * tsk);

static inline int task_detached(struct task_struct *p)
{
return p->exit_signal == -1;
}

static void __unhash_process(struct task_struct *p)
{
nr_threads--;
Expand Down Expand Up @@ -731,85 +726,6 @@ static void exit_mm(struct task_struct * tsk)
mmput(mm);
}

/*
* Called with irqs disabled, returns true if childs should reap themselves.
*/
static int ignoring_children(struct sighand_struct *sigh)
{
int ret;
spin_lock(&sigh->siglock);
ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
(sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
spin_unlock(&sigh->siglock);
return ret;
}

/* Returns nonzero if the tracee should be released. */
int __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
{
__ptrace_unlink(p);

if (p->exit_state != EXIT_ZOMBIE)
return 0;
/*
* If it's a zombie, our attachedness prevented normal
* parent notification or self-reaping. Do notification
* now if it would have happened earlier. If it should
* reap itself we return true.
*
* If it's our own child, there is no notification to do.
* But if our normal children self-reap, then this child
* was prevented by ptrace and we must reap it now.
*/
if (!task_detached(p) && thread_group_empty(p)) {
if (!same_thread_group(p->real_parent, tracer))
do_notify_parent(p, p->exit_signal);
else if (ignoring_children(tracer->sighand))
p->exit_signal = -1;
}

if (!task_detached(p))
return 0;

/* Mark it as in the process of being reaped. */
p->exit_state = EXIT_DEAD;
return 1;
}

/*
* Detach all tasks we were using ptrace on.
* Any that need to be release_task'd are put on the @dead list.
*
* Called with write_lock(&tasklist_lock) held.
*/
static void ptrace_exit(struct task_struct *parent, struct list_head *dead)
{
struct task_struct *p, *n;

list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) {
if (__ptrace_detach(parent, p))
list_add(&p->ptrace_entry, dead);
}
}

/*
* Finish up exit-time ptrace cleanup.
*
* Called without locks.
*/
static void ptrace_exit_finish(struct task_struct *parent,
struct list_head *dead)
{
struct task_struct *p, *n;

BUG_ON(!list_empty(&parent->ptraced));

list_for_each_entry_safe(p, n, dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
release_task(p);
}
}

/* Returns nonzero if the child should be released. */
static int reparent_thread(struct task_struct *p, struct task_struct *father)
{
Expand Down Expand Up @@ -894,12 +810,10 @@ static void forget_original_parent(struct task_struct *father)
struct task_struct *p, *n, *reaper;
LIST_HEAD(ptrace_dead);

exit_ptrace(father);

write_lock_irq(&tasklist_lock);
reaper = find_new_reaper(father);
/*
* First clean up ptrace if we were using it.
*/
ptrace_exit(father, &ptrace_dead);

list_for_each_entry_safe(p, n, &father->children, sibling) {
p->real_parent = reaper;
Expand All @@ -914,7 +828,10 @@ static void forget_original_parent(struct task_struct *father)
write_unlock_irq(&tasklist_lock);
BUG_ON(!list_empty(&father->children));

ptrace_exit_finish(father, &ptrace_dead);
list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
release_task(p);
}
}

/*
Expand Down
78 changes: 76 additions & 2 deletions trunk/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,57 @@ int ptrace_attach(struct task_struct *task)
return retval;
}

/*
* Called with irqs disabled, returns true if childs should reap themselves.
*/
static int ignoring_children(struct sighand_struct *sigh)
{
int ret;
spin_lock(&sigh->siglock);
ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
(sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
spin_unlock(&sigh->siglock);
return ret;
}

/*
* Called with tasklist_lock held for writing.
* Unlink a traced task, and clean it up if it was a traced zombie.
* Return true if it needs to be reaped with release_task().
* (We can't call release_task() here because we already hold tasklist_lock.)
*
* If it's a zombie, our attachedness prevented normal parent notification
* or self-reaping. Do notification now if it would have happened earlier.
* If it should reap itself, return true.
*
* If it's our own child, there is no notification to do.
* But if our normal children self-reap, then this child
* was prevented by ptrace and we must reap it now.
*/
static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
{
__ptrace_unlink(p);

if (p->exit_state == EXIT_ZOMBIE) {
if (!task_detached(p) && thread_group_empty(p)) {
if (!same_thread_group(p->real_parent, tracer))
do_notify_parent(p, p->exit_signal);
else if (ignoring_children(tracer->sighand))
p->exit_signal = -1;
}
if (task_detached(p)) {
/* Mark it as in the process of being reaped. */
p->exit_state = EXIT_DEAD;
return true;
}
}

return false;
}

int ptrace_detach(struct task_struct *child, unsigned int data)
{
int dead = 0;
bool dead = false;

if (!valid_signal(data))
return -EIO;
Expand All @@ -247,7 +295,10 @@ int ptrace_detach(struct task_struct *child, unsigned int data)
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);

write_lock_irq(&tasklist_lock);
/* protect against de_thread()->release_task() */
/*
* This child can be already killed. Make sure de_thread() or
* our sub-thread doing do_wait() didn't do release_task() yet.
*/
if (child->ptrace) {
child->exit_code = data;

Expand All @@ -264,6 +315,29 @@ int ptrace_detach(struct task_struct *child, unsigned int data)
return 0;
}

/*
* Detach all tasks we were using ptrace on.
*/
void exit_ptrace(struct task_struct *tracer)
{
struct task_struct *p, *n;
LIST_HEAD(ptrace_dead);

write_lock_irq(&tasklist_lock);
list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
if (__ptrace_detach(tracer, p))
list_add(&p->ptrace_entry, &ptrace_dead);
}
write_unlock_irq(&tasklist_lock);

BUG_ON(!list_empty(&tracer->ptraced));

list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
release_task(p);
}
}

int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
{
int copied = 0;
Expand Down

0 comments on commit 385c602

Please sign in to comment.