diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-04-30 03:54:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 11:29:48 -0400 |
commit | 24336eaeecea860b2a82530e07c80bc7e0558b73 (patch) | |
tree | 12a34660f732e12ba18262083c1a2eea5a824f6a /kernel | |
parent | 65450cebc6a2efde80ed45514f727e6e4dc1eafd (diff) |
pids: introduce change_pid() helper
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 <oleg@tv-sign.ru>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/pid.c | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/kernel/pid.c b/kernel/pid.c index e9a31d362b28..20d59fa2d493 100644 --- a/kernel/pid.c +++ b/kernel/pid.c | |||
@@ -317,7 +317,7 @@ EXPORT_SYMBOL_GPL(find_pid); | |||
317 | /* | 317 | /* |
318 | * attach_pid() must be called with the tasklist_lock write-held. | 318 | * attach_pid() must be called with the tasklist_lock write-held. |
319 | */ | 319 | */ |
320 | int attach_pid(struct task_struct *task, enum pid_type type, | 320 | void attach_pid(struct task_struct *task, enum pid_type type, |
321 | struct pid *pid) | 321 | struct pid *pid) |
322 | { | 322 | { |
323 | struct pid_link *link; | 323 | struct pid_link *link; |
@@ -325,11 +325,10 @@ int attach_pid(struct task_struct *task, enum pid_type type, | |||
325 | link = &task->pids[type]; | 325 | link = &task->pids[type]; |
326 | link->pid = pid; | 326 | link->pid = pid; |
327 | hlist_add_head_rcu(&link->node, &pid->tasks[type]); | 327 | hlist_add_head_rcu(&link->node, &pid->tasks[type]); |
328 | |||
329 | return 0; | ||
330 | } | 328 | } |
331 | 329 | ||
332 | void detach_pid(struct task_struct *task, enum pid_type type) | 330 | static void __change_pid(struct task_struct *task, enum pid_type type, |
331 | struct pid *new) | ||
333 | { | 332 | { |
334 | struct pid_link *link; | 333 | struct pid_link *link; |
335 | struct pid *pid; | 334 | struct pid *pid; |
@@ -339,7 +338,7 @@ void detach_pid(struct task_struct *task, enum pid_type type) | |||
339 | pid = link->pid; | 338 | pid = link->pid; |
340 | 339 | ||
341 | hlist_del_rcu(&link->node); | 340 | hlist_del_rcu(&link->node); |
342 | link->pid = NULL; | 341 | link->pid = new; |
343 | 342 | ||
344 | for (tmp = PIDTYPE_MAX; --tmp >= 0; ) | 343 | for (tmp = PIDTYPE_MAX; --tmp >= 0; ) |
345 | if (!hlist_empty(&pid->tasks[tmp])) | 344 | if (!hlist_empty(&pid->tasks[tmp])) |
@@ -348,6 +347,18 @@ void detach_pid(struct task_struct *task, enum pid_type type) | |||
348 | free_pid(pid); | 347 | free_pid(pid); |
349 | } | 348 | } |
350 | 349 | ||
350 | void detach_pid(struct task_struct *task, enum pid_type type) | ||
351 | { | ||
352 | __change_pid(task, type, NULL); | ||
353 | } | ||
354 | |||
355 | void change_pid(struct task_struct *task, enum pid_type type, | ||
356 | struct pid *pid) | ||
357 | { | ||
358 | __change_pid(task, type, pid); | ||
359 | attach_pid(task, type, pid); | ||
360 | } | ||
361 | |||
351 | /* transfer_pid is an optimization of attach_pid(new), detach_pid(old) */ | 362 | /* transfer_pid is an optimization of attach_pid(new), detach_pid(old) */ |
352 | void transfer_pid(struct task_struct *old, struct task_struct *new, | 363 | void transfer_pid(struct task_struct *old, struct task_struct *new, |
353 | enum pid_type type) | 364 | enum pid_type type) |