All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
@ 2026-09-07 21:00 Jann Horn
  2026-09-07 21:00 ` [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data Jann Horn
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Jann Horn @ 2026-09-07 21:00 UTC (permalink / raw)
  To: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen
  Cc: Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
	linux-security-module, Ondrej Mosnacek, selinux, Andrew Morton,
	Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm, Jann Horn

The goal of this series is to let SELinux prevent the use of FOLL_FORCE
when a process writes into /proc/self/mem and the system is configured
with PROC_MEM_FORCE_ALWAYS (which used to be the default behavior, and
is still used by current Android devices).

Android has SELinux policy that attempts to ensure that only trusted
code can be mapped as executable in several system processes, but this
protection can currently be bypassed by writing into /proc/self/mem.

I wrote this series after discussion with Android security folks about
the state of proc_mem_foll_force() restrictions on Android.

I'm sending this to:

 - maintainers for LSM hooks
 - maintainers for SELinux
 - maintainers for VFS (because I think they generally own procfs?)
 - some MM folks just as FYI since this touches GUP usage
 - the Android folks I talked to about this

I think this should probably go through either the VFS tree or the
LSM tree.

The motivation for this series is that Project Zero managed to write a
remote exploit for Google Pixel partly because of /proc/self/mem, see
<https://projectzero.google/2026/01/pixel-0-click-part-1.html#whats-the-plan-seth-and-jann>.

Signed-off-by: Jann Horn <jannh@google.com>
---
Changes in v3:
- pass opened_by_owner as boolean flag (Christian, Paul)
- call security hook independent of kernel config
- documentation changes (mainly to adjust for changes above or suggested by Paul)
- add acks
- Link to v2: https://patch.msgid.link/20260825-selinux-pokemem-v2-0-b46bc64916d8@google.com

Changes in v2:
- change SELinux hook to only check PROCESS__PTRACE (Stephen Smalley)
- rename ->introspection to ->opened_by_owner (David Hildenbrand)
- rename introspect_mem_foll_force hook to mem_foll_force_opened_by_owner
- improve comments
- add ack/review trailers
- Link to v1: https://patch.msgid.link/20260818-selinux-pokemem-v1-0-90cd2357ee05@google.com

---
Jann Horn (3):
      proc: refactor /proc/$pid/mem to use struct as private_data
      proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
      selinux: require PROCESS__PTRACE for FOLL_FORCE introspection

 fs/proc/base.c                | 43 ++++++++++++++++++++++++++++++++++++++-----
 include/linux/lsm_hook_defs.h |  1 +
 include/linux/security.h      |  7 +++++++
 security/security.c           | 25 +++++++++++++++++++++++++
 security/selinux/hooks.c      | 31 +++++++++++++++++++++++++++++++
 5 files changed, 102 insertions(+), 5 deletions(-)
---
base-commit: 2f1baf1fc8929e6c48370be543ad028ac7ad4131
change-id: 20260814-selinux-pokemem-44625557c4d4

Best regards,
--  
Jann Horn <jannh@google.com>


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data
  2026-09-07 21:00 [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Jann Horn
@ 2026-09-07 21:00 ` Jann Horn
  2026-09-07 21:07   ` sashiko-bot
  2026-09-07 21:00 ` [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS) Jann Horn
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 17+ messages in thread
From: Jann Horn @ 2026-09-07 21:00 UTC (permalink / raw)
  To: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen
  Cc: Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
	linux-security-module, Ondrej Mosnacek, selinux, Andrew Morton,
	Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm, Jann Horn

Refactor the handlers for proc_mem_operations to use the new struct
mem_private as ->private_data, rather than directly storing an mm_struct*
in ->private_data.

This is in preparation for adding more state in mem_private in the next
commit.

Reviewed-by: Jan Kara <jack@suse.cz>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Jann Horn <jannh@google.com>
---
 fs/proc/base.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 780f81259052..bec6197329dc 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -848,11 +848,24 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
 	return 0;
 }
 
+/* private_data for proc_mem_operations */
+struct mem_private {
+	struct mm_struct *mm;
+};
+
 static int mem_open(struct inode *inode, struct file *file)
 {
+	struct mem_private *priv __free(kfree) = kmalloc_obj(struct mem_private);
+
+	if (!priv)
+		return -ENOMEM;
 	if (WARN_ON_ONCE(!(file->f_op->fop_flags & FOP_UNSIGNED_OFFSET)))
 		return -EINVAL;
-	return __mem_open(inode, file, PTRACE_MODE_ATTACH);
+	priv->mm = proc_mem_open(inode, PTRACE_MODE_ATTACH);
+	if (IS_ERR_OR_NULL(priv->mm))
+		return priv->mm ? PTR_ERR(priv->mm) : -ESRCH;
+	file->private_data = no_free_ptr(priv);
+	return 0;
 }
 
 static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
@@ -880,7 +893,8 @@ static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
 static ssize_t mem_rw(struct file *file, char __user *buf,
 			size_t count, loff_t *ppos, int write)
 {
-	struct mm_struct *mm = file->private_data;
+	struct mem_private *priv = file->private_data;
+	struct mm_struct *mm = priv->mm;
 	unsigned long addr = *ppos;
 	ssize_t copied;
 	char *page;
@@ -970,12 +984,21 @@ static int mem_release(struct inode *inode, struct file *file)
 	return 0;
 }
 
+static int mem_release_with_private(struct inode *inode, struct file *file)
+{
+	struct mem_private *priv = file->private_data;
+
+	mmdrop(priv->mm);
+	kfree(priv);
+	return 0;
+}
+
 static const struct file_operations proc_mem_operations = {
 	.llseek		= mem_lseek,
 	.read		= mem_read,
 	.write		= mem_write,
 	.open		= mem_open,
-	.release	= mem_release,
+	.release	= mem_release_with_private,
 	.fop_flags	= FOP_UNSIGNED_OFFSET,
 };
 

