@@ -688,13 +688,22 @@ static bool has_pid_permissions(struct proc_fs_info *fs_info,
struct task_struct *task,
int hide_pid_min)
{
- int hide_pid = proc_fs_hide_pid(fs_info);
- kgid_t gid = proc_fs_pid_gid(fs_info);
+ int limit_pids = proc_fs_limit_pids(fs_info);
- if (hide_pid < hide_pid_min)
- return true;
- if (in_group_p(gid))
- return true;
+ /*
+ * If 'limit_pids' mount is set force a ptrace check,
+ * we indicate that we are using a filesystem syscall
+ * by passing PTRACE_MODE_READ_FSCREDS
+ */
+ if (limit_pids == PROC_LIMIT_PIDS_OFF) {
+ int hide_pid = proc_fs_hide_pid(fs_info);
+ kgid_t gid = proc_fs_pid_gid(fs_info);
+
+ if (hide_pid < hide_pid_min)
+ return true;
+ if (in_group_p(gid))
+ return true;
+ }
return ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
}
@@ -115,12 +115,16 @@ static int proc_show_options(struct seq_file *seq, struct dentry *root)
struct super_block *sb = root->d_sb;
struct proc_fs_info *fs_info = proc_sb(sb);
struct pid_namespace *pid = fs_info->pid_ns;
+ int limit_pids = proc_fs_limit_pids(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 != HIDEPID_OFF)
seq_printf(seq, ",hidepid=%u", pid->hide_pid);
+ if (limit_pids > PROC_LIMIT_PIDS_OFF)
+ seq_printf(seq, ",limit_pids=%u", limit_pids);
+
return 0;
}
@@ -28,15 +28,66 @@
#include "internal.h"
enum {
- Opt_gid, Opt_hidepid, Opt_err,
+ Opt_gid, Opt_hidepid, Opt_limit_pids, Opt_err,
};
static const match_table_t tokens = {
{Opt_hidepid, "hidepid=%u"},
{Opt_gid, "gid=%u"},
+ {Opt_limit_pids, "limit_pids=%u"},
{Opt_err, NULL},
};
+/* We only parse 'limit_pids' option here */
+int proc_parse_early_options(char *options, struct proc_fs_info *fs_info)
+{
+ char *p, *opts, *orig;
+ substring_t args[MAX_OPT_ARGS];
+ int option, ret;
+
+ if (!options)
+ return 0;
+
+ opts = kstrdup(options, GFP_KERNEL);
+ if (!opts)
+ return -ENOMEM;
+
+ orig = opts;
+
+ while ((p = strsep(&opts, ",")) != NULL) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case Opt_limit_pids:
+ if (match_int(&args[0], &option))
+ return -EINVAL;
+ ret = proc_fs_set_limit_pids(fs_info, option);
+ if (ret < 0) {
+ pr_err("proc: faild to parse mount option "
+ "\"%s\" \n", p);
+ return ret;
+ }
+ proc_fs_set_newinstance(fs_info, true);
+ pr_info("proc: mounting a new procfs instance ");
+ break;
+ case Opt_gid:
+ case Opt_hidepid:
+ break;
+ default:
+ pr_err("proc: unrecognized mount option \"%s\" "
+ "or missing value\n", p);
+ return -EINVAL;
+ }
+ }
+
+ kfree(orig);
+ return 0;
+}
+
int proc_parse_options(char *options, struct proc_fs_info *fs_info)
{
char *p;
@@ -75,6 +126,8 @@ int proc_parse_options(char *options, struct proc_fs_info *fs_info)
}
proc_fs_set_hide_pid(fs_info, option);
break;
+ case Opt_limit_pids:
+ break;
default:
pr_err("proc: unrecognized mount option \"%s\" "
"or missing value\n", p);
@@ -87,18 +140,34 @@ int proc_parse_options(char *options, struct proc_fs_info *fs_info)
int proc_remount(struct super_block *sb, int *flags, char *data)
{
+ int error;
struct proc_fs_info *fs_info = proc_sb(sb);
sync_filesystem(sb);
+
+ /*
+ * If this is a new instance, then parse again the proc mount
+ * options.
+ */
+ if (proc_fs_newinstance(fs_info)) {
+ error = proc_parse_early_options(data, fs_info);
+ if (error < 0)
+ return error;
+ }
+
return !proc_parse_options(data, fs_info);
}
-static int proc_test_super(struct super_block *s, void *data)
+static int proc_test_super(struct super_block *sb, void *data)
{
struct proc_fs_info *p = data;
- struct proc_fs_info *fs_info = proc_sb(s);
+ struct proc_fs_info *fs_info = proc_sb(sb);
- return p->pid_ns == fs_info->pid_ns;
+ if (!proc_fs_newinstance(p) && !proc_fs_newinstance(fs_info) &&
+ p->pid_ns == fs_info->pid_ns)
+ return 1;
+
+ return 0;
}
static int proc_set_super(struct super_block *sb, void *data)
@@ -110,7 +179,7 @@ static int proc_set_super(struct super_block *sb, void *data)
static struct dentry *proc_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- int error;
+ int error = 0;
struct super_block *sb;
struct pid_namespace *ns;
struct proc_fs_info *fs_info;
@@ -126,10 +195,19 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,
if (!fs_info)
return ERR_PTR(-ENOMEM);
+ /* Set it as early as possible */
+ proc_fs_set_newinstance(fs_info, false);
+ proc_fs_set_limit_pids(fs_info, PROC_LIMIT_PIDS_OFF);
+
if (flags & SB_KERNMOUNT) {
ns = data;
data = NULL;
} else {
+ /* Parse early mount options if not a MS_KERNMOUNT */
+ error = proc_parse_early_options(data, fs_info);
+ if (error < 0)
+ goto error_fs_info;
+
ns = task_active_pid_ns(current);
}
@@ -13,10 +13,17 @@
struct proc_dir_entry;
struct pid_namespace;
+enum { /* definitions for proc mount option limit_pids */
+ PROC_LIMIT_PIDS_OFF = 0, /* Limit pids is off */
+ PROC_LIMIT_PIDS_PTRACE = 1, /* Limit pids to only ptracable pids */
+};
+
struct proc_fs_info {
struct pid_namespace *pid_ns;
struct dentry *proc_self; /* For /proc/self */
struct dentry *proc_thread_self; /* For /proc/thread-self/ */
+ bool newinstance; /* Private flag for new separated instances */
+ int limit_pids:1;
};
#ifdef CONFIG_PROC_FS
@@ -36,6 +43,21 @@ static inline void proc_fs_set_pid_gid(struct proc_fs_info *fs_info, kgid_t gid)
fs_info->pid_ns->pid_gid = gid;
}
+static inline void proc_fs_set_newinstance(struct proc_fs_info *fs_info, bool value)
+{
+ fs_info->newinstance = value;
+}
+
+static inline int proc_fs_set_limit_pids(struct proc_fs_info *fs_info, int value)
+{
+ if (value < PROC_LIMIT_PIDS_OFF || value > PROC_LIMIT_PIDS_PTRACE)
+ return -EINVAL;
+
+ fs_info->limit_pids = value;
+
+ return 0;
+}
+
static inline int proc_fs_hide_pid(struct proc_fs_info *fs_info)
{
return fs_info->pid_ns->hide_pid;
@@ -46,6 +68,16 @@ static inline kgid_t proc_fs_pid_gid(struct proc_fs_info *fs_info)
return fs_info->pid_ns->pid_gid;
}
+static inline bool proc_fs_newinstance(struct proc_fs_info *fs_info)
+{
+ return fs_info->newinstance;
+}
+
+static inline int proc_fs_limit_pids(struct proc_fs_info *fs_info)
+{
+ return fs_info->limit_pids;
+}
+
extern void proc_root_init(void);
extern void proc_flush_task(struct task_struct *);
@@ -90,6 +122,15 @@ static inline void proc_fs_set_pid_gid(struct proc_info_fs *fs_info, kgid_t gid)
{
}
+static inline void proc_fs_set_newinstance(struct proc_fs_info *fs_info, bool value)
+{
+}
+
+static inline int proc_fs_set_limit_pids(struct proc_fs_info *fs_info, int value)
+{
+ return 0;
+}
+
static inline int proc_fs_hide_pid(struct proc_fs_info *fs_info)
{
return 0;
@@ -100,6 +141,16 @@ extern kgid_t proc_fs_pid_gid(struct proc_fs_info *fs_info)
return GLOBAL_ROOT_GID;
}
+static inline bool proc_fs_newinstance(struct proc_fs_info *fs_info)
+{
+ return false;
+}
+
+static inline int proc_fs_limit_pids(struct proc_fs_info *fs_info)
+{
+ return 0;
+}
+
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;}