Skip to content

Commit 1c6c4d1

Browse files
legionusebiederm
authored andcommitted
proc: use human-readable values for hidepid
The hidepid parameter values are becoming more and more and it becomes difficult to remember what each new magic number means. Backward compatibility is preserved since it is possible to specify numerical value for the hidepid parameter. This does not break the fsconfig since it is not possible to specify a numerical value through it. All numeric values are converted to a string. The type FSCONFIG_SET_BINARY cannot be used to indicate a numerical value. Selftest has been added to verify this behavior. Suggested-by: Andy Lutomirski <[email protected]> Signed-off-by: Alexey Gladkov <[email protected]> Reviewed-by: Alexey Dobriyan <[email protected]> Reviewed-by: Kees Cook <[email protected]> Signed-off-by: Eric W. Biederman <[email protected]>
1 parent 37e7647 commit 1c6c4d1

File tree

6 files changed

+126
-31
lines changed

6 files changed

+126
-31
lines changed

Documentation/filesystems/proc.rst

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,28 +2147,28 @@ The following mount options are supported:
21472147
subset= Show only the specified subset of procfs.
21482148
========= ========================================================
21492149

2150-
hidepid=0 means classic mode - everybody may access all /proc/<pid>/ directories
2151-
(default).
2152-
2153-
hidepid=1 means users may not access any /proc/<pid>/ directories but their
2154-
own. Sensitive files like cmdline, sched*, status are now protected against
2155-
other users. This makes it impossible to learn whether any user runs
2156-
specific program (given the program doesn't reveal itself by its behaviour).
2157-
As an additional bonus, as /proc/<pid>/cmdline is unaccessible for other users,
2158-
poorly written programs passing sensitive information via program arguments are
2159-
now protected against local eavesdroppers.
2160-
2161-
hidepid=2 means hidepid=1 plus all /proc/<pid>/ will be fully invisible to other
2162-
users. It doesn't mean that it hides a fact whether a process with a specific
2163-
pid value exists (it can be learned by other means, e.g. by "kill -0 $PID"),
2164-
but it hides process' uid and gid, which may be learned by stat()'ing
2165-
/proc/<pid>/ otherwise. It greatly complicates an intruder's task of gathering
2166-
information about running processes, whether some daemon runs with elevated
2167-
privileges, whether other user runs some sensitive program, whether other users
2168-
run any program at all, etc.
2169-
2170-
hidepid=4 means that procfs should only contain /proc/<pid>/ directories
2171-
that the caller can ptrace.
2150+
hidepid=off or hidepid=0 means classic mode - everybody may access all
2151+
/proc/<pid>/ directories (default).
2152+
2153+
hidepid=noaccess or hidepid=1 means users may not access any /proc/<pid>/
2154+
directories but their own. Sensitive files like cmdline, sched*, status are now
2155+
protected against other users. This makes it impossible to learn whether any
2156+
user runs specific program (given the program doesn't reveal itself by its
2157+
behaviour). As an additional bonus, as /proc/<pid>/cmdline is unaccessible for
2158+
other users, poorly written programs passing sensitive information via program
2159+
arguments are now protected against local eavesdroppers.
2160+
2161+
hidepid=invisible or hidepid=2 means hidepid=1 plus all /proc/<pid>/ will be
2162+
fully invisible to other users. It doesn't mean that it hides a fact whether a
2163+
process with a specific pid value exists (it can be learned by other means, e.g.
2164+
by "kill -0 $PID"), but it hides process' uid and gid, which may be learned by
2165+
stat()'ing /proc/<pid>/ otherwise. It greatly complicates an intruder's task of
2166+
gathering information about running processes, whether some daemon runs with
2167+
elevated privileges, whether other user runs some sensitive program, whether
2168+
other users run any program at all, etc.
2169+
2170+
hidepid=ptraceable or hidepid=4 means that procfs should only contain
2171+
/proc/<pid>/ directories that the caller can ptrace.
21722172

21732173
gid= defines a group authorized to learn processes information otherwise
21742174
prohibited by hidepid=. If you use some daemon like identd which needs to learn
@@ -2216,8 +2216,8 @@ creates a new procfs instance. Mount options affect own procfs instance.
22162216
It means that it became possible to have several procfs instances
22172217
displaying tasks with different filtering options in one pid namespace.
22182218

2219-
# mount -o hidepid=2 -t proc proc /proc
2220-
# mount -o hidepid=1 -t proc proc /tmp/proc
2219+
# mount -o hidepid=invisible -t proc proc /proc
2220+
# mount -o hidepid=noaccess -t proc proc /tmp/proc
22212221
# grep ^proc /proc/mounts
2222-
proc /proc proc rw,relatime,hidepid=2 0 0
2223-
proc /tmp/proc proc rw,relatime,hidepid=1 0 0
2222+
proc /proc proc rw,relatime,hidepid=invisible 0 0
2223+
proc /tmp/proc proc rw,relatime,hidepid=noaccess 0 0

fs/proc/inode.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/seq_file.h>
2525
#include <linux/slab.h>
2626
#include <linux/mount.h>
27+
#include <linux/bug.h>
2728

2829
#include <linux/uaccess.h>
2930

@@ -165,14 +166,26 @@ void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock
165166
deactivate_super(old_sb);
166167
}
167168

