Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <luto@kernel.org>
Signed-off-by: Alexey Gladkov <gladkov.alexey@gmail.com>
Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>

authored by

Alexey Gladkov and committed by
Eric W. Biederman
1c6c4d11 37e7647a

+123 -28
+23 -23
Documentation/filesystems/proc.rst
··· 2147 2147 subset= Show only the specified subset of procfs. 2148 2148 ========= ======================================================== 2149 2149 2150 - hidepid=0 means classic mode - everybody may access all /proc/<pid>/ directories 2151 - (default). 2150 + hidepid=off or hidepid=0 means classic mode - everybody may access all 2151 + /proc/<pid>/ directories (default). 2152 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. 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 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. 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 2169 2170 - hidepid=4 means that procfs should only contain /proc/<pid>/ directories 2171 - that the caller can ptrace. 2170 + hidepid=ptraceable or hidepid=4 means that procfs should only contain 2171 + /proc/<pid>/ directories that the caller can ptrace. 2172 2172 2173 2173 gid= defines a group authorized to learn processes information otherwise 2174 2174 prohibited by hidepid=. If you use some daemon like identd which needs to learn ··· 2216 2216 It means that it became possible to have several procfs instances 2217 2217 displaying tasks with different filtering options in one pid namespace. 2218 2218 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 2221 2221 # 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
+14 -1
fs/proc/inode.c
··· 24 24 #include <linux/seq_file.h> 25 25 #include <linux/slab.h> 26 26 #include <linux/mount.h> 27 + #include <linux/bug.h> 27 28 28 29 #include <linux/uaccess.h> 29 30 ··· 166 165 deactivate_super(old_sb); 167 166 } 168 167 168 + static inline const char *hidepid2str(int v) 169 + { 170 + switch (v) { 171 + case HIDEPID_OFF: return "off"; 172 + case HIDEPID_NO_ACCESS: return "noaccess"; 173 + case HIDEPID_INVISIBLE: return "invisible"; 174 + case HIDEPID_NOT_PTRACEABLE: return "ptraceable"; 175 + } 176 + WARN_ONCE(1, "bad hide_pid value: %d\n", v); 177 + return "unknown"; 178 + } 179 + 169 180 static int proc_show_options(struct seq_file *seq, struct dentry *root) 170 181 { 171 182 struct proc_fs_info *fs_info = proc_sb_info(root->d_sb); ··· 185 172 if (!gid_eq(fs_info->pid_gid, GLOBAL_ROOT_GID)) 186 173 seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fs_info->pid_gid)); 187 174 if (fs_info->hide_pid != HIDEPID_OFF) 188 - seq_printf(seq, ",hidepid=%u", fs_info->hide_pid); 175 + seq_printf(seq, ",hidepid=%s", hidepid2str(fs_info->hide_pid)); 189 176 if (fs_info->pidonly != PROC_PIDONLY_OFF) 190 177 seq_printf(seq, ",subset=pid"); 191 178
+34 -4
fs/proc/root.c
··· 45 45 46 46 static const struct fs_parameter_spec proc_fs_parameters[] = { 47 47 fsparam_u32("gid", Opt_gid), 48 - fsparam_u32("hidepid", Opt_hidepid), 48 + fsparam_string("hidepid", Opt_hidepid), 49 49 fsparam_string("subset", Opt_subset), 50 50 {} 51 51 }; ··· 56 56 value == HIDEPID_NO_ACCESS || 57 57 value == HIDEPID_INVISIBLE || 58 58 value == HIDEPID_NOT_PTRACEABLE); 59 + } 60 + 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; 59 90 } 60 91 61 92 static int proc_parse_subset_param(struct fs_context *fc, char *value) ··· 128 97 break; 129 98 130 99 case Opt_hidepid: 131 - if (!valid_hidepid(result.uint_32)) 132 - return invalf(fc, "proc: unknown value of hidepid.\n"); 133 - ctx->hidepid = result.uint_32; 100 + if (proc_parse_hidepid_param(fc, param)) 101 + return -EINVAL; 134 102 break; 135 103 136 104 case Opt_subset:
+1
tools/testing/selftests/proc/.gitignore
··· 2 2 /fd-001-lookup 3 3 /fd-002-posix-eq 4 4 /fd-003-kthread 5 + /proc-fsconfig-hidepid 5 6 /proc-loadavg-001 6 7 /proc-multiple-procfs 7 8 /proc-pid-vm
+1
tools/testing/selftests/proc/Makefile
··· 20 20 TEST_GEN_PROGS += setns-sysvipc 21 21 TEST_GEN_PROGS += thread-self 22 22 TEST_GEN_PROGS += proc-multiple-procfs 23 + TEST_GEN_PROGS += proc-fsconfig-hidepid 23 24 24 25 include ../lib.mk
+50
tools/testing/selftests/proc/proc-fsconfig-hidepid.c
··· 1 + /* 2 + * Copyright © 2020 Alexey Gladkov <gladkov.alexey@gmail.com> 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 + }