-- 
2.55.0.1003.g10538fe699-goog


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-07 21:00 [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Jann Horn
  2026-09-07 21:00 ` [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data Jann Horn
@ 2026-09-07 21:00 ` Jann Horn
  2026-09-07 21:09   ` sashiko-bot
  2026-09-15 17:04   ` Serge Hallyn (AMD)
  2026-09-07 21:00 ` [PATCH v3 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection Jann Horn
  2026-09-10  7:48 ` [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Christian Brauner
  3 siblings, 2 replies; 17+ messages in thread
From: Jann Horn @ 2026-09-07 21:00 UTC (permalink / raw)
  To: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen
  Cc: Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
	linux-security-module, Ondrej Mosnacek, selinux, Andrew Morton,
	Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm, Jann Horn

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.

Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Jann Horn <jannh@google.com>
---
 fs/proc/base.c                | 14 ++++++++++++--
 include/linux/lsm_hook_defs.h |  1 +
 include/linux/security.h      |  7 +++++++
 security/security.c           | 25 +++++++++++++++++++++++++
 4 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index bec6197329dc..295b21203c7f 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;
 
@@ -884,10 +891,13 @@ static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
 					READ_ONCE(task->parent) == current;
 			put_task_struct(task);
 		}
-		return ptrace_active;
+		if (!ptrace_active)
+			return false;
+		break;
 	default:
-		return true;
+		break;
 	}
+	return security_mem_foll_force(file->f_cred, priv->opened_by_owner) == 0;
 }
 
 static ssize_t mem_rw(struct file *file, char __user *buf,
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 65c9609ec207..12f84a1e6fab 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -36,6 +36,7 @@ LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
 LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
 	 unsigned int mode)
 LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
+LSM_HOOK(int, 0, mem_foll_force, const struct cred *subject, bool opened_by_owner)
 LSM_HOOK(int, 0, capget, const struct task_struct *target, kernel_cap_t *effective,
 	 kernel_cap_t *inheritable, kernel_cap_t *permitted)
 LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
diff --git a/include/linux/security.h b/include/linux/security.h
index 153e9043058f..e8bc2e644241 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -338,6 +338,7 @@ int security_binder_transfer_file(const struct cred *from,
 				  const struct cred *to, const struct file *file);
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
 int security_ptrace_traceme(struct task_struct *parent);
+int security_mem_foll_force(const struct cred *subject, bool opened_by_owner);
 int security_capget(const struct task_struct *target,
 		    kernel_cap_t *effective,
 		    kernel_cap_t *inheritable,
@@ -676,6 +677,12 @@ static inline int security_ptrace_traceme(struct task_struct *parent)
 	return cap_ptrace_traceme(parent);
 }
 
+static inline int security_mem_foll_force(const struct cred *subject,
+					  bool opened_by_owner)
+{
+	return 0;
+}
+
 static inline int security_capget(const struct task_struct *target,
 				   kernel_cap_t *effective,
 				   kernel_cap_t *inheritable,
diff --git a/security/security.c b/security/security.c
index 71aea8fdf014..2cde1efdb7a6 100644
--- a/security/security.c
+++ b/security/security.c
@@ -595,6 +595,31 @@ int security_ptrace_traceme(struct task_struct *parent)
 	return call_int_hook(ptrace_traceme, parent);
 }
 
