Commit 41e8149c authored by Adrian Ratiu's avatar Adrian Ratiu Committed by Christian Brauner

proc: add config & param to block forcing mem writes

This adds a Kconfig option and boot param to allow removing
the FOLL_FORCE flag from /proc/pid/mem write calls because
it can be abused.

The traditional forcing behavior is kept as default because
it can break GDB and some other use cases.

Previously we tried a more sophisticated approach allowing
distributions to fine-tune /proc/pid/mem behavior, however
that got NAK-ed by Linus [1], who prefers this simpler
approach with semantics also easier to understand for users.

Link: https://lore.kernel.org/lkml/CAHk-=wiGWLChxYmUA5HrT5aopZrB7_2VTa0NLZcxORgkUe5tEQ@mail.gmail.com/ [1]
Cc: Doug Anderson <dianders@chromium.org>
Cc: Jeff Xu <jeffxu@google.com>
Cc: Jann Horn <jannh@google.com>
Cc: Kees Cook <kees@kernel.org>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Suggested-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarAdrian Ratiu <adrian.ratiu@collabora.com>
Link: https://lore.kernel.org/r/20240802080225.89408-1-adrian.ratiu@collabora.comSigned-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 8400291e
...@@ -4788,6 +4788,16 @@ ...@@ -4788,6 +4788,16 @@
printk.time= Show timing data prefixed to each printk message line printk.time= Show timing data prefixed to each printk message line
Format: <bool> (1/Y/y=enable, 0/N/n=disable) Format: <bool> (1/Y/y=enable, 0/N/n=disable)
proc_mem.force_override= [KNL]
Format: {always | ptrace | never}
Traditionally /proc/pid/mem allows memory permissions to be
overridden without restrictions. This option may be set to
restrict that. Can be one of:
- 'always': traditional behavior always allows mem overrides.
- 'ptrace': only allow mem overrides for active ptracers.
- 'never': never allow mem overrides.
If not specified, default is the CONFIG_PROC_MEM_* choice.
processor.max_cstate= [HW,ACPI] processor.max_cstate= [HW,ACPI]
Limit processor to maximum C-state Limit processor to maximum C-state
max_cstate=9 overrides any DMI blacklist limit. max_cstate=9 overrides any DMI blacklist limit.
......
...@@ -85,6 +85,7 @@ ...@@ -85,6 +85,7 @@
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/pid_namespace.h> #include <linux/pid_namespace.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/fs_parser.h>
#include <linux/fs_struct.h> #include <linux/fs_struct.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched/autogroup.h> #include <linux/sched/autogroup.h>
...@@ -117,6 +118,40 @@ ...@@ -117,6 +118,40 @@
static u8 nlink_tid __ro_after_init; static u8 nlink_tid __ro_after_init;
static u8 nlink_tgid __ro_after_init; static u8 nlink_tgid __ro_after_init;
enum proc_mem_force {
PROC_MEM_FORCE_ALWAYS,
PROC_MEM_FORCE_PTRACE,
PROC_MEM_FORCE_NEVER
};
static enum proc_mem_force proc_mem_force_override __ro_after_init =
IS_ENABLED(CONFIG_PROC_MEM_NO_FORCE) ? PROC_MEM_FORCE_NEVER :
IS_ENABLED(CONFIG_PROC_MEM_FORCE_PTRACE) ? PROC_MEM_FORCE_PTRACE :
PROC_MEM_FORCE_ALWAYS;
static const struct constant_table proc_mem_force_table[] __initconst = {
{ "always", PROC_MEM_FORCE_ALWAYS },
{ "ptrace", PROC_MEM_FORCE_PTRACE },
{ "never", PROC_MEM_FORCE_NEVER },
{ }
};
static int __init early_proc_mem_force_override(char *buf)
{
if (!buf)
return -EINVAL;
/*
* lookup_constant() defaults to proc_mem_force_override to preseve
* the initial Kconfig choice in case an invalid param gets passed.
*/
proc_mem_force_override = lookup_constant(proc_mem_force_table,
buf, proc_mem_force_override);
return 0;
}
early_param("proc_mem.force_override", early_proc_mem_force_override);
struct pid_entry { struct pid_entry {
const char *name; const char *name;
unsigned int len; unsigned int len;
...@@ -835,6 +870,28 @@ static int mem_open(struct inode *inode, struct file *file) ...@@ -835,6 +870,28 @@ static int mem_open(struct inode *inode, struct file *file)
return ret; return ret;
} }
static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
{
struct task_struct *task;
bool ptrace_active = false;
switch (proc_mem_force_override) {
case PROC_MEM_FORCE_NEVER:
return false;
case PROC_MEM_FORCE_PTRACE:
task = get_proc_task(file_inode(file));
if (task) {
ptrace_active = READ_ONCE(task->ptrace) &&
READ_ONCE(task->mm) == mm &&
READ_ONCE(task->parent) == current;
put_task_struct(task);
}
return ptrace_active;
default:
return true;
}
}
static ssize_t mem_rw(struct file *file, char __user *buf, static ssize_t mem_rw(struct file *file, char __user *buf,
size_t count, loff_t *ppos, int write) size_t count, loff_t *ppos, int write)
{ {
...@@ -855,7 +912,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf, ...@@ -855,7 +912,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
if (!mmget_not_zero(mm)) if (!mmget_not_zero(mm))
goto free; goto free;
flags = FOLL_FORCE | (write ? FOLL_WRITE : 0); flags = write ? FOLL_WRITE : 0;
if (proc_mem_foll_force(file, mm))
flags |= FOLL_FORCE;
while (count > 0) { while (count > 0) {
size_t this_len = min_t(size_t, count, PAGE_SIZE); size_t this_len = min_t(size_t, count, PAGE_SIZE);
......
...@@ -19,6 +19,38 @@ config SECURITY_DMESG_RESTRICT ...@@ -19,6 +19,38 @@ config SECURITY_DMESG_RESTRICT
If you are unsure how to answer this question, answer N. If you are unsure how to answer this question, answer N.
choice
prompt "Allow /proc/pid/mem access override"
default PROC_MEM_ALWAYS_FORCE
help
Traditionally /proc/pid/mem allows users to override memory
permissions for users like ptrace, assuming they have ptrace
capability.
This allows people to limit that - either never override, or
require actual active ptrace attachment.
Defaults to the traditional behavior (for now)
config PROC_MEM_ALWAYS_FORCE
bool "Traditional /proc/pid/mem behavior"
help
This allows /proc/pid/mem accesses to override memory mapping
permissions if you have ptrace access rights.
config PROC_MEM_FORCE_PTRACE
bool "Require active ptrace() use for access override"
help
This allows /proc/pid/mem accesses to override memory mapping
permissions for active ptracers like gdb.
config PROC_MEM_NO_FORCE
bool "Never"
help
Never override memory mapping permissions
endchoice
config SECURITY config SECURITY
bool "Enable different security models" bool "Enable different security models"
depends on SYSFS depends on SYSFS
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment