filp->f_pos not correctly updated in proc_task_readdir

filp->f_pos only get updated at the end of the function. Thus d_off of those
dirents who are in the middle will be 0, and this will cause a problem in
glibc's readdir implementation, specifically endless loop. Because when overflow
occurs, f_pos will be set to next dirent to read, however it will be 0, unless
the next one is the last one. So it will start over again and again.

There is a sample program in man 2 gendents. This is the output of the program
running on a multithread program's task dir before this patch is applied:

  $ ./a.out /proc/3807/task
  --------------- nread=128 ---------------
  i-node#  file type  d_reclen  d_off   d_name
    506442  directory    16          1  .
    506441  directory    16          0  ..
    506443  directory    16          0  3807
    506444  directory    16          0  3809
    506445  directory    16          0  3812
    506446  directory    16          0  3861
    506447  directory    16          0  3862
    506448  directory    16          8  3863

This is the output after this patch is applied

  $ ./a.out /proc/3807/task
  --------------- nread=128 ---------------
  i-node#  file type  d_reclen  d_off   d_name
    506442  directory    16          1  .
    506441  directory    16          2  ..
    506443  directory    16          3  3807
    506444  directory    16          4  3809
    506445  directory    16          5  3812
    506446  directory    16          6  3861
    506447  directory    16          7  3862
    506448  directory    16          8  3863

Signed-off-by: Zhang Le <r0bertz@gentoo.org>
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Zhang Le 2009-03-16 14:44:31 +08:00 committed by Linus Torvalds
parent 5bee17f18b
commit ee6f779b9e
1 changed files with 7 additions and 9 deletions

View File

@ -3066,7 +3066,6 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
int retval = -ENOENT; int retval = -ENOENT;
ino_t ino; ino_t ino;
int tid; int tid;
unsigned long pos = filp->f_pos; /* avoiding "long long" filp->f_pos */
struct pid_namespace *ns; struct pid_namespace *ns;
task = get_proc_task(inode); task = get_proc_task(inode);
@ -3083,18 +3082,18 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
goto out_no_task; goto out_no_task;
retval = 0; retval = 0;
switch (pos) { switch (filp->f_pos) {
case 0: case 0:
ino = inode->i_ino; ino = inode->i_ino;
if (filldir(dirent, ".", 1, pos, ino, DT_DIR) < 0) if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) < 0)
goto out; goto out;
pos++; filp->f_pos++;
/* fall through */ /* fall through */
case 1: case 1:
ino = parent_ino(dentry); ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, pos, ino, DT_DIR) < 0) if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) < 0)
goto out; goto out;
pos++; filp->f_pos++;
/* fall through */ /* fall through */
} }
@ -3104,9 +3103,9 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
ns = filp->f_dentry->d_sb->s_fs_info; ns = filp->f_dentry->d_sb->s_fs_info;
tid = (int)filp->f_version; tid = (int)filp->f_version;
filp->f_version = 0; filp->f_version = 0;
for (task = first_tid(leader, tid, pos - 2, ns); for (task = first_tid(leader, tid, filp->f_pos - 2, ns);
task; task;
task = next_tid(task), pos++) { task = next_tid(task), filp->f_pos++) {
tid = task_pid_nr_ns(task, ns); tid = task_pid_nr_ns(task, ns);
if (proc_task_fill_cache(filp, dirent, filldir, task, tid) < 0) { if (proc_task_fill_cache(filp, dirent, filldir, task, tid) < 0) {
/* returning this tgid failed, save it as the first /* returning this tgid failed, save it as the first
@ -3117,7 +3116,6 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
} }
} }
out: out:
filp->f_pos = pos;
put_task_struct(leader); put_task_struct(leader);
out_no_task: out_no_task:
return retval; return retval;