169+
static inline const char *hidepid2str(int v)
170+
{
171+
switch (v) {
172+
case HIDEPID_OFF: return "off";
173+
case HIDEPID_NO_ACCESS: return "noaccess";
174+
case HIDEPID_INVISIBLE: return "invisible";
175+
case HIDEPID_NOT_PTRACEABLE: return "ptraceable";
176+
}
177+
WARN_ONCE(1, "bad hide_pid value: %d\n", v);
178+
return "unknown";
179+
}
180+
168181
static int proc_show_options(struct seq_file *seq, struct dentry *root)
169182
{
170183
struct proc_fs_info *fs_info = proc_sb_info(root->d_sb);
171184

172185
if (!gid_eq(fs_info->pid_gid, GLOBAL_ROOT_GID))
173186
seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fs_info->pid_gid));
174187
if (fs_info->hide_pid != HIDEPID_OFF)
175-
seq_printf(seq, ",hidepid=%u", fs_info->hide_pid);
188+
seq_printf(seq, ",hidepid=%s", hidepid2str(fs_info->hide_pid));
176189
if (fs_info->pidonly != PROC_PIDONLY_OFF)
177190
seq_printf(seq, ",subset=pid");
178191

fs/proc/root.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ enum proc_param {
4545

4646
static const struct fs_parameter_spec proc_fs_parameters[] = {
4747
fsparam_u32("gid", Opt_gid),
48-
fsparam_u32("hidepid", Opt_hidepid),
48+
fsparam_string("hidepid", Opt_hidepid),
4949
fsparam_string("subset", Opt_subset),
5050
{}
5151
};
@@ -58,6 +58,37 @@ static inline int valid_hidepid(unsigned int value)
5858
value == HIDEPID_NOT_PTRACEABLE);
5959
}
6060

61+
static int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param)
62+
{
63+
struct proc_fs_context *ctx = fc->fs_private;
64+
struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid);
65+
struct fs_parse_result result;
66+
int base = (unsigned long)hidepid_u32_spec.data;
67+
68+
if (param->type != fs_value_is_string)
69+
return invalf(fc, "proc: unexpected type of hidepid value\n");
70+
71+
if (!kstrtouint(param->string, base, &result.uint_32)) {
72+
if (!valid_hidepid(result.uint_32))
73+
return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
74+
ctx->hidepid = result.uint_32;
75+
return 0;
76+
}
77+
78+
if (!strcmp(param->string, "off"))
79+
ctx->hidepid = HIDEPID_OFF;
80+
else if (!strcmp(param->string, "noaccess"))
81+
ctx->hidepid = HIDEPID_NO_ACCESS;
82+
else if (!strcmp(param->string, "invisible"))
83+
ctx->hidepid = HIDEPID_INVISIBLE;
84+
else if (!strcmp(param->string, "ptraceable"))
85+
ctx->hidepid = HIDEPID_NOT_PTRACEABLE;
86+
else
87+
return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
88+
89+
return 0;
90+
}
91+
6192
static int proc_parse_subset_param(struct fs_context *fc, char *value)
6293
{
6394
struct proc_fs_context *ctx = fc->fs_private;
@@ -97,9 +128,8 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
97128
break;
98129

99130
case Opt_hidepid:
100-
if (!valid_hidepid(result.uint_32))
101-
return invalf(fc, "proc: unknown value of hidepid.\n");
102-
ctx->hidepid = result.uint_32;
131+
if (proc_parse_hidepid_param(fc, param))
132+
return -EINVAL;
103133
break;
104134

105135
case Opt_subset:

tools/testing/selftests/proc/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/fd-001-lookup
33
/fd-002-posix-eq
44
/fd-003-kthread
5+
/proc-fsconfig-hidepid
56
/proc-loadavg-001
67
/proc-multiple-procfs
78
/proc-pid-vm

tools/testing/selftests/proc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ TEST_GEN_PROGS += setns-dcache
2020
TEST_GEN_PROGS += setns-sysvipc
2121
TEST_GEN_PROGS += thread-self
2222
TEST_GEN_PROGS += proc-multiple-procfs
23+
TEST_GEN_PROGS += proc-fsconfig-hidepid
2324

2425
include ../lib.mk
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright © 2020 Alexey Gladkov <[email protected]>
3+
*
4+
* Permission to use, copy, modify, and distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
#include <assert.h>
17+
#include <unistd.h>
18+
#include <stdlib.h>
19+
#include <errno.h>
20+
#include <linux/mount.h>
21+
#include <linux/unistd.h>
22+
23+
static inline int fsopen(const char *fsname, unsigned int flags)
24+
{
25+
return syscall(__NR_fsopen, fsname, flags);
26+
}
27+
28+
static inline int fsconfig(int fd, unsigned int cmd, const char *key, const void *val, int aux)
29+
{
30+
return syscall(__NR_fsconfig, fd, cmd, key, val, aux);
31+
}
32+
33+
int main(void)
34+
{
35+
int fsfd, ret;
36+
int hidepid = 2;
37+
38+
assert((fsfd = fsopen("proc", 0)) != -1);
39+
40+
ret = fsconfig(fsfd, FSCONFIG_SET_BINARY, "hidepid", &hidepid, 0);
41+
assert(ret == -1);
42+
assert(errno == EINVAL);
43+
44+
assert(!fsconfig(fsfd, FSCONFIG_SET_STRING, "hidepid", "2", 0));
45+
assert(!fsconfig(fsfd, FSCONFIG_SET_STRING, "hidepid", "invisible", 0));
46+
47+
assert(!close(fsfd));
48+
49+
return 0;
50+
}

0 commit comments

Comments
 (0)