[PATCH RFC 2/4] proc: add helpers to set/get hidepid and gid mount options

From: Djalal Harouni
Date: Thu Mar 30 2017 - 11:24:47 EST


This is a preparation patch to allow to set and get hidepid and gid
mount options correctly

Signed-off-by: Djalal Harouni <tixxdz@xxxxxxxxx>
---
fs/proc/base.c | 15 +++++++++------
fs/proc/generic.c | 37 +++++++++++++++++++++++++++++++++++++
fs/proc/inode.c | 5 +++--
fs/proc/internal.h | 2 +-
fs/proc/root.c | 13 ++++++++-----
include/linux/proc_fs.h | 35 ++++++++++++++++++++++++++++++++---
6 files changed, 90 insertions(+), 17 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index cd16979..fd16566 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -681,13 +681,16 @@ 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_fs_info *fs_info,
struct task_struct *task,
int hide_pid_min)
{
- if (pid->hide_pid < hide_pid_min)
+ int hide_pid = proc_fs_get_hide_pid(fs_info);
+ kgid_t gid = proc_fs_get_pid_gid(fs_info);
+
+ if (hide_pid < hide_pid_min)
return true;
- if (in_group_p(pid->pid_gid))
+ if (in_group_p(gid))
return true;
return ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
}
@@ -703,7 +706,7 @@ static int proc_pid_permission(struct inode *inode, int mask)
task = get_proc_task(inode);
if (!task)
return -ESRCH;
- has_perms = has_pid_permissions(pid, task, HIDEPID_NO_ACCESS);
+ has_perms = has_pid_permissions(fs_info, task, HIDEPID_NO_ACCESS);
put_task_struct(task);

