From: Stephen Mell <sub.atomic.fus...@gmail.com> hide_pid and pid_gid are proc mount options whose values are stored in the pid_namespace struct. As a result, if one mounts proc again for the same PID namespace with different mount options, all mounts for that PID namespace will be affected. This seems undesirable. This patch creates a proc_sb_info struct that points to the applicable PID namespace, and it moves hide_pid, pid_gid, and proc_self from pid_namespace to proc_sb_info.
Signed-off-by: Stephen Mell <sub.atomic.fus...@gmail.com> --- diff --git a/fs/proc/base.c b/fs/proc/base.c index dd51e50..853eb68 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -584,13 +584,13 @@ int proc_setattr(struct dentry *dentry, struct iattr *attr) * May current process learn task's sched/cmdline info (for hide_pid_min=1) * or euid/egid (for hide_pid_min=2)? */ -static bool has_pid_permissions(struct pid_namespace *pid, +static bool has_pid_permissions(struct proc_sb_info *fsi, struct task_struct *task, int hide_pid_min) { - if (pid->hide_pid < hide_pid_min) + if (fsi->hide_pid < hide_pid_min) return true; - if (in_group_p(pid->pid_gid)) + if (in_group_p(fsi->pid_gid)) return true; return ptrace_may_access(task, PTRACE_MODE_READ); } @@ -598,18 +598,18 @@ static bool has_pid_permissions(struct pid_namespace *pid, static int proc_pid_permission(struct inode *inode, int mask) { - struct pid_namespace *pid = inode->i_sb->s_fs_info; + struct proc_sb_info *fsi = inode->i_sb->s_fs_info; struct task_struct *task; bool has_perms; task = get_proc_task(inode); if (!task) return -ESRCH; - has_perms = has_pid_permissions(pid, task, 1); + has_perms = has_pid_permissions(fsi, task, 1); put_task_struct(task); if (!has_perms) { - if (pid->hide_pid == 2) { + if (fsi->hide_pid == 2) { /* * Let's make getdents(), stat(), and open() * consistent with each other. If a process @@ -670,12 +670,14 @@ static const struct file_operations proc_info_file_operations = { static int proc_single_show(struct seq_file *m, void *v) { struct inode *inode = m->private; + struct proc_sb_info *fsi; struct pid_namespace *ns; struct pid *pid; struct task_struct *task; int ret; - ns = inode->i_sb->s_fs_info; + fsi = inode->i_sb->s_fs_info; + ns = fsi->ns; pid = proc_pid(inode); task = get_pid_task(pid, PIDTYPE_PID); if (!task) @@ -1574,7 +1576,7 @@ int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) struct inode *inode = dentry->d_inode; struct task_struct *task; const struct cred *cred; - struct pid_namespace *pid = dentry->d_sb->s_fs_info; + struct proc_sb_info *fsi = dentry->d_sb->s_fs_info; generic_fillattr(inode, stat); @@ -1583,7 +1585,7 @@ int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) stat->gid = GLOBAL_ROOT_GID; task = pid_task(proc_pid(inode), PIDTYPE_PID); if (task) { - if (!has_pid_permissions(pid, task, 2)) { + if (!has_pid_permissions(fsi, task, 2)) { rcu_read_unlock(); /* * This doesn't prevent learning whether PID exists, @@ -2869,13 +2871,15 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsign struct dentry *result = NULL; struct task_struct *task; unsigned tgid; + struct proc_sb_info *fsi; struct pid_namespace *ns; tgid = name_to_int(dentry); if (tgid == ~0U) goto out; - ns = dentry->d_sb->s_fs_info; + fsi = dentry->d_sb->s_fs_info; + ns = fsi->ns; rcu_read_lock(); task = find_task_by_pid_ns(tgid, ns); if (task) @@ -2954,6 +2958,7 @@ static int fake_filldir(void *buf, const char *name, int namelen, int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct tgid_iter iter; + struct proc_sb_info *fsi; struct pid_namespace *ns; filldir_t __filldir; loff_t pos = filp->f_pos; @@ -2970,11 +2975,12 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) iter.tgid = pos - TGID_OFFSET; } iter.task = NULL; - ns = filp->f_dentry->d_sb->s_fs_info; + fsi = filp->f_dentry->d_sb->s_fs_info; + ns = fsi->ns; for (iter = next_tgid(ns, iter); iter.task; iter.tgid += 1, iter = next_tgid(ns, iter)) { - if (has_pid_permissions(ns, iter.task, 2)) + if (has_pid_permissions(fsi, iter.task, 2)) __filldir = filldir; else __filldir = fake_filldir; @@ -3132,6 +3138,7 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry struct task_struct *task; struct task_struct *leader = get_proc_task(dir); unsigned tid; + struct proc_sb_info *fsi; struct pid_namespace *ns; if (!leader) @@ -3141,7 +3148,8 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry if (tid == ~0U) goto out; - ns = dentry->d_sb->s_fs_info; + fsi = dentry->d_sb->s_fs_info; + ns = fsi->ns; rcu_read_lock(); task = find_task_by_pid_ns(tid, ns); if (task) @@ -3249,6 +3257,7 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi int retval = -ENOENT; ino_t ino; int tid; + struct proc_sb_info *fsi; struct pid_namespace *ns; task = get_proc_task(inode); @@ -3283,7 +3292,8 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi /* f_version caches the tgid value that the last readdir call couldn't * return. lseek aka telldir automagically resets f_version to 0. */ - ns = filp->f_dentry->d_sb->s_fs_info; + fsi = filp->f_dentry->d_sb->s_fs_info; + ns = fsi->ns; tid = (int)filp->f_version; filp->f_version = 0; for (task = first_tid(leader, tid, filp->f_pos - 2, ns); diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 073aea6..148737d 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -110,12 +110,12 @@ void __init proc_init_inodecache(void) static int proc_show_options(struct seq_file *seq, struct dentry *root) { struct super_block *sb = root->d_sb; - struct pid_namespace *pid = sb->s_fs_info; + struct proc_sb_info *fsi = sb->s_fs_info; - if (!gid_eq(pid->pid_gid, GLOBAL_ROOT_GID)) - seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, pid->pid_gid)); - if (pid->hide_pid != 0) - seq_printf(seq, ",hidepid=%u", pid->hide_pid); + if (!gid_eq(fsi->pid_gid, GLOBAL_ROOT_GID)) + seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fsi->pid_gid)); + if (fsi->hide_pid != 0) + seq_printf(seq, ",hidepid=%u", fsi->hide_pid); return 0; } diff --git a/fs/proc/internal.h b/fs/proc/internal.h index d600fb0..ff970e4 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -29,6 +29,14 @@ struct mempolicy; * /proc file has a parent, but "subdir" is NULL for all * non-directory entries). */ + +struct proc_sb_info { + struct pid_namespace *ns; + struct dentry *proc_self; + kgid_t pid_gid; + int hide_pid; +}; + struct proc_dir_entry { unsigned int low_ino; umode_t mode; diff --git a/fs/proc/root.c b/fs/proc/root.c index 41a6ea9..526aec6 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -20,6 +20,7 @@ #include <linux/mount.h> #include <linux/pid_namespace.h> #include <linux/parser.h> +#include <linux/slab.h> #include "internal.h" @@ -31,10 +32,9 @@ static int proc_test_super(struct super_block *sb, void *data) static int proc_set_super(struct super_block *sb, void *data) { int err = set_anon_super(sb, NULL); - if (!err) { - struct pid_namespace *ns = (struct pid_namespace *)data; - sb->s_fs_info = get_pid_ns(ns); - } + if (!err) + sb->s_fs_info = (struct proc_sb_info *)data; + return err; } @@ -48,7 +48,7 @@ static const match_table_t tokens = { {Opt_err, NULL}, }; -static int proc_parse_options(char *options, struct pid_namespace *pid) +static int proc_parse_options(char *options, struct proc_sb_info *fsi) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -68,7 +68,7 @@ static int proc_parse_options(char *options, struct pid_namespace *pid) case Opt_gid: if (match_int(&args[0], &option)) return 0; - pid->pid_gid = make_kgid(current_user_ns(), option); + fsi->pid_gid = make_kgid(current_user_ns(), option); break; case Opt_hidepid: if (match_int(&args[0], &option)) @@ -77,7 +77,7 @@ static int proc_parse_options(char *options, struct pid_namespace *pid) pr_err("proc: hidepid value must be between 0 and 2.\n"); return 0; } - pid->hide_pid = option; + fsi->hide_pid = option; break; default: pr_err("proc: unrecognized mount option \"%s\" " @@ -91,36 +91,44 @@ static int proc_parse_options(char *options, struct pid_namespace *pid) int proc_remount(struct super_block *sb, int *flags, char *data) { - struct pid_namespace *pid = sb->s_fs_info; - return !proc_parse_options(data, pid); + struct proc_sb_info *fsi = sb->s_fs_info; + return !proc_parse_options(data, fsi); } static struct dentry *proc_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { int err; + struct proc_sb_info *fsi; struct super_block *sb; - struct pid_namespace *ns; char *options; + + fsi = kzalloc(sizeof(struct proc_sb_info), GFP_KERNEL); + if (!fsi) + return ERR_PTR(-ENOMEM); + + sb = sget(fs_type, proc_test_super, proc_set_super, flags, fsi); + if (IS_ERR(sb)) + return ERR_CAST(sb); if (flags & MS_KERNMOUNT) { - ns = (struct pid_namespace *)data; + fsi->ns = (struct pid_namespace *)data; + get_pid_ns(fsi->ns); options = NULL; } else { - ns = task_active_pid_ns(current); - options = data; - if (!current_user_ns()->may_mount_proc) return ERR_PTR(-EPERM); - } - - sb = sget(fs_type, proc_test_super, proc_set_super, flags, ns); - if (IS_ERR(sb)) - return ERR_CAST(sb); + + options = data; + if (!proc_parse_options(options, fsi)) { + deactivate_locked_super(sb); + return ERR_PTR(-EINVAL); + } - if (!proc_parse_options(options, ns)) { - deactivate_locked_super(sb); - return ERR_PTR(-EINVAL); + if (!fsi->ns) { + fsi->ns = task_active_pid_ns(current); + get_pid_ns(fsi->ns); + } } if (!sb->s_root) { @@ -138,13 +146,16 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, static void proc_kill_sb(struct super_block *sb) { + struct proc_sb_info *fsi; struct pid_namespace *ns; - ns = (struct pid_namespace *)sb->s_fs_info; - if (ns->proc_self) - dput(ns->proc_self); + fsi = (struct proc_sb_info *)sb->s_fs_info; + ns = (struct pid_namespace *)fsi->ns; + if (fsi->proc_self) + dput(fsi->proc_self); kill_anon_super(sb); put_pid_ns(ns); + kfree(fsi); } static struct file_system_type proc_fs_type = { diff --git a/fs/proc/self.c b/fs/proc/self.c index 6b6a993..ff4a2e2 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -10,7 +10,8 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - struct pid_namespace *ns = dentry->d_sb->s_fs_info; + struct proc_sb_info *fsi = dentry->d_sb->s_fs_info; + struct pid_namespace *ns = fsi->ns; pid_t tgid = task_tgid_nr_ns(current, ns); char tmp[PROC_NUMBUF]; if (!tgid) @@ -21,7 +22,8 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) { - struct pid_namespace *ns = dentry->d_sb->s_fs_info; + struct proc_sb_info *fsi = dentry->d_sb->s_fs_info; + struct pid_namespace *ns = fsi->ns; pid_t tgid = task_tgid_nr_ns(current, ns); char *name = ERR_PTR(-ENOENT); if (tgid) { @@ -55,7 +57,7 @@ static unsigned self_inum; int proc_setup_self(struct super_block *s) { struct inode *root_inode = s->s_root->d_inode; - struct pid_namespace *ns = s->s_fs_info; + struct proc_sb_info *fsi = s->s_fs_info; struct dentry *self; mutex_lock(&root_inode->i_mutex); @@ -82,7 +84,7 @@ int proc_setup_self(struct super_block *s) pr_err("proc_fill_super: can't allocate /proc/self\n"); return PTR_ERR(self); } - ns->proc_self = self; + fsi->proc_self = self; return 0; } diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index e277266..6185386 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -31,15 +31,12 @@ struct pid_namespace { struct pid_namespace *parent; #ifdef CONFIG_PROC_FS struct vfsmount *proc_mnt; - struct dentry *proc_self; #endif #ifdef CONFIG_BSD_PROCESS_ACCT struct bsd_acct_struct *bacct; #endif struct user_namespace *user_ns; struct work_struct proc_work; - kgid_t pid_gid; - int hide_pid; int reboot; /* group exit code if this pidns was rebooted */ unsigned int proc_inum; }; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/