Re: [PATCH v2 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
From: Christian Brauner <brauner@kernel.org>
Date: 2026-09-04 09:31:16
Also in:
linux-fsdevel, linux-mm, selinux
On Fri, Aug 28, 2026 at 03:03:26PM +0200, Jann Horn wrote:
On Fri, Aug 28, 2026 at 3:10 AM Paul Moore [off-list ref] wrote:quoted
On Aug 25, 2026 Jann Horn [off-list ref] wrote:quoted
If the system is running with PROC_MEM_FORCE_ALWAYS, LSMs currently have no good opportunity to block a process from overwriting read-only code in its own address space through FOLL_FORCE writes via /proc/self/mem. The security_ptrace_access_check() LSM hook is bypassed when a process opens /proc/self/mem because this is considered "introspection". This causes a hole in SELinux EXECMEM enforcement, which tries to ensure that a process cannot create executable anonymous pages. PROC_MEM_FORCE_PTRACE prevents that and ensures that such FOLL_FORCE accesses are only possible when the LSM allows ptrace() attachment; but it is unclear how quickly PROC_MEM_FORCE_PTRACE can be deployed in environments running lots of third-party code, such as Android. So, introduce a new LSM hook that can forbid FOLL_FORCE specifically for such "introspective" accesses. Signed-off-by: Jann Horn <jannh@google.com> Acked-by: David Hildenbrand (Arm) <david@kernel.org> Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org> --- fs/proc/base.c | 9 +++++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 20 ++++++++++++++++++++ 4 files changed, 36 insertions(+)diff --git a/fs/proc/base.c b/fs/proc/base.c index bec6197329dc..dc6fdcb47b79 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c@@ -851,6 +851,11 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode) /* private_data for proc_mem_operations */ struct mem_private { struct mm_struct *mm; + /* + * Was the ptrace access check on open bypassed because the opener used + * the same MM (introspection)? + */ + bool opened_by_owner; }; static int mem_open(struct inode *inode, struct file *file)@@ -864,12 +869,14 @@ static int mem_open(struct inode *inode, struct file *file) priv->mm = proc_mem_open(inode, PTRACE_MODE_ATTACH); if (IS_ERR_OR_NULL(priv->mm)) return priv->mm ? PTR_ERR(priv->mm) : -ESRCH; + priv->opened_by_owner = priv->mm == current->mm; file->private_data = no_free_ptr(priv); return 0; } static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm) { + struct mem_private *priv = file->private_data; struct task_struct *task; bool ptrace_active = false;@@ -886,6 +893,8 @@ static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm) } return ptrace_active; default: + if (priv->opened_by_owner) + return security_mem_foll_force_opened_by_owner(file->f_cred) == 0; return true; } }First things first, we've got to shorten that hook name :) What do you think of security_proc_mem_foll_force()?If we move the "opened_by_owner" part into a flag then I guess that works... will do.quoted
Beyond that, we really try to avoid making LSM hook calls conditional. It can limit what an LSM can enforce, it tends to be a bit more fragile, and it adds some unnecessary work in the case where CONFIG_SECURITY is disabled. I would suggest passing 'opened_by_owner' flag as a second parameter to the LSM hook and calling the hook unconditionally in the default switch case as a replacement for the 'return true;' statement. I understand it may seem a bit odd, but we try to make the LSM interface as generic as possible with respect to different models and this is one wayI guess I can do that, but then the question becomes, what other modes of using the LSM hook that don't currently exist in the kernel should I be supporting with this? I can make this a parameter, but any LSM policy that actually uses the parameter in a different way would probably be buggy/inconsistent, unless other new LSM hooks are added. If your intent is to make the hook work for any /proc/$pid/mem access, including when the caller is ptrace-attached, then I guess I have to move around the security hook call in proc_mem_foll_force() a little bit. I guess I'll do that in v3, though I really don't like trying to come up with a reasonable in-kernel API contract for a scenario that currently has zero users.
So we effectively landed on what I proposed in the first version...