diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2007-11-28 19:21:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-11-29 12:24:52 -0500 |
commit | 19fd4bb2a0cfede054e4904e0b167e0ca4f36cc7 (patch) | |
tree | e37d0ee2c7ea08f6d8721a4e9e807f85994b608a /fs/proc/base.c | |
parent | c895078355b6b6e05c60aa205892526dd3390f0a (diff) |
proc: remove races from proc_id_readdir()
Oleg noticed that the call of task_pid_nr_ns() in proc_pid_readdir
is racy with respect to tasks exiting.
After a bit of examination it also appears that the call itself
is completely unnecessary.
So to fix the problem this patch modifies next_tgid() to return
both a tgid and the task struct in question.
A structure is introduced to return these values because it is
slightly cleaner and easier to optimize, and the resulting code
is a little shorter.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 51 |
1 files changed, 28 insertions, 23 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index a17c26859074..02a63ac04178 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2411,19 +2411,23 @@ out: | |||
2411 | * Find the first task with tgid >= tgid | 2411 | * Find the first task with tgid >= tgid |
2412 | * | 2412 | * |
2413 | */ | 2413 | */ |
2414 | static struct task_struct *next_tgid(unsigned int tgid, | 2414 | struct tgid_iter { |
2415 | struct pid_namespace *ns) | 2415 | unsigned int tgid; |
2416 | { | ||
2417 | struct task_struct *task; | 2416 | struct task_struct *task; |
2417 | }; | ||
2418 | static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter) | ||
2419 | { | ||
2418 | struct pid *pid; | 2420 | struct pid *pid; |
2419 | 2421 | ||
2422 | if (iter.task) | ||
2423 | put_task_struct(iter.task); | ||
2420 | rcu_read_lock(); | 2424 | rcu_read_lock(); |
2421 | retry: | 2425 | retry: |
2422 | task = NULL; | 2426 | iter.task = NULL; |
2423 | pid = find_ge_pid(tgid, ns); | 2427 | pid = find_ge_pid(iter.tgid, ns); |
2424 | if (pid) { | 2428 | if (pid) { |
2425 | tgid = pid_nr_ns(pid, ns) + 1; | 2429 | iter.tgid = pid_nr_ns(pid, ns); |
2426 | task = pid_task(pid, PIDTYPE_PID); | 2430 | iter.task = pid_task(pid, PIDTYPE_PID); |
2427 | /* What we to know is if the pid we have find is the | 2431 | /* What we to know is if the pid we have find is the |
2428 | * pid of a thread_group_leader. Testing for task | 2432 | * pid of a thread_group_leader. Testing for task |
2429 | * being a thread_group_leader is the obvious thing | 2433 | * being a thread_group_leader is the obvious thing |
@@ -2436,23 +2440,25 @@ retry: | |||
2436 | * found doesn't happen to be a thread group leader. | 2440 | * found doesn't happen to be a thread group leader. |
2437 | * As we don't care in the case of readdir. | 2441 | * As we don't care in the case of readdir. |
2438 | */ | 2442 | */ |
2439 | if (!task || !has_group_leader_pid(task)) | 2443 | if (!iter.task || !has_group_leader_pid(iter.task)) { |
2444 | iter.tgid += 1; | ||
2440 | goto retry; | 2445 | goto retry; |
2441 | get_task_struct(task); | 2446 | } |
2447 | get_task_struct(iter.task); | ||
2442 | } | 2448 | } |
2443 | rcu_read_unlock(); | 2449 | rcu_read_unlock(); |
2444 | return task; | 2450 | return iter; |
2445 | } | 2451 | } |
2446 | 2452 | ||
2447 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + ARRAY_SIZE(proc_base_stuff)) | 2453 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + ARRAY_SIZE(proc_base_stuff)) |
2448 | 2454 | ||
2449 | static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldir, | 2455 | static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldir, |
2450 | struct task_struct *task, int tgid) | 2456 | struct tgid_iter iter) |
2451 | { | 2457 | { |
2452 | char name[PROC_NUMBUF]; | 2458 | char name[PROC_NUMBUF]; |
2453 | int len = snprintf(name, sizeof(name), "%d", tgid); | 2459 | int len = snprintf(name, sizeof(name), "%d", iter.tgid); |
2454 | return proc_fill_cache(filp, dirent, filldir, name, len, | 2460 | return proc_fill_cache(filp, dirent, filldir, name, len, |
2455 | proc_pid_instantiate, task, NULL); | 2461 | proc_pid_instantiate, iter.task, NULL); |
2456 | } | 2462 | } |
2457 | 2463 | ||
2458 | /* for the /proc/ directory itself, after non-process stuff has been done */ | 2464 | /* for the /proc/ directory itself, after non-process stuff has been done */ |
@@ -2460,8 +2466,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
2460 | { | 2466 | { |
2461 | unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; | 2467 | unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; |
2462 | struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode); | 2468 | struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode); |
2463 | struct task_struct *task; | 2469 | struct tgid_iter iter; |
2464 | int tgid; | ||
2465 | struct pid_namespace *ns; | 2470 | struct pid_namespace *ns; |
2466 | 2471 | ||
2467 | if (!reaper) | 2472 | if (!reaper) |
@@ -2474,14 +2479,14 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
2474 | } | 2479 | } |
2475 | 2480 | ||
2476 | ns = filp->f_dentry->d_sb->s_fs_info; | 2481 | ns = filp->f_dentry->d_sb->s_fs_info; |
2477 | tgid = filp->f_pos - TGID_OFFSET; | 2482 | iter.task = NULL; |
2478 | for (task = next_tgid(tgid, ns); | 2483 | iter.tgid = filp->f_pos - TGID_OFFSET; |
2479 | task; | 2484 | for (iter = next_tgid(ns, iter); |
2480 | put_task_struct(task), task = next_tgid(tgid + 1, ns)) { | 2485 | iter.task; |
2481 | tgid = task_pid_nr_ns(task, ns); | 2486 | iter.tgid += 1, iter = next_tgid(ns, iter)) { |
2482 | filp->f_pos = tgid + TGID_OFFSET; | 2487 | filp->f_pos = iter.tgid + TGID_OFFSET; |
2483 | if (proc_pid_fill_cache(filp, dirent, filldir, task, tgid) < 0) { | 2488 | if (proc_pid_fill_cache(filp, dirent, filldir, iter) < 0) { |
2484 | put_task_struct(task); | 2489 | put_task_struct(iter.task); |
2485 | goto out; | 2490 | goto out; |
2486 | } | 2491 | } |
2487 | } | 2492 | } |