Skip to content

Commit e8fc317

Browse files
committed
Merge tag 'vfs-6.12.procfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull procfs updates from Christian Brauner: "This contains the following changes for procfs: - Add config options and parameters to block forcing memory writes. This adds a Kconfig option and boot param to allow removing the FOLL_FORCE flag from /proc/<pid>/mem write calls as this can be used in various attacks. The traditional forcing behavior is kept as default because it can break GDB and some other use cases. This is the simpler version that you had requested. - Restrict overmounting of ephemeral entities. It is currently possible to mount on top of various ephemeral entities in procfs. This specifically includes magic links. To recap, magic links are links of the form /proc/<pid>/fd/<nr>. They serve as references to a target file and during path lookup they cause a jump to the target path. Such magic links disappear if the corresponding file descriptor is closed. Currently it is possible to overmount such magic links. This is mostly interesting for an attacker that wants to somehow trick a process into e.g., reopening something that it didn't intend to reopen or to hide a malicious file descriptor. But also it risks leaking mounts for long-running processes. When overmounting a magic link like above, the mount will not be detached when the file descriptor is closed. Only the target mountpoint will disappear. Which has the consequence of making it impossible to unmount that mount afterwards. So the mount will stick around until the process exits and the /proc/<pid>/ directory is cleaned up during proc_flush_pid() when the dentries are pruned and invalidated. That in turn means it's possible for a program to accidentally leak mounts and it's also possible to make a task leak mounts without it's knowledge if the attacker just keeps overmounting things under /proc/<pid>/fd/<nr>. Disallow overmounting of such ephemeral entities. - Cleanup the readdir method naming in some procfs file operations. - Replace kmalloc() and strcpy() with a simple kmemdup() call" * tag 'vfs-6.12.procfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: proc: fold kmalloc() + strcpy() into kmemdup() proc: block mounting on top of /proc/<pid>/fdinfo/* proc: block mounting on top of /proc/<pid>/fd/* proc: block mounting on top of /proc/<pid>/map_files/* proc: add proc_splice_unmountable() proc: proc_readfdinfo() -> proc_fdinfo_iterate() proc: proc_readfd() -> proc_fd_iterate() proc: add config & param to block forcing mem writes
2 parents ee25861 + 4ad5f9a commit e8fc317

File tree

6 files changed

+127
-13
lines changed

6 files changed

+127
-13
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4788,6 +4788,16 @@
47884788
printk.time= Show timing data prefixed to each printk message line
47894789
Format: <bool> (1/Y/y=enable, 0/N/n=disable)
47904790

4791+
proc_mem.force_override= [KNL]
4792+
Format: {always | ptrace | never}
4793+
Traditionally /proc/pid/mem allows memory permissions to be
4794+
overridden without restrictions. This option may be set to
4795+
restrict that. Can be one of:
4796+
- 'always': traditional behavior always allows mem overrides.
4797+
- 'ptrace': only allow mem overrides for active ptracers.
4798+
- 'never': never allow mem overrides.
4799+
If not specified, default is the CONFIG_PROC_MEM_* choice.
4800+
47914801
processor.max_cstate= [HW,ACPI]
47924802
Limit processor to maximum C-state
47934803
max_cstate=9 overrides any DMI blacklist limit.

fs/proc/base.c

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#include <linux/elf.h>
8686
#include <linux/pid_namespace.h>
8787
#include <linux/user_namespace.h>
88+
#include <linux/fs_parser.h>
8889
#include <linux/fs_struct.h>
8990
#include <linux/slab.h>
9091
#include <linux/sched/autogroup.h>
@@ -117,6 +118,40 @@
117118
static u8 nlink_tid __ro_after_init;
118119
static u8 nlink_tgid __ro_after_init;
119120

121+
enum proc_mem_force {
122+
PROC_MEM_FORCE_ALWAYS,
123+
PROC_MEM_FORCE_PTRACE,
124+
PROC_MEM_FORCE_NEVER
125+
};
126+
127+
static enum proc_mem_force proc_mem_force_override __ro_after_init =
128+
IS_ENABLED(CONFIG_PROC_MEM_NO_FORCE) ? PROC_MEM_FORCE_NEVER :
129+
IS_ENABLED(CONFIG_PROC_MEM_FORCE_PTRACE) ? PROC_MEM_FORCE_PTRACE :
130+
PROC_MEM_FORCE_ALWAYS;
131+
132+
static const struct constant_table proc_mem_force_table[] __initconst = {
133+
{ "always", PROC_MEM_FORCE_ALWAYS },
134+
{ "ptrace", PROC_MEM_FORCE_PTRACE },
135+
{ "never", PROC_MEM_FORCE_NEVER },
136+
{ }
137+
};
138+
139+
static int __init early_proc_mem_force_override(char *buf)
140+
{
141+
if (!buf)
142+
return -EINVAL;
143+
144+
/*
145+
* lookup_constant() defaults to proc_mem_force_override to preseve
146+
* the initial Kconfig choice in case an invalid param gets passed.
147+
*/
148+
proc_mem_force_override = lookup_constant(proc_mem_force_table,
149+
buf, proc_mem_force_override);
150+
151+
return 0;
152+
}
153+
early_param("proc_mem.force_override", early_proc_mem_force_override);
154+
120155
struct pid_entry {
121156
const char *name;
122157
unsigned int len;
@@ -832,6 +867,28 @@ static int mem_open(struct inode *inode, struct file *file)
832867
return __mem_open(inode, file, PTRACE_MODE_ATTACH);
833868
}
834869