+/**
+ * security_mem_foll_force() - Check if FOLL_FORCE is allowed
+ * @subject: credentials using which /proc/$pid/mem was opened
+ * @opened_by_owner: whether checks on open() were bypassed because the opener
+ *                   has the same MM as the target
+ *
+ * Check if FOLL_FORCE is allowed for accessing process memory through
+ * /proc/$pid/mem. opened_by_owner signals whether the opener's MM was the same
+ * as the target MM, meaning the security_ptrace_access_check() hook was
+ * bypassed on open().
+ * (Current current->mm does not matter for this; for example, if write() is
+ * called on an FD that was received from another process which obtained it with
+ * open("/proc/self/mem"), @opened_by_owner is still true.)
+ *
+ * Note that this hook is only designed to be useful in the opened_by_owner
+ * case, where the subject credentials effectively also describe the object.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mem_foll_force(const struct cred *subject,
+					    bool opened_by_owner)
+{
+	return call_int_hook(mem_foll_force, subject, opened_by_owner);
+}
+
 /**
  * security_capget() - Get the capability sets for a process
  * @target: target process

-- 
2.55.0.1003.g10538fe699-goog


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection
  2026-09-07 21:00 [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Jann Horn
  2026-09-07 21:00 ` [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data Jann Horn
  2026-09-07 21:00 ` [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS) Jann Horn
@ 2026-09-07 21:00 ` Jann Horn
  2026-09-07 21:08   ` sashiko-bot
  2026-09-10  7:48 ` [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Christian Brauner
  3 siblings, 1 reply; 17+ messages in thread
From: Jann Horn @ 2026-09-07 21:00 UTC (permalink / raw)
  To: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen
  Cc: Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
	linux-security-module, Ondrej Mosnacek, selinux, Andrew Morton,
	Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm, Jann Horn

On systems configured with PROC_MEM_FORCE_ALWAYS, ensure that a process can
only create anonymous executable memory via /proc/self/mem if it has
PROCESS__PTRACE (like when using /proc/$pid/mem of another process).

This closes a hole in code integrity enforcement that Project Zero has used
in a remote Android exploit chain:
It was possible to use a memory corruption bug in a service without
EXECMEM/EXECMOD/PTRACE permission to overwrite executable code via
/proc/self/mem, which made it possible to load and run shellcode containing
a kernel exploit.

Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Jann Horn <jannh@google.com>
---
 security/selinux/hooks.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 18dd28b2bb13..45ead18c8f01 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2157,6 +2157,36 @@ static int selinux_ptrace_traceme(struct task_struct *parent)
 			    SECCLASS_PROCESS, PROCESS__PTRACE, NULL);
 }
 
+/**
+ * selinux_mem_foll_force() - Determine whether /proc/$pid/mem can use FOLL_FORCE
+ * @subject: credentials using which /proc/$pid/mem was opened
+ * @opened_by_owner: whether checks on open() were bypassed because the opener
+ *                   has the same MM as the target
+ *
+ * Decide whether it should be possible to read non-readable VMAs and write
+ * non-writable VMAs via /proc/self/mem.
+ * The @opened_by_owner case only applies to systems configured with
+ * PROC_MEM_FORCE_ALWAYS, and only happens on accesses that are not visible to
+ * selinux_ptrace_access_check() because of the introspection exceptions in
+ * may_access_mm() and __ptrace_may_access().
+ *
+ * This allows a process to overwrite read-only code in its own address space.
+ *
+ * Creating an audit record on denial doesn't make sense here, since we can't
+ * tell whether FOLL_FORCE matters for the accessed VMAs.
+ */
+static int selinux_mem_foll_force(const struct cred *subject, bool opened_by_owner)
+{
+	struct av_decision avd;
+	u32 sid;
+
+	if (!opened_by_owner)
+		return 0;
+	sid = cred_sid(subject);
+
+	return avc_has_perm_noaudit(sid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, 0, &avd);
+}
+
 static int selinux_capget(const struct task_struct *target, kernel_cap_t *effective,
 			  kernel_cap_t *inheritable, kernel_cap_t *permitted)
 {
@@ -7558,6 +7588,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 
 	LSM_HOOK_INIT(ptrace_access_check, selinux_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, selinux_ptrace_traceme),
+	LSM_HOOK_INIT(mem_foll_force, selinux_mem_foll_force),
 	LSM_HOOK_INIT(capget, selinux_capget),
 	LSM_HOOK_INIT(capset, selinux_capset),
 	LSM_HOOK_INIT(capable, selinux_capable),

-- 
2.55.0.1003.g10538fe699-goog


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data
  2026-09-07 21:00 ` [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data Jann Horn
@ 2026-09-07 21:07   ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-09-07 21:07 UTC (permalink / raw)
  Cc: selinux

> Refactor the handlers for proc_mem_operations to use the new struct
> mem_private as ->private_data, rather than directly storing an mm_struct*
> in ->private_data.
> 
> This is in preparation for adding more state in mem_private in the next
> commit.
> 
> Reviewed-by: Jan Kara <jack@suse.cz>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> Signed-off-by: Jann Horn <jannh@google.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260907-selinux-pokemem-v3-0-0bafbaeafe50@google.com?part=1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection
  2026-09-07 21:00 ` [PATCH v3 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection Jann Horn
@ 2026-09-07 21:08   ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-09-07 21:08 UTC (permalink / raw)
  Cc: selinux

> On systems configured with PROC_MEM_FORCE_ALWAYS, ensure that a process can
> only create anonymous executable memory via /proc/self/mem if it has
> PROCESS__PTRACE (like when using /proc/$pid/mem of another process).
> 
> This closes a hole in code integrity enforcement that Project Zero has used
> in a remote Android exploit chain:
> It was possible to use a memory corruption bug in a service without
> EXECMEM/EXECMOD/PTRACE permission to overwrite executable code via
> /proc/self/mem, which made it possible to load and run shellcode containing
> a kernel exploit.
> 
> Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> Signed-off-by: Jann Horn <jannh@google.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260907-selinux-pokemem-v3-0-0bafbaeafe50@google.com?part=3


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-07 21:00 ` [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS) Jann Horn
@ 2026-09-07 21:09   ` sashiko-bot
  2026-09-15 17:04   ` Serge Hallyn (AMD)
  1 sibling, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-09-07 21:09 UTC (permalink / raw)
  Cc: selinux

> 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.
> 
> Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> Signed-off-by: Jann Horn <jannh@google.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260907-selinux-pokemem-v3-0-0bafbaeafe50@google.com?part=2


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
  2026-09-07 21:00 [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Jann Horn
                   ` (2 preceding siblings ...)
  2026-09-07 21:00 ` [PATCH v3 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection Jann Horn
@ 2026-09-10  7:48 ` Christian Brauner
  2026-09-14 17:05   ` Paul Moore
  3 siblings, 1 reply; 17+ messages in thread
From: Christian Brauner @ 2026-09-10  7:48 UTC (permalink / raw)
  To: Jann Horn
  Cc: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro, Jan Kara,
	linux-fsdevel, linux-security-module, Ondrej Mosnacek, selinux,
	Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka,
	Pedro Falcato, David Hildenbrand, linux-mm

On Mon, 07 Sep 2026 23:00:15 +0200, Jann Horn wrote:
> proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
> 
> The goal of this series is to let SELinux prevent the use of FOLL_FORCE
> when a process writes into /proc/self/mem and the system is configured
> with PROC_MEM_FORCE_ALWAYS (which used to be the default behavior, and
> is still used by current Android devices).
> 
> [...]

Let's move this on a shared branch that both the vfs tree and the lsm
tree pull. That's the standard way of handling dependencies between two
subsystems that want to route code that touches both of them. Branch is
stable.

---

Applied to the vfs-7.4.shared.lsm.foll_force branch of the vfs/vfs.git tree.
Patches in the vfs-7.4.shared.lsm.foll_force branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.4.shared.lsm.foll_force

[1/3] proc: refactor /proc/$pid/mem to use struct as private_data
      https://git.kernel.org/vfs/vfs/c/d5662602d178
[2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
      https://git.kernel.org/vfs/vfs/c/9f90f96af73d
[3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection
      https://git.kernel.org/vfs/vfs/c/5dac8c291d9b


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
  2026-09-10  7:48 ` [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Christian Brauner
@ 2026-09-14 17:05   ` Paul Moore
  2026-09-16  9:01     ` Christian Brauner
  0 siblings, 1 reply; 17+ messages in thread
From: Paul Moore @ 2026-09-14 17:05 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Jann Horn, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro, Jan Kara,
	linux-fsdevel, linux-security-module, Ondrej Mosnacek, selinux,
	Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka,
	Pedro Falcato, David Hildenbrand, linux-mm

On Thu, Sep 10, 2026 at 3:48 AM Christian Brauner <brauner@kernel.org> wrote:
> On Mon, 07 Sep 2026 23:00:15 +0200, Jann Horn wrote:
> > proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
> >
> > The goal of this series is to let SELinux prevent the use of FOLL_FORCE
> > when a process writes into /proc/self/mem and the system is configured
> > with PROC_MEM_FORCE_ALWAYS (which used to be the default behavior, and
> > is still used by current Android devices).
> >
> > [...]
>
> Let's move this on a shared branch that both the vfs tree and the lsm
> tree pull. That's the standard way of handling dependencies between two
> subsystems that want to route code that touches both of them. Branch is
> stable.

For some reason (/me looks at gmail) your email never hit my inbox, I
only noticed it now while reviewing Jann's patchset.

Christian, can you explain why you merged LSM code that I haven't
ACK'd?  We've talked about this in the past and I think I've been
fairly clear about it; I wouldn't merge VFS code without an
ACK/Reviewed-by/etc. from you or Al, I've been expecting the same
consideration from you.

While I don't have a problem with a topic branch for this, can you
also explain why this topic branch should live in the VFS tree?  There
are more changes under security/ than fs/, and unless I've misread
Jann's cover letter, this entire patchset is focused around enabling
LSM/SELinux controls and not necessarily anything really new from a
VFS perspective.

> ---
>
> Applied to the vfs-7.4.shared.lsm.foll_force branch of the vfs/vfs.git tree.
> Patches in the vfs-7.4.shared.lsm.foll_force branch should appear in linux-next soon.
>
> Please report any outstanding bugs that were missed during review in a
> new review to the original patch series allowing us to drop it.
>
> It's encouraged to provide Acked-bys and Reviewed-bys even though the
> patch has now been applied. If possible patch trailers will be updated.
>
> Note that commit hashes shown below are subject to change due to rebase,
> trailer updates or similar. If in doubt, please check the listed branch.
>
> tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
> branch: vfs-7.4.shared.lsm.foll_force
>
> [1/3] proc: refactor /proc/$pid/mem to use struct as private_data
>       https://git.kernel.org/vfs/vfs/c/d5662602d178
> [2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
>       https://git.kernel.org/vfs/vfs/c/9f90f96af73d
> [3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection
>       https://git.kernel.org/vfs/vfs/c/5dac8c291d9b

-- 
paul-moore.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-07 21:00 ` [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS) Jann Horn
  2026-09-07 21:09   ` sashiko-bot
@ 2026-09-15 17:04   ` Serge Hallyn (AMD)
  2026-09-15 17:24     ` Paul Moore
  2026-09-15 17:44     ` Jann Horn
  1 sibling, 2 replies; 17+ messages in thread
From: Serge Hallyn (AMD) @ 2026-09-15 17:04 UTC (permalink / raw)
  To: Jann Horn
  Cc: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro,
	Christian Brauner, Jan Kara, linux-fsdevel, linux-security-module,
	Ondrej Mosnacek, selinux, Andrew Morton, Liam R. Howlett,
	Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm

On Mon, Sep 07, 2026 at 11:00:17PM +0200, Jann Horn wrote:
> 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.
> 
> Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> Signed-off-by: Jann Horn <jannh@google.com>
> ---
>  fs/proc/base.c                | 14 ++++++++++++--
>  include/linux/lsm_hook_defs.h |  1 +
>  include/linux/security.h      |  7 +++++++
>  security/security.c           | 25 +++++++++++++++++++++++++
>  4 files changed, 45 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index bec6197329dc..295b21203c7f 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;
>  
> @@ -884,10 +891,13 @@ static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
>  					READ_ONCE(task->parent) == current;
>  			put_task_struct(task);
>  		}
> -		return ptrace_active;
> +		if (!ptrace_active)
> +			return false;
> +		break;
>  	default:
> -		return true;
> +		break;
>  	}
> +	return security_mem_foll_force(file->f_cred, priv->opened_by_owner) == 0;
>  }
>  
>  static ssize_t mem_rw(struct file *file, char __user *buf,
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index 65c9609ec207..12f84a1e6fab 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -36,6 +36,7 @@ LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
>  LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
>  	 unsigned int mode)
>  LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
> +LSM_HOOK(int, 0, mem_foll_force, const struct cred *subject, bool opened_by_owner)
>  LSM_HOOK(int, 0, capget, const struct task_struct *target, kernel_cap_t *effective,
>  	 kernel_cap_t *inheritable, kernel_cap_t *permitted)
>  LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 153e9043058f..e8bc2e644241 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -338,6 +338,7 @@ int security_binder_transfer_file(const struct cred *from,
>  				  const struct cred *to, const struct file *file);
>  int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
>  int security_ptrace_traceme(struct task_struct *parent);
> +int security_mem_foll_force(const struct cred *subject, bool opened_by_owner);
>  int security_capget(const struct task_struct *target,
>  		    kernel_cap_t *effective,
>  		    kernel_cap_t *inheritable,
> @@ -676,6 +677,12 @@ static inline int security_ptrace_traceme(struct task_struct *parent)
>  	return cap_ptrace_traceme(parent);
>  }
>  
> +static inline int security_mem_foll_force(const struct cred *subject,
> +					  bool opened_by_owner)
> +{
> +	return 0;
> +}
> +
>  static inline int security_capget(const struct task_struct *target,
>  				   kernel_cap_t *effective,
>  				   kernel_cap_t *inheritable,
> diff --git a/security/security.c b/security/security.c
> index 71aea8fdf014..2cde1efdb7a6 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -595,6 +595,31 @@ int security_ptrace_traceme(struct task_struct *parent)
>  	return call_int_hook(ptrace_traceme, parent);
>  }
>  
> +/**
> + * security_mem_foll_force() - Check if FOLL_FORCE is allowed
> + * @subject: credentials using which /proc/$pid/mem was opened
> + * @opened_by_owner: whether checks on open() were bypassed because the opener
> + *                   has the same MM as the target
> + *
> + * Check if FOLL_FORCE is allowed for accessing process memory through
> + * /proc/$pid/mem. opened_by_owner signals whether the opener's MM was the same
> + * as the target MM, meaning the security_ptrace_access_check() hook was
> + * bypassed on open().
> + * (Current current->mm does not matter for this; for example, if write() is
> + * called on an FD that was received from another process which obtained it with
> + * open("/proc/self/mem"), @opened_by_owner is still true.)
> + *
> + * Note that this hook is only designed to be useful in the opened_by_owner
> + * case, where the subject credentials effectively also describe the object.

Given this, would it make more sense to call the hook something
like `security_mem_foll_force_self()` and only call it in the
opened_by_owner==true case?

I only suggest it because it seems to lower the cognitive load
when looking at this code, so it might make it easier to maintain.

> + *
> + * Return: Returns 0 if permission is granted.
> + */
> +int security_mem_foll_force(const struct cred *subject,
> +					    bool opened_by_owner)
> +{
> +	return call_int_hook(mem_foll_force, subject, opened_by_owner);
> +}
> +
>  /**
>   * security_capget() - Get the capability sets for a process
>   * @target: target process
> 
> -- 
> 2.55.0.1003.g10538fe699-goog
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-15 17:04   ` Serge Hallyn (AMD)
@ 2026-09-15 17:24     ` Paul Moore
  2026-09-15 17:54       ` Serge E. Hallyn
  2026-09-15 17:44     ` Jann Horn
  1 sibling, 1 reply; 17+ messages in thread
From: Paul Moore @ 2026-09-15 17:24 UTC (permalink / raw)
  To: Serge Hallyn (AMD)
  Cc: Jann Horn, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro,
	Christian Brauner, Jan Kara, linux-fsdevel, linux-security-module,
	Ondrej Mosnacek, selinux, Andrew Morton, Liam R. Howlett,
	Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm

On Tue, Sep 15, 2026 at 1:04 PM Serge Hallyn (AMD) <sergeh@kernel.org> wrote:
> On Mon, Sep 07, 2026 at 11:00:17PM +0200, Jann Horn wrote:
> > 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.
> >
> > Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> > Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> > Signed-off-by: Jann Horn <jannh@google.com>
> > ---
> >  fs/proc/base.c                | 14 ++++++++++++--
> >  include/linux/lsm_hook_defs.h |  1 +
> >  include/linux/security.h      |  7 +++++++
> >  security/security.c           | 25 +++++++++++++++++++++++++
> >  4 files changed, 45 insertions(+), 2 deletions(-)
> >
> > diff --git a/fs/proc/base.c b/fs/proc/base.c
> > index bec6197329dc..295b21203c7f 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;
> >
> > @@ -884,10 +891,13 @@ static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
> >                                       READ_ONCE(task->parent) == current;
> >                       put_task_struct(task);
> >               }
> > -             return ptrace_active;
> > +             if (!ptrace_active)
> > +                     return false;
> > +             break;
> >       default:
> > -             return true;
> > +             break;
> >       }
> > +     return security_mem_foll_force(file->f_cred, priv->opened_by_owner) == 0;
> >  }
> >
> >  static ssize_t mem_rw(struct file *file, char __user *buf,
> > diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> > index 65c9609ec207..12f84a1e6fab 100644
> > --- a/include/linux/lsm_hook_defs.h
> > +++ b/include/linux/lsm_hook_defs.h
> > @@ -36,6 +36,7 @@ LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
> >  LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
> >        unsigned int mode)
> >  LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
> > +LSM_HOOK(int, 0, mem_foll_force, const struct cred *subject, bool opened_by_owner)
> >  LSM_HOOK(int, 0, capget, const struct task_struct *target, kernel_cap_t *effective,
> >        kernel_cap_t *inheritable, kernel_cap_t *permitted)
> >  LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
> > diff --git a/include/linux/security.h b/include/linux/security.h
> > index 153e9043058f..e8bc2e644241 100644
> > --- a/include/linux/security.h
> > +++ b/include/linux/security.h
> > @@ -338,6 +338,7 @@ int security_binder_transfer_file(const struct cred *from,
> >                                 const struct cred *to, const struct file *file);
> >  int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
> >  int security_ptrace_traceme(struct task_struct *parent);
> > +int security_mem_foll_force(const struct cred *subject, bool opened_by_owner);
> >  int security_capget(const struct task_struct *target,
> >                   kernel_cap_t *effective,
> >                   kernel_cap_t *inheritable,
> > @@ -676,6 +677,12 @@ static inline int security_ptrace_traceme(struct task_struct *parent)
> >       return cap_ptrace_traceme(parent);
> >  }
> >
> > +static inline int security_mem_foll_force(const struct cred *subject,
> > +                                       bool opened_by_owner)
> > +{
> > +     return 0;
> > +}
> > +
> >  static inline int security_capget(const struct task_struct *target,
> >                                  kernel_cap_t *effective,
> >                                  kernel_cap_t *inheritable,
> > diff --git a/security/security.c b/security/security.c
> > index 71aea8fdf014..2cde1efdb7a6 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -595,6 +595,31 @@ int security_ptrace_traceme(struct task_struct *parent)
> >       return call_int_hook(ptrace_traceme, parent);
> >  }
> >
> > +/**
> > + * security_mem_foll_force() - Check if FOLL_FORCE is allowed
> > + * @subject: credentials using which /proc/$pid/mem was opened
> > + * @opened_by_owner: whether checks on open() were bypassed because the opener
> > + *                   has the same MM as the target
> > + *
> > + * Check if FOLL_FORCE is allowed for accessing process memory through
> > + * /proc/$pid/mem. opened_by_owner signals whether the opener's MM was the same
> > + * as the target MM, meaning the security_ptrace_access_check() hook was
> > + * bypassed on open().
> > + * (Current current->mm does not matter for this; for example, if write() is
> > + * called on an FD that was received from another process which obtained it with
> > + * open("/proc/self/mem"), @opened_by_owner is still true.)
> > + *
> > + * Note that this hook is only designed to be useful in the opened_by_owner
> > + * case, where the subject credentials effectively also describe the object.
>
> Given this, would it make more sense to call the hook something
> like `security_mem_foll_force_self()` and only call it in the
> opened_by_owner==true case?

Sometimes we can't avoid it, but in general I'd like to see us avoid
calling LSM hooks conditionally, I'd much prefer the hook
implementation (either at the LSM framework level or the individual
LSM) do the conditional check.  See my comments on the previous
revision.

> I only suggest it because it seems to lower the cognitive load
> when looking at this code, so it might make it easier to maintain.

-- 
paul-moore.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-15 17:04   ` Serge Hallyn (AMD)
  2026-09-15 17:24     ` Paul Moore
@ 2026-09-15 17:44     ` Jann Horn
  2026-09-15 17:53       ` Serge E. Hallyn
  1 sibling, 1 reply; 17+ messages in thread
From: Jann Horn @ 2026-09-15 17:44 UTC (permalink / raw)
  To: Serge Hallyn (AMD)
  Cc: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro,
	Christian Brauner, Jan Kara, linux-fsdevel, linux-security-module,
	Ondrej Mosnacek, selinux, Andrew Morton, Liam R. Howlett,
	Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm

On Tue, Sep 15, 2026 at 7:04 PM Serge Hallyn (AMD) <sergeh@kernel.org> wrote:
> On Mon, Sep 07, 2026 at 11:00:17PM +0200, Jann Horn wrote:
> > +/**
> > + * security_mem_foll_force() - Check if FOLL_FORCE is allowed
> > + * @subject: credentials using which /proc/$pid/mem was opened
> > + * @opened_by_owner: whether checks on open() were bypassed because the opener
> > + *                   has the same MM as the target
> > + *
> > + * Check if FOLL_FORCE is allowed for accessing process memory through
> > + * /proc/$pid/mem. opened_by_owner signals whether the opener's MM was the same
> > + * as the target MM, meaning the security_ptrace_access_check() hook was
> > + * bypassed on open().
> > + * (Current current->mm does not matter for this; for example, if write() is
> > + * called on an FD that was received from another process which obtained it with
> > + * open("/proc/self/mem"), @opened_by_owner is still true.)
> > + *
> > + * Note that this hook is only designed to be useful in the opened_by_owner
> > + * case, where the subject credentials effectively also describe the object.
>
> Given this, would it make more sense to call the hook something
> like `security_mem_foll_force_self()` and only call it in the
> opened_by_owner==true case?
>
> I only suggest it because it seems to lower the cognitive load
> when looking at this code, so it might make it easier to maintain.

I agree with you, and that is what I did in the previous versions. :P

But given that two relevant maintainers disagreed with me, I changed
it in this version.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-15 17:44     ` Jann Horn
@ 2026-09-15 17:53       ` Serge E. Hallyn
  0 siblings, 0 replies; 17+ messages in thread
From: Serge E. Hallyn @ 2026-09-15 17:53 UTC (permalink / raw)
  To: Jann Horn
  Cc: Serge Hallyn (AMD), Paul Moore, James Morris, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro,
	Christian Brauner, Jan Kara, linux-fsdevel, linux-security-module,
	Ondrej Mosnacek, selinux, Andrew Morton, Liam R. Howlett,
	Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm

On Tue, Sep 15, 2026 at 07:44:23PM +0200, Jann Horn wrote:
> On Tue, Sep 15, 2026 at 7:04 PM Serge Hallyn (AMD) <sergeh@kernel.org> wrote:
> > On Mon, Sep 07, 2026 at 11:00:17PM +0200, Jann Horn wrote:
> > > +/**
> > > + * security_mem_foll_force() - Check if FOLL_FORCE is allowed
> > > + * @subject: credentials using which /proc/$pid/mem was opened
> > > + * @opened_by_owner: whether checks on open() were bypassed because the opener
> > > + *                   has the same MM as the target
> > > + *
> > > + * Check if FOLL_FORCE is allowed for accessing process memory through
> > > + * /proc/$pid/mem. opened_by_owner signals whether the opener's MM was the same
> > > + * as the target MM, meaning the security_ptrace_access_check() hook was
> > > + * bypassed on open().
> > > + * (Current current->mm does not matter for this; for example, if write() is
> > > + * called on an FD that was received from another process which obtained it with
> > > + * open("/proc/self/mem"), @opened_by_owner is still true.)
> > > + *
> > > + * Note that this hook is only designed to be useful in the opened_by_owner
> > > + * case, where the subject credentials effectively also describe the object.
> >
> > Given this, would it make more sense to call the hook something
> > like `security_mem_foll_force_self()` and only call it in the
> > opened_by_owner==true case?
> >
> > I only suggest it because it seems to lower the cognitive load
> > when looking at this code, so it might make it easier to maintain.
> 
> I agree with you, and that is what I did in the previous versions. :P
> 
> But given that two relevant maintainers disagreed with me, I changed
> it in this version.

Ah, I'm sorry.  Didn't mean to dispute a prior decision.

Sounds good.

IIUC it's already in a tree, but for the record

Reviewed-by: Serge Hallyn <sergeh@kernel.org>

thanks,
-serge

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS)
  2026-09-15 17:24     ` Paul Moore
@ 2026-09-15 17:54       ` Serge E. Hallyn
  0 siblings, 0 replies; 17+ messages in thread
From: Serge E. Hallyn @ 2026-09-15 17:54 UTC (permalink / raw)
  To: Paul Moore
  Cc: Serge Hallyn (AMD), Jann Horn, James Morris, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro,
	Christian Brauner, Jan Kara, linux-fsdevel, linux-security-module,
	Ondrej Mosnacek, selinux, Andrew Morton, Liam R. Howlett,
	Lorenzo Stoakes, Vlastimil Babka, Pedro Falcato,
	David Hildenbrand, linux-mm

On Tue, Sep 15, 2026 at 01:24:27PM -0400, Paul Moore wrote:
> On Tue, Sep 15, 2026 at 1:04 PM Serge Hallyn (AMD) <sergeh@kernel.org> wrote:
> > On Mon, Sep 07, 2026 at 11:00:17PM +0200, Jann Horn wrote:
> > > 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.
> > >
> > > Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> > > Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> > > Signed-off-by: Jann Horn <jannh@google.com>
> > > ---
> > >  fs/proc/base.c                | 14 ++++++++++++--
> > >  include/linux/lsm_hook_defs.h |  1 +
> > >  include/linux/security.h      |  7 +++++++
> > >  security/security.c           | 25 +++++++++++++++++++++++++
> > >  4 files changed, 45 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/fs/proc/base.c b/fs/proc/base.c
> > > index bec6197329dc..295b21203c7f 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;
> > >
> > > @@ -884,10 +891,13 @@ static bool proc_mem_foll_force(struct file *file, struct mm_struct *mm)
> > >                                       READ_ONCE(task->parent) == current;
> > >                       put_task_struct(task);
> > >               }
> > > -             return ptrace_active;
> > > +             if (!ptrace_active)
> > > +                     return false;
> > > +             break;
> > >       default:
> > > -             return true;
> > > +             break;
> > >       }
> > > +     return security_mem_foll_force(file->f_cred, priv->opened_by_owner) == 0;
> > >  }
> > >
> > >  static ssize_t mem_rw(struct file *file, char __user *buf,
> > > diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> > > index 65c9609ec207..12f84a1e6fab 100644
> > > --- a/include/linux/lsm_hook_defs.h
> > > +++ b/include/linux/lsm_hook_defs.h
> > > @@ -36,6 +36,7 @@ LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
> > >  LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
> > >        unsigned int mode)
> > >  LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
> > > +LSM_HOOK(int, 0, mem_foll_force, const struct cred *subject, bool opened_by_owner)
> > >  LSM_HOOK(int, 0, capget, const struct task_struct *target, kernel_cap_t *effective,
> > >        kernel_cap_t *inheritable, kernel_cap_t *permitted)
> > >  LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
> > > diff --git a/include/linux/security.h b/include/linux/security.h
> > > index 153e9043058f..e8bc2e644241 100644
> > > --- a/include/linux/security.h
> > > +++ b/include/linux/security.h
> > > @@ -338,6 +338,7 @@ int security_binder_transfer_file(const struct cred *from,
> > >                                 const struct cred *to, const struct file *file);
> > >  int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
> > >  int security_ptrace_traceme(struct task_struct *parent);
> > > +int security_mem_foll_force(const struct cred *subject, bool opened_by_owner);
> > >  int security_capget(const struct task_struct *target,
> > >                   kernel_cap_t *effective,
> > >                   kernel_cap_t *inheritable,
> > > @@ -676,6 +677,12 @@ static inline int security_ptrace_traceme(struct task_struct *parent)
> > >       return cap_ptrace_traceme(parent);
> > >  }
> > >
> > > +static inline int security_mem_foll_force(const struct cred *subject,
> > > +                                       bool opened_by_owner)
> > > +{
> > > +     return 0;
> > > +}
> > > +
> > >  static inline int security_capget(const struct task_struct *target,
> > >                                  kernel_cap_t *effective,
> > >                                  kernel_cap_t *inheritable,
> > > diff --git a/security/security.c b/security/security.c
> > > index 71aea8fdf014..2cde1efdb7a6 100644
> > > --- a/security/security.c
> > > +++ b/security/security.c
> > > @@ -595,6 +595,31 @@ int security_ptrace_traceme(struct task_struct *parent)
> > >       return call_int_hook(ptrace_traceme, parent);
> > >  }
> > >
> > > +/**
> > > + * security_mem_foll_force() - Check if FOLL_FORCE is allowed
> > > + * @subject: credentials using which /proc/$pid/mem was opened
> > > + * @opened_by_owner: whether checks on open() were bypassed because the opener
> > > + *                   has the same MM as the target
> > > + *
> > > + * Check if FOLL_FORCE is allowed for accessing process memory through
> > > + * /proc/$pid/mem. opened_by_owner signals whether the opener's MM was the same
> > > + * as the target MM, meaning the security_ptrace_access_check() hook was
> > > + * bypassed on open().
> > > + * (Current current->mm does not matter for this; for example, if write() is
> > > + * called on an FD that was received from another process which obtained it with
> > > + * open("/proc/self/mem"), @opened_by_owner is still true.)
> > > + *
> > > + * Note that this hook is only designed to be useful in the opened_by_owner
> > > + * case, where the subject credentials effectively also describe the object.
> >
> > Given this, would it make more sense to call the hook something
> > like `security_mem_foll_force_self()` and only call it in the
> > opened_by_owner==true case?
> 
> Sometimes we can't avoid it, but in general I'd like to see us avoid
> calling LSM hooks conditionally, I'd much prefer the hook
> implementation (either at the LSM framework level or the individual
> LSM) do the conditional check.  See my comments on the previous
> revision.

Ah.  Sorry, I had missed that.  +1.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
  2026-09-14 17:05   ` Paul Moore
@ 2026-09-16  9:01     ` Christian Brauner
  2026-09-16 14:33       ` Jann Horn
  2026-09-21  2:33       ` Paul Moore
  0 siblings, 2 replies; 17+ messages in thread
From: Christian Brauner @ 2026-09-16  9:01 UTC (permalink / raw)
  To: Paul Moore
  Cc: Jann Horn, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro, Jan Kara,
	linux-fsdevel, linux-security-module, Ondrej Mosnacek, selinux,
	Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka,
	Pedro Falcato, David Hildenbrand, linux-mm

On Mon, Sep 14, 2026 at 01:05:39PM -0400, Paul Moore wrote:
> On Thu, Sep 10, 2026 at 3:48 AM Christian Brauner <brauner@kernel.org> wrote:
> > On Mon, 07 Sep 2026 23:00:15 +0200, Jann Horn wrote:
> > > proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
> > >
> > > The goal of this series is to let SELinux prevent the use of FOLL_FORCE
> > > when a process writes into /proc/self/mem and the system is configured
> > > with PROC_MEM_FORCE_ALWAYS (which used to be the default behavior, and
> > > is still used by current Android devices).
> > >
> > > [...]
> >
> > Let's move this on a shared branch that both the vfs tree and the lsm
> > tree pull. That's the standard way of handling dependencies between two
> > subsystems that want to route code that touches both of them. Branch is
> > stable.
> 
> For some reason (/me looks at gmail) your email never hit my inbox, I
> only noticed it now while reviewing Jann's patchset.

Oh weird?

> Christian, can you explain why you merged LSM code that I haven't
> ACK'd?  We've talked about this in the past and I think I've been
> fairly clear about it; I wouldn't merge VFS code without an
> ACK/Reviewed-by/etc. from you or Al, I've been expecting the same
> consideration from you.
> 
> While I don't have a problem with a topic branch for this, can you
> also explain why this topic branch should live in the VFS tree?  There
> are more changes under security/ than fs/, and unless I've misread
> Jann's cover letter, this entire patchset is focused around enabling
> LSM/SELinux controls and not necessarily anything really new from a
> VFS perspective.

See the reply on the bpf thread. I think this just a misunderstanding. I
was under the impression that with minor tweaks v2 was already acked by
lsm people.

In general, if a subsystems hooks into the vfs layer and filesystems
that are directly maintained by the vfs layer then the vfs tree provides
the shared branch. This is not at all specific to security. It's just
more pronounced for notify or security because hooks are accepted
layering violations that place constraints on how vfs code itself is
organized.

I think this is just a misunderstanding that would've been easy to
clarify without the kerfuffle.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
  2026-09-16  9:01     ` Christian Brauner
@ 2026-09-16 14:33       ` Jann Horn
  2026-09-21  2:33       ` Paul Moore
  1 sibling, 0 replies; 17+ messages in thread
From: Jann Horn @ 2026-09-16 14:33 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Paul Moore, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro, Jan Kara,
	linux-fsdevel, linux-security-module, Ondrej Mosnacek, selinux,
	Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka,
	Pedro Falcato, David Hildenbrand, linux-mm

On Wed, Sep 16, 2026 at 11:02 AM Christian Brauner <brauner@kernel.org> wrote:
> On Mon, Sep 14, 2026 at 01:05:39PM -0400, Paul Moore wrote:
> > For some reason (/me looks at gmail) your email never hit my inbox, I
> > only noticed it now while reviewing Jann's patchset.
>
> Oh weird?

(Same for me, I don't see that mail from Christian on this thread.)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
  2026-09-16  9:01     ` Christian Brauner
  2026-09-16 14:33       ` Jann Horn
@ 2026-09-21  2:33       ` Paul Moore
  1 sibling, 0 replies; 17+ messages in thread
From: Paul Moore @ 2026-09-21  2:33 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Jann Horn, James Morris, Serge E. Hallyn, Stephen Smalley,
	Jeff Xu, Thiébaud Weksteen, Alexander Viro, Jan Kara,
	linux-fsdevel, linux-security-module, Ondrej Mosnacek, selinux,
	Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Vlastimil Babka,
	Pedro Falcato, David Hildenbrand, linux-mm

On Wed, Sep 16, 2026 at 5:01 AM Christian Brauner <brauner@kernel.org> wrote:
> On Mon, Sep 14, 2026 at 01:05:39PM -0400, Paul Moore wrote:
> > On Thu, Sep 10, 2026 at 3:48 AM Christian Brauner <brauner@kernel.org> wrote:
> > > On Mon, 07 Sep 2026 23:00:15 +0200, Jann Horn wrote:
> > > > proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem
> > > >
> > > > The goal of this series is to let SELinux prevent the use of FOLL_FORCE
> > > > when a process writes into /proc/self/mem and the system is configured
> > > > with PROC_MEM_FORCE_ALWAYS (which used to be the default behavior, and
> > > > is still used by current Android devices).
> > > >
> > > > [...]
> > >
> > > Let's move this on a shared branch that both the vfs tree and the lsm
> > > tree pull. That's the standard way of handling dependencies between two
> > > subsystems that want to route code that touches both of them. Branch is
> > > stable.
> >
> > For some reason (/me looks at gmail) your email never hit my inbox, I
> > only noticed it now while reviewing Jann's patchset.
>
> Oh weird?
>
> > Christian, can you explain why you merged LSM code that I haven't
> > ACK'd?  We've talked about this in the past and I think I've been
> > fairly clear about it; I wouldn't merge VFS code without an
> > ACK/Reviewed-by/etc. from you or Al, I've been expecting the same
> > consideration from you.
> >
> > While I don't have a problem with a topic branch for this, can you
> > also explain why this topic branch should live in the VFS tree?  There
> > are more changes under security/ than fs/, and unless I've misread
> > Jann's cover letter, this entire patchset is focused around enabling
> > LSM/SELinux controls and not necessarily anything really new from a
> > VFS perspective.
>
> See the reply on the bpf thread. I think this just a misunderstanding. I
> was under the impression that with minor tweaks v2 was already acked by
> lsm people.
>
> In general, if a subsystems hooks into the vfs layer and filesystems
> that are directly maintained by the vfs layer then the vfs tree provides
> the shared branch. This is not at all specific to security. It's just
> more pronounced for notify or security because hooks are accepted
> layering violations that place constraints on how vfs code itself is
> organized.
>
> I think this is just a misunderstanding that would've been easy to
> clarify without the kerfuffle.

I'll follow up in the other thread, but I will just say here that had
this been the first time there had been an issue like this, it would
have likely been different.  However, from my perspective this was
continuing a pattern of behavior I had previously talked with you
about changing, and as a result I was much more annoyed having to
revisit the same process issues.

-- 
paul-moore.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-09-21  2:33 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07 21:00 [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Jann Horn
2026-09-07 21:00 ` [PATCH v3 1/3] proc: refactor /proc/$pid/mem to use struct as private_data Jann Horn
2026-09-07 21:07   ` sashiko-bot
2026-09-07 21:00 ` [PATCH v3 2/3] proc: query LSMs for introspective mem access (if PROC_MEM_FORCE_ALWAYS) Jann Horn
2026-09-07 21:09   ` sashiko-bot
2026-09-15 17:04   ` Serge Hallyn (AMD)
2026-09-15 17:24     ` Paul Moore
2026-09-15 17:54       ` Serge E. Hallyn
2026-09-15 17:44     ` Jann Horn
2026-09-15 17:53       ` Serge E. Hallyn
2026-09-07 21:00 ` [PATCH v3 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection Jann Horn
2026-09-07 21:08   ` sashiko-bot
2026-09-10  7:48 ` [PATCH v3 0/3] proc,security,selinux: let SELinux block FOLL_FORCE for /proc/self/mem Christian Brauner
2026-09-14 17:05   ` Paul Moore
2026-09-16  9:01     ` Christian Brauner
2026-09-16 14:33       ` Jann Horn
2026-09-21  2:33       ` Paul Moore

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.