if (!has_perms) {
@@ -1745,7 +1748,7 @@ int pid_getattr(const struct path *path, struct kstat *stat,
stat->gid = GLOBAL_ROOT_GID;
task = pid_task(proc_pid(inode), PIDTYPE_PID);
if (task) {
- if (!has_pid_permissions(pid, task, HIDEPID_INVISIBLE)) {
+ if (!has_pid_permissions(fs_info, task, HIDEPID_INVISIBLE)) {
rcu_read_unlock();
/*
* This doesn't prevent learning whether PID exists,
@@ -3179,7 +3182,7 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx)
int len;

cond_resched();
- if (!has_pid_permissions(ns, iter.task, HIDEPID_INVISIBLE))
+ if (!has_pid_permissions(fs_info, iter.task, HIDEPID_INVISIBLE))
continue;

len = snprintf(name, sizeof(name), "%d", iter.tgid);
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 49c8cb9..7e5e419 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/printk.h>
+#include <linux/pid_namespace.h>
#include <linux/mount.h>
#include <linux/init.h>
#include <linux/idr.h>
@@ -33,6 +34,42 @@ struct proc_fs_info *proc_sb(struct super_block *sb)
return sb->s_fs_info;
}

+void proc_fs_set_hide_pid(struct proc_fs_info *fs_info, int hide_pid)
+{
+ /* For backward compatibility */
+ if (fs_info->version == PROC_FS_V1)
+ fs_info->pid_ns->hide_pid = hide_pid;
+ else if (fs_info->version == PROC_FS_V2)
+ fs_info->hide_pid = hide_pid;
+}
+
+void proc_fs_set_pid_gid(struct proc_fs_info *fs_info, kgid_t gid)
+{
+ /* For backward compatibility */
+ if (fs_info->version == PROC_FS_V1)
+ fs_info->pid_ns->pid_gid = gid;
+ else if (fs_info->version == PROC_FS_V2)
+ fs_info->pid_gid = gid;
+}
+
+int proc_fs_get_hide_pid(struct proc_fs_info *fs_info)
+{
+ /* For backward compatibility */
+ if (fs_info->version == PROC_FS_V1)
+ return fs_info->pid_ns->hide_pid;
+
+ return fs_info->hide_pid;
+}
+
+kgid_t proc_fs_get_pid_gid(struct proc_fs_info *fs_info)
+{
+ /* For backward compatibility */
+ if (fs_info->version == PROC_FS_V1)
+ return fs_info->pid_ns->pid_gid;
+
+ return fs_info->pid_gid;
+}
+
static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de)
{
if (len < de->namelen)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index e708288..ca47a0a 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -475,11 +475,12 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
int proc_fill_super(struct super_block *s, void *data, int silent)
{
struct proc_fs_info *fs_info = proc_sb(s);
- struct pid_namespace *ns = get_pid_ns(fs_info->pid_ns);
struct inode *root_inode;
int ret;

- if (!proc_parse_options(data, ns))
+ get_pid_ns(fs_info->pid_ns);
+
+ if (!proc_parse_options(data, fs_info))
return -EINVAL;

/* User space would break if executables or devices appear on proc */
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index c5ae09b..126fa31 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -261,7 +261,7 @@ static inline void proc_tty_init(void) {}
* root.c
*/
extern struct proc_dir_entry proc_root;
-extern int proc_parse_options(char *options, struct pid_namespace *pid);
+extern int proc_parse_options(char *options, struct proc_fs_info *fs_info);

extern void proc_self_init(void);
extern int proc_remount(struct super_block *, int *, char *);
diff --git a/fs/proc/root.c b/fs/proc/root.c
index a683e93..6a96c02 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -36,11 +36,12 @@ static const match_table_t tokens = {
{Opt_err, NULL},
};

-int proc_parse_options(char *options, struct pid_namespace *pid)
+int proc_parse_options(char *options, struct proc_fs_info *fs_info)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
+ kgid_t gid;

if (!options)
return 1;
@@ -56,7 +57,8 @@ 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);
+ gid = make_kgid(current_user_ns(), option);
+ proc_fs_set_pid_gid(fs_info, gid);
break;
case Opt_hidepid:
if (match_int(&args[0], &option))
@@ -66,7 +68,7 @@ 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;
+ proc_fs_set_hide_pid(fs_info, option);
break;
default:
pr_err("proc: unrecognized mount option \"%s\" "
@@ -81,10 +83,9 @@ int proc_parse_options(char *options, struct pid_namespace *pid)
int proc_remount(struct super_block *sb, int *flags, char *data)
{
struct proc_fs_info *fs_info = proc_sb(sb);
- struct pid_namespace *pid = fs_info->pid_ns;

sync_filesystem(sb);
- return !proc_parse_options(data, pid);
+ return !proc_parse_options(data, fs_info);
}

static int proc_test_super(struct super_block *s, void *data)
@@ -130,6 +131,8 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,

fs_info->pid_ns = ns;
fs_info->version = PROC_FS_V1;
+ fs_info->hide_pid = HIDEPID_OFF;
+ fs_info->pid_gid = GLOBAL_ROOT_GID;
refcount_set(&fs_info->users, 1);

sb = sget_userns(fs_type, proc_test_super, proc_set_super, flags,
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index e1cb9c3..c23299d 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -9,8 +9,8 @@
#include <linux/refcount.h>

enum {
- PROC_FS_V1 = 1,
- PROC_FS_V2 = 2,
+ PROC_FS_V1 = 1, /* Inside same pidns procfs mounts are shared */
+ PROC_FS_V2 = 2, /* New procfs mounts are separated by default */
};

struct proc_fs_info {
@@ -27,6 +27,13 @@ struct proc_dir_entry;

extern struct proc_fs_info *proc_sb(struct super_block *sb);

+extern void proc_fs_set_hide_pid(struct proc_fs_info *fs_info, int hide_pid);
+
+extern void proc_fs_set_pid_gid(struct proc_fs_info *fs_info, kgid_t gid);
+
+extern int proc_fs_get_hide_pid(struct proc_fs_info *fs_info);
+extern kgid_t proc_fs_get_pid_gid(struct proc_fs_info *fs_info);
+
extern void proc_root_init(void);
extern void proc_flush_task(struct task_struct *);

@@ -38,7 +45,7 @@ extern struct proc_dir_entry *proc_mkdir_data(const char *, umode_t,
extern struct proc_dir_entry *proc_mkdir_mode(const char *, umode_t,
struct proc_dir_entry *);
struct proc_dir_entry *proc_create_mount_point(const char *name);
-
+
extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
struct proc_dir_entry *,
const struct file_operations *,
@@ -69,6 +76,28 @@ static inline void proc_flush_task(struct task_struct *task)
{
}

+static inline void proc_fs_set_hide_pid(struct proc_fs_info *fs_info, int hide_pid)
+{
+}
+
+static inline void proc_fs_set_hide_pid(struct proc_fs_info *fs_info, int hide_pid)
+{
+}
+
+static inline void proc_fs_set_pid_gid(struct proc_info_fs *fs_info, kgid_t gid)
+{
+}
+
+static inline int proc_fs_get_hide_pid(struct proc_fs_info *fs_info)
+{
+ return 0;
+}
+
+extern kgid_t proc_fs_get_pid_gid(struct proc_fs_info *fs_info)
+{
+ return GLOBAL_ROOT_GID;
+}
+
extern inline struct proc_fs_info *proc_sb(struct super_block *sb) { return NULL;}
static inline struct proc_dir_entry *proc_symlink(const char *name,
struct proc_dir_entry *parent,const char *dest) { return NULL;}
--
2.10.2