Thread (16 messages) flat view 16 messages, 6 authors, 14d ago

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 way
I 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... 
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help