|
@@ -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(¤t->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
|