870+
static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
871+
{
872+
struct task_struct *task;
873+
bool ptrace_active = false;
874+
875+
switch (proc_mem_force_override) {
876+
case PROC_MEM_FORCE_NEVER:
877+
return false;
878+
case PROC_MEM_FORCE_PTRACE:
879+
task = get_proc_task(file_inode(file));
880+
if (task) {
881+
ptrace_active = READ_ONCE(task->ptrace) &&
882+
READ_ONCE(task->mm) == mm &&
883+
READ_ONCE(task->parent) == current;
884+
put_task_struct(task);
885+
}
886+
return ptrace_active;
887+
default:
888+
return true;
889+
}
890+
}
891+
835892
static ssize_t mem_rw(struct file *file, char __user *buf,
836893
size_t count, loff_t *ppos, int write)
837894
{
@@ -852,7 +909,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
852909
if (!mmget_not_zero(mm))
853910
goto free;
854911

855-
flags = FOLL_FORCE | (write ? FOLL_WRITE : 0);
912+
flags = write ? FOLL_WRITE : 0;
913+
if (proc_mem_foll_force(file, mm))
914+
flags |= FOLL_FORCE;
856915

857916
while (count > 0) {
858917
size_t this_len = min_t(size_t, count, PAGE_SIZE);
@@ -2274,8 +2333,8 @@ proc_map_files_instantiate(struct dentry *dentry,
22742333
inode->i_op = &proc_map_files_link_inode_operations;
22752334
inode->i_size = 64;
22762335

2277-
d_set_d_op(dentry, &tid_map_files_dentry_operations);
2278-
return d_splice_alias(inode, dentry);
2336+
return proc_splice_unmountable(inode, dentry,
2337+
&tid_map_files_dentry_operations);
22792338
}
22802339

22812340
static struct dentry *proc_map_files_lookup(struct inode *dir,

fs/proc/fd.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ static struct dentry *proc_fd_instantiate(struct dentry *dentry,
220220
ei->op.proc_get_link = proc_fd_link;
221221
tid_fd_update_inode(task, inode, data->mode);
222222

223-
d_set_d_op(dentry, &tid_fd_dentry_operations);
224-
return d_splice_alias(inode, dentry);
223+
return proc_splice_unmountable(inode, dentry,
224+
&tid_fd_dentry_operations);
225225
}
226226

227227
static struct dentry *proc_lookupfd_common(struct inode *dir,
@@ -312,14 +312,14 @@ static int proc_readfd_count(struct inode *inode, loff_t *count)
312312
return 0;
313313
}
314314

315-
static int proc_readfd(struct file *file, struct dir_context *ctx)
315+
static int proc_fd_iterate(struct file *file, struct dir_context *ctx)
316316
{
317317
return proc_readfd_common(file, ctx, proc_fd_instantiate);
318318
}
319319

320320
const struct file_operations proc_fd_operations = {
321321
.read = generic_read_dir,
322-
.iterate_shared = proc_readfd,
322+
.iterate_shared = proc_fd_iterate,
323323
.llseek = generic_file_llseek,
324324
};
325325

@@ -397,8 +397,8 @@ static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry,
397397
inode->i_fop = &proc_fdinfo_file_operations;
398398
tid_fd_update_inode(task, inode, 0);
399399

400-
d_set_d_op(dentry, &tid_fd_dentry_operations);
401-
return d_splice_alias(inode, dentry);
400+
return proc_splice_unmountable(inode, dentry,
401+
&tid_fd_dentry_operations);
402402
}
403403

404404
static struct dentry *
@@ -407,7 +407,7 @@ proc_lookupfdinfo(struct inode *dir, struct dentry *dentry, unsigned int flags)
407407
return proc_lookupfd_common(dir, dentry, proc_fdinfo_instantiate);
408408
}
409409

410-
static int proc_readfdinfo(struct file *file, struct dir_context *ctx)
410+
static int proc_fdinfo_iterate(struct file *file, struct dir_context *ctx)
411411
{
412412
return proc_readfd_common(file, ctx,
413413
proc_fdinfo_instantiate);
@@ -421,6 +421,6 @@ const struct inode_operations proc_fdinfo_inode_operations = {
421421

422422
const struct file_operations proc_fdinfo_operations = {
423423
.read = generic_read_dir,
424-
.iterate_shared = proc_readfdinfo,
424+
.iterate_shared = proc_fdinfo_iterate,
425425
.llseek = generic_file_llseek,
426426
};

fs/proc/generic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,9 @@ struct proc_dir_entry *proc_symlink(const char *name,
464464
(S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
465465

466466
if (ent) {
467-
ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
467+
ent->size = strlen(dest);
468+
ent->data = kmemdup(dest, ent->size + 1, GFP_KERNEL);
468469
if (ent->data) {
469-
strcpy((char*)ent->data,dest);
470470
ent->proc_iops = &proc_link_inode_operations;
471471
ent = proc_register(parent, ent);
472472
} else {

fs/proc/internal.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,16 @@ static inline void pde_force_lookup(struct proc_dir_entry *pde)
349349
/* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
350350
pde->proc_dops = &proc_net_dentry_ops;
351351
}
352+
353+
/*
354+
* Add a new procfs dentry that can't serve as a mountpoint. That should
355+
* encompass anything that is ephemeral and can just disappear while the
356+
* process is still around.
357+
*/
358+
static inline struct dentry *proc_splice_unmountable(struct inode *inode,
359+
struct dentry *dentry, const struct dentry_operations *d_ops)
360+
{
361+
d_set_d_op(dentry, d_ops);
362+
dont_mount(dentry);
363+
return d_splice_alias(inode, dentry);
364+
}

security/Kconfig

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,38 @@ config SECURITY_DMESG_RESTRICT
1919

2020
If you are unsure how to answer this question, answer N.
2121

22+
choice
23+
prompt "Allow /proc/pid/mem access override"
24+
default PROC_MEM_ALWAYS_FORCE
25+
help
26+
Traditionally /proc/pid/mem allows users to override memory
27+
permissions for users like ptrace, assuming they have ptrace
28+
capability.
29+
30+
This allows people to limit that - either never override, or
31+
require actual active ptrace attachment.
32+
33+
Defaults to the traditional behavior (for now)
34+
35+
config PROC_MEM_ALWAYS_FORCE
36+
bool "Traditional /proc/pid/mem behavior"
37+
help
38+
This allows /proc/pid/mem accesses to override memory mapping
39+
permissions if you have ptrace access rights.
40+
41+
config PROC_MEM_FORCE_PTRACE
42+
bool "Require active ptrace() use for access override"
43+
help
44+
This allows /proc/pid/mem accesses to override memory mapping
45+
permissions for active ptracers like gdb.
46+
47+
config PROC_MEM_NO_FORCE
48+
bool "Never"
49+
help
50+
Never override memory mapping permissions
51+
52+
endchoice
53+
2254
config SECURITY
2355
bool "Enable different security models"
2456
depends on SYSFS

0 commit comments

Comments
 (0)