Skip to content

Commit

Permalink
pids: introduce change_pid() helper
Browse files Browse the repository at this point in the history
Based on Eric W. Biederman's idea.

Without tasklist_lock held task_session()/task_pgrp() can return NULL if the
caller races with setprgp()/setsid() which does detach_pid() + attach_pid().
This can happen even if task == current.

Intoduce the new helper, change_pid(), which should be used instead.  This way
the caller always sees the special pid != NULL, either old or new.

Also change the prototype of attach_pid(), it always returns 0 and nobody
check the returned value.

Signed-off-by: Oleg Nesterov <[email protected]>
Cc:  "Eric W. Biederman" <[email protected]>
Cc: Pavel Emelyanov <[email protected]>
Cc: Roland McGrath <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
Oleg Nesterov authored and torvalds committed Apr 30, 2008
1 parent 65450ce commit 24336ea
Showing 2 changed files with 20 additions and 7 deletions.
6 changes: 4 additions & 2 deletions include/linux/pid.h
Original file line number Diff line number Diff line change
@@ -89,9 +89,11 @@ extern struct pid *get_task_pid(struct task_struct *task, enum pid_type type);
* attach_pid() and detach_pid() must be called with the tasklist_lock
* write-held.
*/
extern int attach_pid(struct task_struct *task, enum pid_type type,
struct pid *pid);
extern void attach_pid(struct task_struct *task, enum pid_type type,
struct pid *pid);
extern void detach_pid(struct task_struct *task, enum pid_type);
extern void change_pid(struct task_struct *task, enum pid_type,
struct pid *pid);
extern void transfer_pid(struct task_struct *old, struct task_struct *new,
enum pid_type);

21 changes: 16 additions & 5 deletions kernel/pid.c
Original file line number Diff line number Diff line change
@@ -317,19 +317,18 @@ EXPORT_SYMBOL_GPL(find_pid);
/*
* attach_pid() must be called with the tasklist_lock write-held.
*/
int attach_pid(struct task_struct *task, enum pid_type type,
void attach_pid(struct task_struct *task, enum pid_type type,
struct pid *pid)
{
struct pid_link *link;

link = &task->pids[type];
link->pid = pid;
hlist_add_head_rcu(&link->node, &pid->tasks[type]);

return 0;
}

void detach_pid(struct task_struct *task, enum pid_type type)
static void __change_pid(struct task_struct *task, enum pid_type type,
struct pid *new)
{
struct pid_link *link;
struct pid *pid;
@@ -339,7 +338,7 @@ void detach_pid(struct task_struct *task, enum pid_type type)
pid = link->pid;

hlist_del_rcu(&link->node);
link->pid = NULL;
link->pid = new;

for (tmp = PIDTYPE_MAX; --tmp >= 0; )
if (!hlist_empty(&pid->tasks[tmp]))
@@ -348,6 +347,18 @@ void detach_pid(struct task_struct *task, enum pid_type type)
free_pid(pid);
}

void detach_pid(struct task_struct *task, enum pid_type type)
{
__change_pid(task, type, NULL);
}

void change_pid(struct task_struct *task, enum pid_type type,
struct pid *pid)
{
__change_pid(task, type, pid);
attach_pid(task, type, pid);
}

/* transfer_pid is an optimization of attach_pid(new), detach_pid(old) */
void transfer_pid(struct task_struct *old, struct task_struct *new,
enum pid_type type)

0 comments on commit 24336ea

Please sign in to comment.