[PATCH] Fix invisible threads problem
Sripathi Kodi [Mon, 19 Sep 2005 23:26:12 +0000 (18:26 -0500)]
When the main thread of a thread group has done pthread_exit() and died,
the other threads are still happily running, but will not be visible
under /proc because their leader is no longer accessible.

This fixes the access control so that we can see the sub-threads again.

Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Acked-by: Al Viro <viro@ftp.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

fs/proc/base.c

index 23db452..fb34f88 100644 (file)
@@ -340,6 +340,52 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf
        return result;
 }
 
+
+/* Same as proc_root_link, but this addionally tries to get fs from other
+ * threads in the group */
+static int proc_task_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+{
+       struct fs_struct *fs;
+       int result = -ENOENT;
+       struct task_struct *leader = proc_task(inode);
+
+       task_lock(leader);
+       fs = leader->fs;
+       if (fs) {
+               atomic_inc(&fs->count);
+               task_unlock(leader);
+       } else {
+               /* Try to get fs from other threads */
+               task_unlock(leader);
+               struct task_struct *task = leader;
+               read_lock(&tasklist_lock);
+               if (pid_alive(task)) {
+                       while ((task = next_thread(task)) != leader) {
+                               task_lock(task);
+                               fs = task->fs;
+                               if (fs) {
+                                       atomic_inc(&fs->count);
+                                       task_unlock(task);
+                                       break;
+                               }
+                               task_unlock(task);
+                       }
+               }
+               read_unlock(&tasklist_lock);
+       }
+
+       if (fs) {
+               read_lock(&fs->lock);
+               *mnt = mntget(fs->rootmnt);
+               *dentry = dget(fs->root);
+               read_unlock(&fs->lock);
+               result = 0;
+               put_fs_struct(fs);
+       }
+       return result;
+}
+
+
 #define MAY_PTRACE(task) \
        (task == current || \
        (task->parent == current && \
@@ -471,14 +517,14 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
 
 /* permission checks */
 
-static int proc_check_root(struct inode *inode)
+/* If the process being read is separated by chroot from the reading process,
+ * don't let the reader access the threads.
+ */
+static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt)
 {
-       struct dentry *de, *base, *root;
-       struct vfsmount *our_vfsmnt, *vfsmnt, *mnt;
+       struct dentry *de, *base;
+       struct vfsmount *our_vfsmnt, *mnt;
        int res = 0;
-
-       if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */
-               return -ENOENT;
        read_lock(&current->fs->lock);
        our_vfsmnt = mntget(current->fs->rootmnt);
        base = dget(current->fs->root);
@@ -511,6 +557,16 @@ out:
        goto exit;
 }
 
+static int proc_check_root(struct inode *inode)
+{
+       struct dentry *root;
+       struct vfsmount *vfsmnt;
+
+       if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */
+               return -ENOENT;
+       return proc_check_chroot(root, vfsmnt);
+}
+
 static int proc_permission(struct inode *inode, int mask, struct nameidata *nd)
 {
        if (generic_permission(inode, mask, NULL) != 0)
@@ -518,6 +574,20 @@ static int proc_permission(struct inode *inode, int mask, struct nameidata *nd)
        return proc_check_root(inode);
 }
 
+static int proc_task_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       struct dentry *root;
+       struct vfsmount *vfsmnt;
+
+       if (generic_permission(inode, mask, NULL) != 0)
+               return -EACCES;
+
+       if (proc_task_root_link(inode, &root, &vfsmnt))
+               return -ENOENT;
+
+       return proc_check_chroot(root, vfsmnt);
+}
+
 extern struct seq_operations proc_pid_maps_op;
 static int maps_open(struct inode *inode, struct file *file)
 {
@@ -1419,7 +1489,7 @@ static struct inode_operations proc_fd_inode_operations = {
 
 static struct inode_operations proc_task_inode_operations = {
        .lookup         = proc_task_lookup,
-       .permission     = proc_permission,
+       .permission     = proc_task_permission,
 };
 
 #ifdef CONFIG_SECURITY