Commit 9b6722ed authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] /proc thread visibility fixes

From: Kingsley Cheung <kingsley@aurema.com>

Is is possible to examine the data of tasks currently existing in the system
which are not threads of the same thread group.

For example, the only task in the group where init is group leader is itself:

gen2 02:50:44 ~: ls /proc/1/task
1

However, I can then read the contents of 'stat' for any other task in the
system:

gen2 02:49:45 ~: cat /proc/1/task/$$/stat
1669 (bash) S 1668 1669 1669 34816 1730 256 1480 6479 12 4 8 5 5 17 15 0 1 0
+8065 3252224 451 4294967295 134512640 134955932 3221225104 3221222840
+4294960144 0 65536 3686404 1266761467 3222442959 0 0 17 0 0 0

I had a look at fs/proc/base.c and found that the 'lookup' functions for
these directories were checking that the task in question existed, but
overlooked the following:

1.  In the function proc_pid_lookup, a check is required to ensure that
    the task in question is a thread group leader.  Without the check, any
    task can have its data retrieved accordingly.  Consider the following.
    There is a multithreaded process 1777.

gen2 23:22:47 /proc/1777: ls task
1777  1778  1779  1780  1781  1782  1783  1784  1785  1786  1787  1788

However, I can read the stat file for its thread 1778 as follows:

gen2 23:22:50 /proc/1777: cat /proc/1778/stat
1778 (multithreadtest) T 1777 1777 1672 34816 1672 64 0 0 0 0 14 17 0 0 15 0 12 0 8871 24727552 104 4294967295 134512640 134515104 3221222496 1077365276 4294960144 0 0 0 0 3222479248 0 0 -1 1 0 0

But 1778 is not meant to show up in /proc/, as intended right?:

gen2 23:22:56 /proc/1777: ls /proc/
1     1365  1661  1793  881        dma          kcore       scsi
10    1371  1662  18    9          driver       kmsg        self
1014  1372  1663  2     909        execdomains  loadavg     slabinfo
1032  14    1664  3     963        fb           locks       stat
1062  15    1665  4     966        filesystems  mdstat      swaps
1066  16    1666  5     buddyinfo  fs           meminfo     sys
1067  1605  1669  6     bus        ide          misc        sysrq-trigger
1087  1610  1670  7     cmdline    interrupts   modules     sysvipc
1095  1611  1671  736   cpuinfo    iomem        mounts      tty
11    1641  1672  8     crypto     ioports      mtrr        uptime
12    1658  17    807   devices    irq          net         version
13    1660  1777  810   diskstats  kallsyms     partitions  vmstat

2.  The other part of the bug is in the function proc_task_lookup.  Here
    there needs to be a check that the task X is indeed a thread of the
    thread group Y when we read /proc/<Y>/task/<X>.

Right now, this check does not exist, which allows for any existing
task to have its data read from another thread group directory.  The
following reads the stat directory of my bash shell from the thread
group 1.

gen2 23:28:07 ~: cd /proc/1
gen2 23:28:10 /proc/1: ls
auxv     cwd      exe  maps  mounts  stat   status  wchan
cmdline  environ  fd   mem   root    statm  task
gen2 23:28:11 /proc/1: ls task
1
gen2 23:28:27 /proc/1: cat task/$$/stat
1671 (bash) S 1670 1671 1671 34817 1802 256 1953 8101 12 4 10 6 9 26 15 0 1 0 5789 3252224 454 4294967295 134512640 134955932 3221225104 3221222840 4294960144 0 65536 3686404 1266761467 3222442959 0 0 17 0 0 0
parent 8bbb25c3
...@@ -1582,14 +1582,13 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct ...@@ -1582,14 +1582,13 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
if (!task) if (!task)
goto out; goto out;
if (!thread_group_leader(task))
goto out_drop_task;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_TGID_INO); inode = proc_pid_make_inode(dir->i_sb, task, PROC_TGID_INO);
if (!inode)
if (!inode) { goto out_drop_task;
put_task_struct(task);
goto out;
}
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tgid_base_inode_operations; inode->i_op = &proc_tgid_base_inode_operations;
inode->i_fop = &proc_tgid_base_operations; inode->i_fop = &proc_tgid_base_operations;
...@@ -1614,6 +1613,8 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct ...@@ -1614,6 +1613,8 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
goto out; goto out;
} }
return NULL; return NULL;
out_drop_task:
put_task_struct(task);
out: out:
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
...@@ -1622,6 +1623,7 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct ...@@ -1622,6 +1623,7 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{ {
struct task_struct *task; struct task_struct *task;
struct task_struct *leader = proc_task(dir);
struct inode *inode; struct inode *inode;
unsigned tid; unsigned tid;
...@@ -1636,14 +1638,14 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry ...@@ -1636,14 +1638,14 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
if (!task) if (!task)
goto out; goto out;
if (leader->tgid != task->tgid)
goto out_drop_task;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_INO); inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_INO);
if (!inode) { if (!inode)
put_task_struct(task); goto out_drop_task;
goto out;
}
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tid_base_inode_operations; inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations; inode->i_fop = &proc_tid_base_operations;
...@@ -1656,6 +1658,8 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry ...@@ -1656,6 +1658,8 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
put_task_struct(task); put_task_struct(task);
return NULL; return NULL;
out_drop_task:
put_task_struct(task);
out: out:
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment