Merge 9ff7258575
("Merge branch 'proc-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace") into android-mainline
Baby steps on the way to 5.8-rc1. Change-Id: Ie10408266ea00e8b989c6f31c824231da1da90c2 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
commit
44699162a3
@ -51,6 +51,8 @@ fixes/update part 1.1 Stefani Seibold <stefani@seibold.net> June 9 2009
|
||||
4 Configuring procfs
|
||||
4.1 Mount options
|
||||
|
||||
5 Filesystem behavior
|
||||
|
||||
Preface
|
||||
=======
|
||||
|
||||
@ -2149,28 +2151,80 @@ The following mount options are supported:
|
||||
========= ========================================================
|
||||
hidepid= Set /proc/<pid>/ access mode.
|
||||
gid= Set the group authorized to learn processes information.
|
||||
subset= Show only the specified subset of procfs.
|
||||
========= ========================================================
|
||||
|
||||
hidepid=0 means classic mode - everybody may access all /proc/<pid>/ directories
|
||||
(default).
|
||||
hidepid=off or hidepid=0 means classic mode - everybody may access all
|
||||
/proc/<pid>/ directories (default).
|
||||
|
||||
hidepid=1 means users may not access any /proc/<pid>/ directories but their
|
||||
own. Sensitive files like cmdline, sched*, status are now protected against
|
||||
other users. This makes it impossible to learn whether any user runs
|
||||
specific program (given the program doesn't reveal itself by its behaviour).
|
||||
As an additional bonus, as /proc/<pid>/cmdline is unaccessible for other users,
|
||||
poorly written programs passing sensitive information via program arguments are
|
||||
now protected against local eavesdroppers.
|
||||
hidepid=noaccess or hidepid=1 means users may not access any /proc/<pid>/
|
||||
directories but their own. Sensitive files like cmdline, sched*, status are now
|
||||
protected against other users. This makes it impossible to learn whether any
|
||||
user runs specific program (given the program doesn't reveal itself by its
|
||||
behaviour). As an additional bonus, as /proc/<pid>/cmdline is unaccessible for
|
||||
other users, poorly written programs passing sensitive information via program
|
||||
arguments are now protected against local eavesdroppers.
|
||||
|
||||
hidepid=2 means hidepid=1 plus all /proc/<pid>/ will be fully invisible to other
|
||||
users. It doesn't mean that it hides a fact whether a process with a specific
|
||||
pid value exists (it can be learned by other means, e.g. by "kill -0 $PID"),
|
||||
but it hides process' uid and gid, which may be learned by stat()'ing
|
||||
/proc/<pid>/ otherwise. It greatly complicates an intruder's task of gathering
|
||||
information about running processes, whether some daemon runs with elevated
|
||||
privileges, whether other user runs some sensitive program, whether other users
|
||||
run any program at all, etc.
|
||||
hidepid=invisible or hidepid=2 means hidepid=1 plus all /proc/<pid>/ will be
|
||||
fully invisible to other users. It doesn't mean that it hides a fact whether a
|
||||
process with a specific pid value exists (it can be learned by other means, e.g.
|
||||
by "kill -0 $PID"), but it hides process' uid and gid, which may be learned by
|
||||
stat()'ing /proc/<pid>/ otherwise. It greatly complicates an intruder's task of
|
||||
gathering information about running processes, whether some daemon runs with
|
||||
elevated privileges, whether other user runs some sensitive program, whether
|
||||
other users run any program at all, etc.
|
||||
|
||||
hidepid=ptraceable or hidepid=4 means that procfs should only contain
|
||||
/proc/<pid>/ directories that the caller can ptrace.
|
||||
|
||||
gid= defines a group authorized to learn processes information otherwise
|
||||
prohibited by hidepid=. If you use some daemon like identd which needs to learn
|
||||
information about processes information, just add identd to this group.
|
||||
|
||||
subset=pid hides all top level files and directories in the procfs that
|
||||
are not related to tasks.
|
||||
|
||||
5 Filesystem behavior
|
||||
----------------------------
|
||||
|
||||
Originally, before the advent of pid namepsace, procfs was a global file
|
||||
system. It means that there was only one procfs instance in the system.
|
||||
|
||||
When pid namespace was added, a separate procfs instance was mounted in
|
||||
each pid namespace. So, procfs mount options are global among all
|
||||
mountpoints within the same namespace.
|
||||
|
||||
::
|
||||
|
||||
# grep ^proc /proc/mounts
|
||||
proc /proc proc rw,relatime,hidepid=2 0 0
|
||||
|
||||
# strace -e mount mount -o hidepid=1 -t proc proc /tmp/proc
|
||||
mount("proc", "/tmp/proc", "proc", 0, "hidepid=1") = 0
|
||||
+++ exited with 0 +++
|
||||
|
||||
# grep ^proc /proc/mounts
|
||||
proc /proc proc rw,relatime,hidepid=2 0 0
|
||||
proc /tmp/proc proc rw,relatime,hidepid=2 0 0
|
||||
|
||||
and only after remounting procfs mount options will change at all
|
||||
mountpoints.
|
||||
|
||||
# mount -o remount,hidepid=1 -t proc proc /tmp/proc
|
||||
|
||||
# grep ^proc /proc/mounts
|
||||
proc /proc proc rw,relatime,hidepid=1 0 0
|
||||
proc /tmp/proc proc rw,relatime,hidepid=1 0 0
|
||||
|
||||
This behavior is different from the behavior of other filesystems.
|
||||
|
||||
The new procfs behavior is more like other filesystems. Each procfs mount
|
||||
creates a new procfs instance. Mount options affect own procfs instance.
|
||||
It means that it became possible to have several procfs instances
|
||||
displaying tasks with different filtering options in one pid namespace.
|
||||
|
||||
# mount -o hidepid=invisible -t proc proc /proc
|
||||
# mount -o hidepid=noaccess -t proc proc /tmp/proc
|
||||
# grep ^proc /proc/mounts
|
||||
proc /proc proc rw,relatime,hidepid=invisible 0 0
|
||||
proc /tmp/proc proc rw,relatime,hidepid=noaccess 0 0
|
||||
|
@ -1176,7 +1176,6 @@ static int de_thread(struct task_struct *tsk)
|
||||
tsk->start_boottime = leader->start_boottime;
|
||||
|
||||
BUG_ON(!same_thread_group(leader, tsk));
|
||||
BUG_ON(has_group_leader_pid(tsk));
|
||||
/*
|
||||
* An exec() starts a new thread group with the
|
||||
* TGID of the previous thread group. Rehash the
|
||||
@ -1186,11 +1185,8 @@ static int de_thread(struct task_struct *tsk)
|
||||
|
||||
/* Become a process group leader with the old leader's pid.
|
||||
* The old leader becomes a thread of the this thread group.
|
||||
* Note: The old leader also uses this pid until release_task
|
||||
* is called. Odd but simple and correct.
|
||||
*/
|
||||
tsk->pid = leader->pid;
|
||||
change_pid(tsk, PIDTYPE_PID, task_pid(leader));
|
||||
exchange_tids(tsk, leader);
|
||||
transfer_pid(leader, tsk, PIDTYPE_TGID);
|
||||
transfer_pid(leader, tsk, PIDTYPE_PGID);
|
||||
transfer_pid(leader, tsk, PIDTYPE_SID);
|
||||
|
@ -2823,7 +2823,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
unsigned int fl_pid;
|
||||
struct pid_namespace *proc_pidns = file_inode(f->file)->i_sb->s_fs_info;
|
||||
struct pid_namespace *proc_pidns = proc_pid_ns(file_inode(f->file)->i_sb);
|
||||
|
||||
fl_pid = locks_translate_pid(fl, proc_pidns);
|
||||
/*
|
||||
@ -2901,7 +2901,7 @@ static int locks_show(struct seq_file *f, void *v)
|
||||
{
|
||||
struct locks_iterator *iter = f->private;
|
||||
struct file_lock *fl, *bfl;
|
||||
struct pid_namespace *proc_pidns = file_inode(f->file)->i_sb->s_fs_info;
|
||||
struct pid_namespace *proc_pidns = proc_pid_ns(file_inode(f->file)->i_sb);
|
||||
|
||||
fl = hlist_entry(v, struct file_lock, fl_link);
|
||||
|
||||
|
@ -728,7 +728,7 @@ static int children_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct inode *inode = file_inode(seq->file);
|
||||
|
||||
seq_printf(seq, "%d ", pid_nr_ns(v, proc_pid_ns(inode)));
|
||||
seq_printf(seq, "%d ", pid_nr_ns(v, proc_pid_ns(inode->i_sb)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -698,13 +698,21 @@ 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)
|
||||
enum proc_hidepid hide_pid_min)
|
||||
{
|
||||
if (pid->hide_pid < hide_pid_min)
|
||||
/*
|
||||
* If 'hidpid' mount option is set force a ptrace check,
|
||||
* we indicate that we are using a filesystem syscall
|
||||
* by passing PTRACE_MODE_READ_FSCREDS
|
||||
*/
|
||||
if (fs_info->hide_pid == HIDEPID_NOT_PTRACEABLE)
|
||||
return ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
|
||||
|
||||
if (fs_info->hide_pid < hide_pid_min)
|
||||
return true;
|
||||
if (in_group_p(pid->pid_gid))
|
||||
if (in_group_p(fs_info->pid_gid))
|
||||
return true;
|
||||
return ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
|
||||
}
|
||||
@ -712,18 +720,18 @@ static bool has_pid_permissions(struct pid_namespace *pid,
|
||||
|
||||
static int proc_pid_permission(struct inode *inode, int mask)
|
||||
{
|
||||
struct pid_namespace *pid = proc_pid_ns(inode);
|
||||
struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
|
||||
struct task_struct *task;
|
||||
bool has_perms;
|
||||
|
||||
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) {
|
||||
if (pid->hide_pid == HIDEPID_INVISIBLE) {
|
||||
if (fs_info->hide_pid == HIDEPID_INVISIBLE) {
|
||||
/*
|
||||
* Let's make getdents(), stat(), and open()
|
||||
* consistent with each other. If a process
|
||||
@ -747,7 +755,7 @@ static const struct inode_operations proc_def_inode_operations = {
|
||||
static int proc_single_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct inode *inode = m->private;
|
||||
struct pid_namespace *ns = proc_pid_ns(inode);
|
||||
struct pid_namespace *ns = proc_pid_ns(inode->i_sb);
|
||||
struct pid *pid = proc_pid(inode);
|
||||
struct task_struct *task;
|
||||
int ret;
|
||||
@ -1416,7 +1424,7 @@ static const struct file_operations proc_fail_nth_operations = {
|
||||
static int sched_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct inode *inode = m->private;
|
||||
struct pid_namespace *ns = proc_pid_ns(inode);
|
||||
struct pid_namespace *ns = proc_pid_ns(inode->i_sb);
|
||||
struct task_struct *p;
|
||||
|
||||
p = get_proc_task(inode);
|
||||
@ -1910,7 +1918,7 @@ int pid_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags)
|
||||
{
|
||||
struct inode *inode = d_inode(path->dentry);
|
||||
struct pid_namespace *pid = proc_pid_ns(inode);
|
||||
struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
|
||||
struct task_struct *task;
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
@ -1920,7 +1928,7 @@ int pid_getattr(const struct path *path, struct kstat *stat,
|
||||
rcu_read_lock();
|
||||
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,
|
||||
@ -2471,7 +2479,7 @@ static int proc_timers_open(struct inode *inode, struct file *file)
|
||||
return -ENOMEM;
|
||||
|
||||
tp->pid = proc_pid(inode);
|
||||
tp->ns = proc_pid_ns(inode);
|
||||
tp->ns = proc_pid_ns(inode->i_sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3316,6 +3324,7 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct task_struct *task;
|
||||
unsigned tgid;
|
||||
struct proc_fs_info *fs_info;
|
||||
struct pid_namespace *ns;
|
||||
struct dentry *result = ERR_PTR(-ENOENT);
|
||||
|
||||
@ -3323,7 +3332,8 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags)
|
||||
if (tgid == ~0U)
|
||||
goto out;
|
||||
|
||||
ns = dentry->d_sb->s_fs_info;
|
||||
fs_info = proc_sb_info(dentry->d_sb);
|
||||
ns = fs_info->pid_ns;
|
||||
rcu_read_lock();
|
||||
task = find_task_by_pid_ns(tgid, ns);
|
||||
if (task)
|
||||
@ -3332,7 +3342,14 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags)
|
||||
if (!task)
|
||||
goto out;
|
||||
|
||||
/* Limit procfs to only ptraceable tasks */
|
||||
if (fs_info->hide_pid == HIDEPID_NOT_PTRACEABLE) {
|
||||
if (!has_pid_permissions(fs_info, task, HIDEPID_NO_ACCESS))
|
||||
goto out_put_task;
|
||||
}
|
||||
|
||||
result = proc_pid_instantiate(dentry, task, NULL);
|
||||
out_put_task:
|
||||
put_task_struct(task);
|
||||
out:
|
||||
return result;
|
||||
@ -3358,20 +3375,8 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite
|
||||
pid = find_ge_pid(iter.tgid, ns);
|
||||
if (pid) {
|
||||
iter.tgid = pid_nr_ns(pid, ns);
|
||||
iter.task = pid_task(pid, PIDTYPE_PID);
|
||||
/* What we to know is if the pid we have find is the
|
||||
* pid of a thread_group_leader. Testing for task
|
||||
* being a thread_group_leader is the obvious thing
|
||||
* todo but there is a window when it fails, due to
|
||||
* the pid transfer logic in de_thread.
|
||||
*
|
||||
* So we perform the straight forward test of seeing
|
||||
* if the pid we have found is the pid of a thread
|
||||
* group leader, and don't worry if the task we have
|
||||
* found doesn't happen to be a thread group leader.
|
||||
* As we don't care in the case of readdir.
|
||||
*/
|
||||
if (!iter.task || !has_group_leader_pid(iter.task)) {
|
||||
iter.task = pid_task(pid, PIDTYPE_TGID);
|
||||
if (!iter.task) {
|
||||
iter.tgid += 1;
|
||||
goto retry;
|
||||
}
|
||||
@ -3387,20 +3392,21 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite
|
||||
int proc_pid_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct tgid_iter iter;
|
||||
struct pid_namespace *ns = proc_pid_ns(file_inode(file));
|
||||
struct proc_fs_info *fs_info = proc_sb_info(file_inode(file)->i_sb);
|
||||
struct pid_namespace *ns = proc_pid_ns(file_inode(file)->i_sb);
|
||||
loff_t pos = ctx->pos;
|
||||
|
||||
if (pos >= PID_MAX_LIMIT + TGID_OFFSET)
|
||||
return 0;
|
||||
|
||||
if (pos == TGID_OFFSET - 2) {
|
||||
struct inode *inode = d_inode(ns->proc_self);
|
||||
struct inode *inode = d_inode(fs_info->proc_self);
|
||||
if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK))
|
||||
return 0;
|
||||
ctx->pos = pos = pos + 1;
|
||||
}
|
||||
if (pos == TGID_OFFSET - 1) {
|
||||
struct inode *inode = d_inode(ns->proc_thread_self);
|
||||
struct inode *inode = d_inode(fs_info->proc_thread_self);
|
||||
if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK))
|
||||
return 0;
|
||||
ctx->pos = pos = pos + 1;
|
||||
@ -3414,7 +3420,7 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx)
|
||||
unsigned 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), "%u", iter.tgid);
|
||||
@ -3617,6 +3623,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_fs_info *fs_info;
|
||||
struct pid_namespace *ns;
|
||||
struct dentry *result = ERR_PTR(-ENOENT);
|
||||
|
||||
@ -3627,7 +3634,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;
|
||||
fs_info = proc_sb_info(dentry->d_sb);
|
||||
ns = fs_info->pid_ns;
|
||||
rcu_read_lock();
|
||||
task = find_task_by_pid_ns(tid, ns);
|
||||
if (task)
|
||||
@ -3741,7 +3749,7 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx)
|
||||
/* f_version caches the tgid value that the last readdir call couldn't
|
||||
* return. lseek aka telldir automagically resets f_version to 0.
|
||||
*/
|
||||
ns = proc_pid_ns(inode);
|
||||
ns = proc_pid_ns(inode->i_sb);
|
||||
tid = (int)file->f_version;
|
||||
file->f_version = 0;
|
||||
for (task = first_tid(proc_pid(inode), tid, ctx->pos - 2, ns);
|
||||
|
@ -269,6 +269,11 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
|
||||
struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct proc_fs_info *fs_info = proc_sb_info(dir->i_sb);
|
||||
|
||||
if (fs_info->pidonly == PROC_PIDONLY_ON)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return proc_lookup_de(dir, dentry, PDE(dir));
|
||||
}
|
||||
|
||||
@ -325,6 +330,10 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,
|
||||
int proc_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
|
||||
|
||||
if (fs_info->pidonly == PROC_PIDONLY_ON)
|
||||
return 1;
|
||||
|
||||
return proc_readdir_de(file, ctx, PDE(inode));
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@ -165,15 +166,28 @@ void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock
|
||||
deactivate_super(old_sb);
|
||||
}
|
||||
|
||||
static inline const char *hidepid2str(enum proc_hidepid v)
|
||||
{
|
||||
switch (v) {
|
||||
case HIDEPID_OFF: return "off";
|
||||
case HIDEPID_NO_ACCESS: return "noaccess";
|
||||
case HIDEPID_INVISIBLE: return "invisible";
|
||||
case HIDEPID_NOT_PTRACEABLE: return "ptraceable";
|
||||
}
|
||||
WARN_ONCE(1, "bad hide_pid value: %d\n", v);
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
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_fs_info *fs_info = proc_sb_info(root->d_sb);
|
||||
|
||||
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 (!gid_eq(fs_info->pid_gid, GLOBAL_ROOT_GID))
|
||||
seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fs_info->pid_gid));
|
||||
if (fs_info->hide_pid != HIDEPID_OFF)
|
||||
seq_printf(seq, ",hidepid=%s", hidepid2str(fs_info->hide_pid));
|
||||
if (fs_info->pidonly != PROC_PIDONLY_OFF)
|
||||
seq_printf(seq, ",subset=pid");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -464,6 +478,7 @@ proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
|
||||
|
||||
static int proc_reg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
|
||||
struct proc_dir_entry *pde = PDE(inode);
|
||||
int rv = 0;
|
||||
typeof_member(struct proc_ops, proc_open) open;
|
||||
@ -477,6 +492,9 @@ static int proc_reg_open(struct inode *inode, struct file *file)
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (fs_info->pidonly == PROC_PIDONLY_ON)
|
||||
return -ENOENT;
|
||||
|
||||
/*
|
||||
* Ensure that
|
||||
* 1) PDE's ->release hook will be called no matter what
|
||||
|
133
fs/proc/root.c
133
fs/proc/root.c
@ -32,21 +32,86 @@
|
||||
struct proc_fs_context {
|
||||
struct pid_namespace *pid_ns;
|
||||
unsigned int mask;
|
||||
int hidepid;
|
||||
enum proc_hidepid hidepid;
|
||||
int gid;
|
||||
enum proc_pidonly pidonly;
|
||||
};
|
||||
|
||||
enum proc_param {
|
||||
Opt_gid,
|
||||
Opt_hidepid,
|
||||
Opt_subset,
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec proc_fs_parameters[] = {
|
||||
fsparam_u32("gid", Opt_gid),
|
||||
fsparam_u32("hidepid", Opt_hidepid),
|
||||
fsparam_string("hidepid", Opt_hidepid),
|
||||
fsparam_string("subset", Opt_subset),
|
||||
{}
|
||||
};
|
||||
|
||||
static inline int valid_hidepid(unsigned int value)
|
||||
{
|
||||
return (value == HIDEPID_OFF ||
|
||||
value == HIDEPID_NO_ACCESS ||
|
||||
value == HIDEPID_INVISIBLE ||
|
||||
value == HIDEPID_NOT_PTRACEABLE);
|
||||
}
|
||||
|
||||
static int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid);
|
||||
struct fs_parse_result result;
|
||||
int base = (unsigned long)hidepid_u32_spec.data;
|
||||
|
||||
if (param->type != fs_value_is_string)
|
||||
return invalf(fc, "proc: unexpected type of hidepid value\n");
|
||||
|
||||
if (!kstrtouint(param->string, base, &result.uint_32)) {
|
||||
if (!valid_hidepid(result.uint_32))
|
||||
return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
|
||||
ctx->hidepid = result.uint_32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(param->string, "off"))
|
||||
ctx->hidepid = HIDEPID_OFF;
|
||||
else if (!strcmp(param->string, "noaccess"))
|
||||
ctx->hidepid = HIDEPID_NO_ACCESS;
|
||||
else if (!strcmp(param->string, "invisible"))
|
||||
ctx->hidepid = HIDEPID_INVISIBLE;
|
||||
else if (!strcmp(param->string, "ptraceable"))
|
||||
ctx->hidepid = HIDEPID_NOT_PTRACEABLE;
|
||||
else
|
||||
return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_parse_subset_param(struct fs_context *fc, char *value)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
|
||||
while (value) {
|
||||
char *ptr = strchr(value, ',');
|
||||
|
||||
if (ptr != NULL)
|
||||
*ptr++ = '\0';
|
||||
|
||||
if (*value != '\0') {
|
||||
if (!strcmp(value, "pid")) {
|
||||
ctx->pidonly = PROC_PIDONLY_ON;
|
||||
} else {
|
||||
return invalf(fc, "proc: unsupported subset option - %s\n", value);
|
||||
}
|
||||
}
|
||||
value = ptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
@ -63,10 +128,13 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
break;
|
||||
|
||||
case Opt_hidepid:
|
||||
ctx->hidepid = result.uint_32;
|
||||
if (ctx->hidepid < HIDEPID_OFF ||
|
||||
ctx->hidepid > HIDEPID_INVISIBLE)
|
||||
return invalfc(fc, "hidepid value must be between 0 and 2.\n");
|
||||
if (proc_parse_hidepid_param(fc, param))
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case Opt_subset:
|
||||
if (proc_parse_subset_param(fc, param->string) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -77,26 +145,33 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void proc_apply_options(struct super_block *s,
|
||||
static void proc_apply_options(struct proc_fs_info *fs_info,
|
||||
struct fs_context *fc,
|
||||
struct pid_namespace *pid_ns,
|
||||
struct user_namespace *user_ns)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
|
||||
if (ctx->mask & (1 << Opt_gid))
|
||||
pid_ns->pid_gid = make_kgid(user_ns, ctx->gid);
|
||||
fs_info->pid_gid = make_kgid(user_ns, ctx->gid);
|
||||
if (ctx->mask & (1 << Opt_hidepid))
|
||||
pid_ns->hide_pid = ctx->hidepid;
|
||||
fs_info->hide_pid = ctx->hidepid;
|
||||
if (ctx->mask & (1 << Opt_subset))
|
||||
fs_info->pidonly = ctx->pidonly;
|
||||
}
|
||||
|
||||
static int proc_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
struct pid_namespace *pid_ns = get_pid_ns(s->s_fs_info);
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
struct inode *root_inode;
|
||||
struct proc_fs_info *fs_info;
|
||||
int ret;
|
||||
|
||||
proc_apply_options(s, fc, pid_ns, current_user_ns());
|
||||
fs_info = kzalloc(sizeof(*fs_info), GFP_KERNEL);
|
||||
if (!fs_info)
|
||||
return -ENOMEM;
|
||||
|
||||
fs_info->pid_ns = get_pid_ns(ctx->pid_ns);
|
||||
proc_apply_options(fs_info, fc, current_user_ns());
|
||||
|
||||
/* User space would break if executables or devices appear on proc */
|
||||
s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV;
|
||||
@ -106,6 +181,7 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
s->s_magic = PROC_SUPER_MAGIC;
|
||||
s->s_op = &proc_sops;
|
||||
s->s_time_gran = 1;
|
||||
s->s_fs_info = fs_info;
|
||||
|
||||
/*
|
||||
* procfs isn't actually a stacking filesystem; however, there is
|
||||
@ -113,7 +189,7 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
* top of it
|
||||
*/
|
||||
s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
|
||||
|
||||
|
||||
/* procfs dentries and inodes don't require IO to create */
|
||||
s->s_shrink.seeks = 0;
|
||||
|
||||
@ -140,19 +216,17 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
static int proc_reconfigure(struct fs_context *fc)
|
||||
{
|
||||
struct super_block *sb = fc->root->d_sb;
|
||||
struct pid_namespace *pid = sb->s_fs_info;
|
||||
struct proc_fs_info *fs_info = proc_sb_info(sb);
|
||||
|
||||
sync_filesystem(sb);
|
||||
|
||||
proc_apply_options(sb, fc, pid, current_user_ns());
|
||||
proc_apply_options(fs_info, fc, current_user_ns());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_get_tree(struct fs_context *fc)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
|
||||
return get_tree_keyed(fc, proc_fill_super, ctx->pid_ns);
|
||||
return get_tree_nodev(fc, proc_fill_super);
|
||||
}
|
||||
|
||||
static void proc_fs_context_free(struct fs_context *fc)
|
||||
@ -188,22 +262,17 @@ static int proc_init_fs_context(struct fs_context *fc)
|
||||
|
||||
static void proc_kill_sb(struct super_block *sb)
|
||||
{
|
||||
struct pid_namespace *ns;
|
||||
struct proc_fs_info *fs_info = proc_sb_info(sb);
|
||||
|
||||
if (fs_info->proc_self)
|
||||
dput(fs_info->proc_self);
|
||||
|
||||
if (fs_info->proc_thread_self)
|
||||
dput(fs_info->proc_thread_self);
|
||||
|
||||
ns = (struct pid_namespace *)sb->s_fs_info;
|
||||
if (ns->proc_self)
|
||||
dput(ns->proc_self);
|
||||
if (ns->proc_thread_self)
|
||||
dput(ns->proc_thread_self);
|
||||
kill_anon_super(sb);
|
||||
|
||||
/* Make the pid namespace safe for the next mount of proc */
|
||||
ns->proc_self = NULL;
|
||||
ns->proc_thread_self = NULL;
|
||||
ns->pid_gid = GLOBAL_ROOT_GID;
|
||||
ns->hide_pid = 0;
|
||||
|
||||
put_pid_ns(ns);
|
||||
put_pid_ns(fs_info->pid_ns);
|
||||
kfree(fs_info);
|
||||
}
|
||||
|
||||
static struct file_system_type proc_fs_type = {
|
||||
|
@ -12,7 +12,7 @@ static const char *proc_self_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
struct pid_namespace *ns = proc_pid_ns(inode);
|
||||
struct pid_namespace *ns = proc_pid_ns(inode->i_sb);
|
||||
pid_t tgid = task_tgid_nr_ns(current, ns);
|
||||
char *name;
|
||||
|
||||
@ -36,10 +36,10 @@ static unsigned self_inum __ro_after_init;
|
||||
int proc_setup_self(struct super_block *s)
|
||||
{
|
||||
struct inode *root_inode = d_inode(s->s_root);
|
||||
struct pid_namespace *ns = proc_pid_ns(root_inode);
|
||||
struct proc_fs_info *fs_info = proc_sb_info(s);
|
||||
struct dentry *self;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
|
||||
inode_lock(root_inode);
|
||||
self = d_alloc_name(s->s_root, "self");
|
||||
if (self) {
|
||||
@ -62,7 +62,7 @@ int proc_setup_self(struct super_block *s)
|
||||
if (ret)
|
||||
pr_err("proc_fill_super: can't allocate /proc/self\n");
|
||||
else
|
||||
ns->proc_self = self;
|
||||
fs_info->proc_self = self;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ static const char *proc_thread_self_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
struct pid_namespace *ns = proc_pid_ns(inode);
|
||||
struct pid_namespace *ns = proc_pid_ns(inode->i_sb);
|
||||
pid_t tgid = task_tgid_nr_ns(current, ns);
|
||||
pid_t pid = task_pid_nr_ns(current, ns);
|
||||
char *name;
|
||||
@ -36,7 +36,7 @@ static unsigned thread_self_inum __ro_after_init;
|
||||
int proc_setup_thread_self(struct super_block *s)
|
||||
{
|
||||
struct inode *root_inode = d_inode(s->s_root);
|
||||
struct pid_namespace *ns = proc_pid_ns(root_inode);
|
||||
struct proc_fs_info *fs_info = proc_sb_info(s);
|
||||
struct dentry *thread_self;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
@ -60,9 +60,9 @@ int proc_setup_thread_self(struct super_block *s)
|
||||
inode_unlock(root_inode);
|
||||
|
||||
if (ret)
|
||||
pr_err("proc_fill_super: can't allocate /proc/thread_self\n");
|
||||
pr_err("proc_fill_super: can't allocate /proc/thread-self\n");
|
||||
else
|
||||
ns->proc_thread_self = thread_self;
|
||||
fs_info->proc_thread_self = thread_self;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -37,14 +37,14 @@ static __poll_t mounts_poll(struct file *file, poll_table *wait)
|
||||
return res;
|
||||
}
|
||||
|
||||
struct proc_fs_info {
|
||||
struct proc_fs_opts {
|
||||
int flag;
|
||||
const char *str;
|
||||
};
|
||||
|
||||
static int show_sb_opts(struct seq_file *m, struct super_block *sb)
|
||||
{
|
||||
static const struct proc_fs_info fs_info[] = {
|
||||
static const struct proc_fs_opts fs_opts[] = {
|
||||
{ SB_SYNCHRONOUS, ",sync" },
|
||||
{ SB_DIRSYNC, ",dirsync" },
|
||||
{ SB_MANDLOCK, ",mand" },
|
||||
@ -52,9 +52,9 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb)
|
||||
{ SB_INLINECRYPT, ",inlinecrypt" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
const struct proc_fs_info *fs_infop;
|
||||
const struct proc_fs_opts *fs_infop;
|
||||
|
||||
for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
|
||||
for (fs_infop = fs_opts; fs_infop->flag; fs_infop++) {
|
||||
if (sb->s_flags & fs_infop->flag)
|
||||
seq_puts(m, fs_infop->str);
|
||||
}
|
||||
@ -64,7 +64,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb)
|
||||
|
||||
static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
|
||||
{
|
||||
static const struct proc_fs_info mnt_info[] = {
|
||||
static const struct proc_fs_opts mnt_opts[] = {
|
||||
{ MNT_NOSUID, ",nosuid" },
|
||||
{ MNT_NODEV, ",nodev" },
|
||||
{ MNT_NOEXEC, ",noexec" },
|
||||
@ -73,9 +73,9 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
|
||||
{ MNT_RELATIME, ",relatime" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
const struct proc_fs_info *fs_infop;
|
||||
const struct proc_fs_opts *fs_infop;
|
||||
|
||||
for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) {
|
||||
for (fs_infop = mnt_opts; fs_infop->flag; fs_infop++) {
|
||||
if (mnt->mnt_flags & fs_infop->flag)
|
||||
seq_puts(m, fs_infop->str);
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ extern void attach_pid(struct task_struct *task, enum pid_type);
|
||||
extern void detach_pid(struct task_struct *task, enum pid_type);
|
||||
extern void change_pid(struct task_struct *task, enum pid_type,
|
||||
struct pid *pid);
|
||||
extern void exchange_tids(struct task_struct *task, struct task_struct *old);
|
||||
extern void transfer_pid(struct task_struct *old, struct task_struct *new,
|
||||
enum pid_type);
|
||||
|
||||
|
@ -17,12 +17,6 @@
|
||||
|
||||
struct fs_pin;
|
||||
|
||||
enum { /* definitions for pid_namespace's hide_pid field */
|
||||
HIDEPID_OFF = 0,
|
||||
HIDEPID_NO_ACCESS = 1,
|
||||
HIDEPID_INVISIBLE = 2,
|
||||
};
|
||||
|
||||
struct pid_namespace {
|
||||
struct kref kref;
|
||||
struct idr idr;
|
||||
@ -32,17 +26,11 @@ struct pid_namespace {
|
||||
struct kmem_cache *pid_cachep;
|
||||
unsigned int level;
|
||||
struct pid_namespace *parent;
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct dentry *proc_self;
|
||||
struct dentry *proc_thread_self;
|
||||
#endif
|
||||
#ifdef CONFIG_BSD_PROCESS_ACCT
|
||||
struct fs_pin *bacct;
|
||||
#endif
|
||||
struct user_namespace *user_ns;
|
||||
struct ucounts *ucounts;
|
||||
kgid_t pid_gid;
|
||||
int hide_pid;
|
||||
int reboot; /* group exit code if this pidns was rebooted */
|
||||
struct ns_common ns;
|
||||
} __randomize_layout;
|
||||
|
@ -42,6 +42,34 @@ struct proc_ops {
|
||||
unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
|
||||
} __randomize_layout;
|
||||
|
||||
/* definitions for hide_pid field */
|
||||
enum proc_hidepid {
|
||||
HIDEPID_OFF = 0,
|
||||
HIDEPID_NO_ACCESS = 1,
|
||||
HIDEPID_INVISIBLE = 2,
|
||||
HIDEPID_NOT_PTRACEABLE = 4, /* Limit pids to only ptraceable pids */
|
||||
};
|
||||
|
||||
/* definitions for proc mount option pidonly */
|
||||
enum proc_pidonly {
|
||||
PROC_PIDONLY_OFF = 0,
|
||||
PROC_PIDONLY_ON = 1,
|
||||
};
|
||||
|
||||
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 */
|
||||
kgid_t pid_gid;
|
||||
enum proc_hidepid hide_pid;
|
||||
enum proc_pidonly pidonly;
|
||||
};
|
||||
|
||||
static inline struct proc_fs_info *proc_sb_info(struct super_block *sb)
|
||||
{
|
||||
return sb->s_fs_info;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
typedef int (*proc_write_t)(struct file *, char *, size_t);
|
||||
@ -177,9 +205,9 @@ int open_related_ns(struct ns_common *ns,
|
||||
struct ns_common *(*get_ns)(struct ns_common *ns));
|
||||
|
||||
/* get the associated pid namespace for a file in procfs */
|
||||
static inline struct pid_namespace *proc_pid_ns(const struct inode *inode)
|
||||
static inline struct pid_namespace *proc_pid_ns(struct super_block *sb)
|
||||
{
|
||||
return inode->i_sb->s_fs_info;
|
||||
return proc_sb_info(sb)->pid_ns;
|
||||
}
|
||||
|
||||
bool proc_ns_file(const struct file *file);
|
||||
|
@ -506,6 +506,27 @@ static inline void hlist_replace_rcu(struct hlist_node *old,
|
||||
WRITE_ONCE(old->pprev, LIST_POISON2);
|
||||
}
|
||||
|
||||
/**
|
||||
* hlists_swap_heads_rcu - swap the lists the hlist heads point to
|
||||
* @left: The hlist head on the left
|
||||
* @right: The hlist head on the right
|
||||
*
|
||||
* The lists start out as [@left ][node1 ... ] and
|
||||
[@right ][node2 ... ]
|
||||
* The lists end up as [@left ][node2 ... ]
|
||||
* [@right ][node1 ... ]
|
||||
*/
|
||||
static inline void hlists_swap_heads_rcu(struct hlist_head *left, struct hlist_head *right)
|
||||
{
|
||||
struct hlist_node *node1 = left->first;
|
||||
struct hlist_node *node2 = right->first;
|
||||
|
||||
rcu_assign_pointer(left->first, node2);
|
||||
rcu_assign_pointer(right->first, node1);
|
||||
WRITE_ONCE(node2->pprev, &left->first);
|
||||
WRITE_ONCE(node1->pprev, &right->first);
|
||||
}
|
||||
|
||||
/*
|
||||
* return the first or the next element in an RCU protected hlist
|
||||
*/
|
||||
|
@ -654,17 +654,6 @@ static inline bool thread_group_leader(struct task_struct *p)
|
||||
return p->exit_signal >= 0;
|
||||
}
|
||||
|
||||
/* Do to the insanities of de_thread it is possible for a process
|
||||
* to have the pid of the thread group leader without actually being
|
||||
* the thread group leader. For iteration through the pids in proc
|
||||
* all we care about is that we have a task with the appropriate
|
||||
* pid, we don't actually care if we have the right task.
|
||||
*/
|
||||
static inline bool has_group_leader_pid(struct task_struct *p)
|
||||
{
|
||||
return task_pid(p) == task_tgid(p);
|
||||
}
|
||||
|
||||
static inline
|
||||
bool same_thread_group(struct task_struct *p1, struct task_struct *p2)
|
||||
{
|
||||
|
@ -1761,7 +1761,7 @@ static void pidfd_show_fdinfo(struct seq_file *m, struct file *f)
|
||||
pid_t nr = -1;
|
||||
|
||||
if (likely(pid_has_task(pid, PIDTYPE_PID))) {
|
||||
ns = proc_pid_ns(file_inode(m->file));
|
||||
ns = proc_pid_ns(file_inode(m->file)->i_sb);
|
||||
nr = pid_nr_ns(pid, ns);
|
||||
}
|
||||
|
||||
|
22
kernel/pid.c
22
kernel/pid.c
@ -363,6 +363,25 @@ void change_pid(struct task_struct *task, enum pid_type type,
|
||||
attach_pid(task, type);
|
||||
}
|
||||
|
||||
void exchange_tids(struct task_struct *left, struct task_struct *right)
|
||||
{
|
||||
struct pid *pid1 = left->thread_pid;
|
||||
struct pid *pid2 = right->thread_pid;
|
||||
struct hlist_head *head1 = &pid1->tasks[PIDTYPE_PID];
|
||||
struct hlist_head *head2 = &pid2->tasks[PIDTYPE_PID];
|
||||
|
||||
/* Swap the single entry tid lists */
|
||||
hlists_swap_heads_rcu(head1, head2);
|
||||
|
||||
/* Swap the per task_struct pid */
|
||||
rcu_assign_pointer(left->thread_pid, pid2);
|
||||
rcu_assign_pointer(right->thread_pid, pid1);
|
||||
|
||||
/* Swap the cached value */
|
||||
WRITE_ONCE(left->pid, pid_nr(pid2));
|
||||
WRITE_ONCE(right->pid, pid_nr(pid1));
|
||||
}
|
||||
|
||||
/* transfer_pid is an optimization of attach_pid(new), detach_pid(old) */
|
||||
void transfer_pid(struct task_struct *old, struct task_struct *new,
|
||||
enum pid_type type)
|
||||
@ -476,8 +495,7 @@ pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
|
||||
rcu_read_lock();
|
||||
if (!ns)
|
||||
ns = task_active_pid_ns(current);
|
||||
if (likely(pid_alive(task)))
|
||||
nr = pid_nr_ns(rcu_dereference(*task_pid_ptr(task, type)), ns);
|
||||
nr = pid_nr_ns(rcu_dereference(*task_pid_ptr(task, type)), ns);
|
||||
rcu_read_unlock();
|
||||
|
||||
return nr;
|
||||
|
@ -47,85 +47,65 @@ void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
|
||||
/*
|
||||
* Functions for validating access to tasks.
|
||||
*/
|
||||
static struct task_struct *lookup_task(const pid_t pid, bool thread,
|
||||
bool gettime)
|
||||
static struct pid *pid_for_clock(const clockid_t clock, bool gettime)
|
||||
{
|
||||
struct task_struct *p;
|
||||
const bool thread = !!CPUCLOCK_PERTHREAD(clock);
|
||||
const pid_t upid = CPUCLOCK_PID(clock);
|
||||
struct pid *pid;
|
||||
|
||||
if (CPUCLOCK_WHICH(clock) >= CPUCLOCK_MAX)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* If the encoded PID is 0, then the timer is targeted at current
|
||||
* or the process to which current belongs.
|
||||
*/
|
||||
if (upid == 0)
|
||||
return thread ? task_pid(current) : task_tgid(current);
|
||||
|
||||
pid = find_vpid(upid);
|
||||
if (!pid)
|
||||
return thread ? current : current->group_leader;
|
||||
return NULL;
|
||||
|
||||
p = find_task_by_vpid(pid);
|
||||
if (!p)
|
||||
return p;
|
||||
|
||||
if (thread)
|
||||
return same_thread_group(p, current) ? p : NULL;
|
||||
|
||||
if (gettime) {
|
||||
/*
|
||||
* For clock_gettime(PROCESS) the task does not need to be
|
||||
* the actual group leader. tsk->sighand gives
|
||||
* access to the group's clock.
|
||||
*
|
||||
* Timers need the group leader because they take a
|
||||
* reference on it and store the task pointer until the
|
||||
* timer is destroyed.
|
||||
*/
|
||||
return (p == current || thread_group_leader(p)) ? p : NULL;
|
||||
if (thread) {
|
||||
struct task_struct *tsk = pid_task(pid, PIDTYPE_PID);
|
||||
return (tsk && same_thread_group(tsk, current)) ? pid : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For processes require that p is group leader.
|
||||
* For clock_gettime(PROCESS) allow finding the process by
|
||||
* with the pid of the current task. The code needs the tgid
|
||||
* of the process so that pid_task(pid, PIDTYPE_TGID) can be
|
||||
* used to find the process.
|
||||
*/
|
||||
return has_group_leader_pid(p) ? p : NULL;
|
||||
}
|
||||
if (gettime && (pid == task_pid(current)))
|
||||
return task_tgid(current);
|
||||
|
||||
static struct task_struct *__get_task_for_clock(const clockid_t clock,
|
||||
bool getref, bool gettime)
|
||||
{
|
||||
const bool thread = !!CPUCLOCK_PERTHREAD(clock);
|
||||
const pid_t pid = CPUCLOCK_PID(clock);
|
||||
struct task_struct *p;
|
||||
|
||||
if (CPUCLOCK_WHICH(clock) >= CPUCLOCK_MAX)
|
||||
return NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
p = lookup_task(pid, thread, gettime);
|
||||
if (p && getref)
|
||||
get_task_struct(p);
|
||||
rcu_read_unlock();
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline struct task_struct *get_task_for_clock(const clockid_t clock)
|
||||
{
|
||||
return __get_task_for_clock(clock, true, false);
|
||||
}
|
||||
|
||||
static inline struct task_struct *get_task_for_clock_get(const clockid_t clock)
|
||||
{
|
||||
return __get_task_for_clock(clock, true, true);
|
||||
/*
|
||||
* For processes require that pid identifies a process.
|
||||
*/
|
||||
return pid_has_task(pid, PIDTYPE_TGID) ? pid : NULL;
|
||||
}
|
||||
|
||||
static inline int validate_clock_permissions(const clockid_t clock)
|
||||
{
|
||||
return __get_task_for_clock(clock, false, false) ? 0 : -EINVAL;
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ret = pid_for_clock(clock, false) ? 0 : -EINVAL;
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline enum pid_type cpu_timer_pid_type(struct k_itimer *timer)
|
||||
static inline enum pid_type clock_pid_type(const clockid_t clock)
|
||||
{
|
||||
return CPUCLOCK_PERTHREAD(timer->it_clock) ? PIDTYPE_PID : PIDTYPE_TGID;
|
||||
return CPUCLOCK_PERTHREAD(clock) ? PIDTYPE_PID : PIDTYPE_TGID;
|
||||
}
|
||||
|
||||
static inline struct task_struct *cpu_timer_task_rcu(struct k_itimer *timer)
|
||||
{
|
||||
return pid_task(timer->it.cpu.pid, cpu_timer_pid_type(timer));
|
||||
return pid_task(timer->it.cpu.pid, clock_pid_type(timer->it_clock));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -373,15 +353,18 @@ static int posix_cpu_clock_get(const clockid_t clock, struct timespec64 *tp)
|
||||
struct task_struct *tsk;
|
||||
u64 t;
|
||||
|
||||
tsk = get_task_for_clock_get(clock);
|
||||
if (!tsk)
|
||||
rcu_read_lock();
|
||||
tsk = pid_task(pid_for_clock(clock, true), clock_pid_type(clock));
|
||||
if (!tsk) {
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (CPUCLOCK_PERTHREAD(clock))
|
||||
t = cpu_clock_sample(clkid, tsk);
|
||||
else
|
||||
t = cpu_clock_sample_group(clkid, tsk, false);
|
||||
put_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
|
||||
*tp = ns_to_timespec64(t);
|
||||
return 0;
|
||||
@ -394,19 +377,19 @@ static int posix_cpu_clock_get(const clockid_t clock, struct timespec64 *tp)
|
||||
*/
|
||||
static int posix_cpu_timer_create(struct k_itimer *new_timer)
|
||||
{
|
||||
struct task_struct *p = get_task_for_clock(new_timer->it_clock);
|
||||
struct pid *pid;
|
||||
|
||||
if (!p)
|
||||
rcu_read_lock();
|
||||
pid = pid_for_clock(new_timer->it_clock, false);
|
||||
if (!pid) {
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
new_timer->kclock = &clock_posix_cpu;
|
||||
timerqueue_init(&new_timer->it.cpu.node);
|
||||
new_timer->it.cpu.pid = get_task_pid(p, cpu_timer_pid_type(new_timer));
|
||||
/*
|
||||
* get_task_for_clock() took a reference on @p. Drop it as the timer
|
||||
* holds a reference on the pid of @p.
|
||||
*/
|
||||
put_task_struct(p);
|
||||
new_timer->it.cpu.pid = get_pid(pid);
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -779,7 +779,7 @@ static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
|
||||
|
||||
state->pid_ns = proc_pid_ns(file_inode(seq->file));
|
||||
state->pid_ns = proc_pid_ns(file_inode(seq->file)->i_sb);
|
||||
|
||||
rcu_read_lock_bh();
|
||||
return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include <linux/magic.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
/**
|
||||
* tomoyo_encode2 - Encode binary string to ascii string.
|
||||
@ -161,9 +162,10 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
|
||||
if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
|
||||
char *ep;
|
||||
const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
|
||||
struct pid_namespace *proc_pidns = proc_pid_ns(sb);
|
||||
|
||||
if (*ep == '/' && pid && pid ==
|
||||
task_tgid_nr_ns(current, sb->s_fs_info)) {
|
||||
task_tgid_nr_ns(current, proc_pidns)) {
|
||||
pos = ep - 5;
|
||||
if (pos < buffer)
|
||||
goto out;
|
||||
|
2
tools/testing/selftests/proc/.gitignore
vendored
2
tools/testing/selftests/proc/.gitignore
vendored
@ -2,7 +2,9 @@
|
||||
/fd-001-lookup
|
||||
/fd-002-posix-eq
|
||||
/fd-003-kthread
|
||||
/proc-fsconfig-hidepid
|
||||
/proc-loadavg-001
|
||||
/proc-multiple-procfs
|
||||
/proc-pid-vm
|
||||
/proc-self-map-files-001
|
||||
/proc-self-map-files-002
|
||||
|
@ -19,5 +19,7 @@ TEST_GEN_PROGS += self
|
||||
TEST_GEN_PROGS += setns-dcache
|
||||
TEST_GEN_PROGS += setns-sysvipc
|
||||
TEST_GEN_PROGS += thread-self
|
||||
TEST_GEN_PROGS += proc-multiple-procfs
|
||||
TEST_GEN_PROGS += proc-fsconfig-hidepid
|
||||
|
||||
include ../lib.mk
|
||||
|
50
tools/testing/selftests/proc/proc-fsconfig-hidepid.c
Normal file
50
tools/testing/selftests/proc/proc-fsconfig-hidepid.c
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright © 2020 Alexey Gladkov <gladkov.alexey@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
static inline int fsopen(const char *fsname, unsigned int flags)
|
||||
{
|
||||
return syscall(__NR_fsopen, fsname, flags);
|
||||
}
|
||||
|
||||
static inline int fsconfig(int fd, unsigned int cmd, const char *key, const void *val, int aux)
|
||||
{
|
||||
return syscall(__NR_fsconfig, fd, cmd, key, val, aux);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fsfd, ret;
|
||||
int hidepid = 2;
|
||||
|
||||
assert((fsfd = fsopen("proc", 0)) != -1);
|
||||
|
||||
ret = fsconfig(fsfd, FSCONFIG_SET_BINARY, "hidepid", &hidepid, 0);
|
||||
assert(ret == -1);
|
||||
assert(errno == EINVAL);
|
||||
|
||||
assert(!fsconfig(fsfd, FSCONFIG_SET_STRING, "hidepid", "2", 0));
|
||||
assert(!fsconfig(fsfd, FSCONFIG_SET_STRING, "hidepid", "invisible", 0));
|
||||
|
||||
assert(!close(fsfd));
|
||||
|
||||
return 0;
|
||||
}
|
48
tools/testing/selftests/proc/proc-multiple-procfs.c
Normal file
48
tools/testing/selftests/proc/proc-multiple-procfs.c
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2020 Alexey Gladkov <gladkov.alexey@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct stat proc_st1, proc_st2;
|
||||
char procbuff[] = "/tmp/proc.XXXXXX/meminfo";
|
||||
char procdir1[] = "/tmp/proc.XXXXXX";
|
||||
char procdir2[] = "/tmp/proc.XXXXXX";
|
||||
|
||||
assert(mkdtemp(procdir1) != NULL);
|
||||
assert(mkdtemp(procdir2) != NULL);
|
||||
|
||||
assert(!mount("proc", procdir1, "proc", 0, "hidepid=1"));
|
||||
assert(!mount("proc", procdir2, "proc", 0, "hidepid=2"));
|
||||
|
||||
snprintf(procbuff, sizeof(procbuff), "%s/meminfo", procdir1);
|
||||
assert(!stat(procbuff, &proc_st1));
|
||||
|
||||
snprintf(procbuff, sizeof(procbuff), "%s/meminfo", procdir2);
|
||||
assert(!stat(procbuff, &proc_st2));
|
||||
|
||||
umount(procdir1);
|
||||
umount(procdir2);
|
||||
|
||||
assert(proc_st1.st_dev != proc_st2.st_dev);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user