#include <linux/poll.h>
 #include "internal.h"
 
+/* NOTE:
+ *     Implementing inode permission operations in /proc is almost
+ *     certainly an error.  Permission checks need to happen during
+ *     each system call not at open time.  The reason is that most of
+ *     what we wish to check for permissions in /proc varies at runtime.
+ *
+ *     The classic example of a problem is opening file descriptors
+ *     in /proc for a task before it execs a suid executable.
+ */
+
 /*
  * For hysterical raisins we keep the same inumbers as in the old procfs.
  * Feel free to change the macro below - just keep the range distinct from
 
 /* If the process being read is separated by chroot from the reading process,
  * don't let the reader access the threads.
- *
- * note: this does dput(root) and mntput(vfsmnt) on exit.
  */
-static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt)
+static int proc_check_chroot(struct dentry *de, struct vfsmount *mnt)
 {
-       struct dentry *de, *base;
-       struct vfsmount *our_vfsmnt, *mnt;
+       struct dentry *base;
+       struct vfsmount *our_vfsmnt;
        int res = 0;
 
        read_lock(¤t->fs->lock);
        read_unlock(¤t->fs->lock);
 
        spin_lock(&vfsmount_lock);
-       de = root;
-       mnt = vfsmnt;
 
        while (mnt != our_vfsmnt) {
                if (mnt == mnt->mnt_parent)
 exit:
        dput(base);
        mntput(our_vfsmnt);
-       dput(root);
-       mntput(vfsmnt);
        return res;
 out:
        spin_unlock(&vfsmount_lock);
        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)
-               return -EACCES;
-       return proc_check_root(inode);
-}
-
 extern struct seq_operations proc_pid_maps_op;
 static int maps_open(struct inode *inode, struct file *file)
 {
 };
 #endif /* CONFIG_SECCOMP */
 
+static int proc_check_dentry_visible(struct inode *inode,
+       struct dentry *dentry, struct vfsmount *mnt)
+{
+       /* Verify that the current process can already see the
+        * file pointed at by the file descriptor.
+        * This prevents /proc from being an accidental information leak.
+        *
+        * This prevents access to files that are not visible do to
+        * being on the otherside of a chroot, in a different
+        * namespace, or are simply process local (like pipes).
+        */
+       struct task_struct *task;
+       struct files_struct *task_files, *files;
+       int error = -EACCES;
+
+       /* See if the the two tasks share a commone set of
+        * file descriptors.  If so everything is visible.
+        */
+       task = proc_task(inode);
+       if (!task)
+               goto out;
+       files = get_files_struct(current);
+       task_files = get_files_struct(task);
+       if (files && task_files && (files == task_files))
+               error = 0;
+       if (task_files)
+               put_files_struct(task_files);
+       if (files)
+               put_files_struct(files);
+       if (!error)
+               goto out;
+
+       /* If the two tasks don't share a common set of file
+        * descriptors see if the destination dentry is already
+        * visible in the current tasks filesystem namespace.
+        */
+       error = proc_check_chroot(dentry, mnt);
+out:
+       return error;
+
+}
+
 static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        struct inode *inode = dentry->d_inode;
 
        if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
                goto out;
-       error = proc_check_root(inode);
-       if (error)
-               goto out;
 
        error = PROC_I(inode)->op.proc_get_link(inode, &nd->dentry, &nd->mnt);
        nd->last_type = LAST_BIND;
+       if (error)
+               goto out;
+
+       /* Only return files this task can already see */
+       error = proc_check_dentry_visible(inode, nd->dentry, nd->mnt);
+       if (error)
+               path_release(nd);
 out:
        return ERR_PTR(error);
 }
 
        if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
                goto out;
-       error = proc_check_root(inode);
-       if (error)
-               goto out;
 
        error = PROC_I(inode)->op.proc_get_link(inode, &de, &mnt);
        if (error)
                goto out;
 
+       /* Only return files this task can already see */
+       error = proc_check_dentry_visible(inode, de, mnt);
+       if (error)
+               goto out_put;
+
        error = do_proc_readlink(de, mnt, buffer, buflen);
+out_put:
        dput(de);
        mntput(mnt);
 out:
  */
 static struct inode_operations proc_fd_inode_operations = {
        .lookup         = proc_lookupfd,
-       .permission     = proc_permission,
 };
 
 static struct inode_operations proc_task_inode_operations = {