Re: [PATCH 3/3] selinux: require EXECMEM or PTRACE for FOLL_FORCE introspection
"Lorenzo Stoakes (ARM)" <[email protected]>
| Newsgroups | org.kernel.vger.linux-security-module,org.kernel.vger.linux-fsdevel,org.kernel.vger.selinux,org.kvack.linux-mm |
|---|---|
| Message-ID | <aoie0oyojqdHciKM@gremlin> |
On Tue, Aug 18, 2026 at 09:51:07PM +0200, Jann Horn wrote: > 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 one > of: > > - EXECMEM (like for other methods of creating anonymous executable pages) > - PTRACE (like when using /proc/$pid/mem of another process) > > This closes a hole in EXECMEM 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 > permission to overwrite executable code via /proc/self/mem, which made it > possible to load and run shellcode with a kernel exploit. > > Signed-off-by: Jann Horn <[email protected]> > --- > security/selinux/hooks.c | 27 +++++++++++++++++++++++++++ > 1 file changed, 27 insertions(+) > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 18dd28b2bb13..905137c47321 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -2157,6 +2157,32 @@ static int selinux_ptrace_traceme(struct task_struct *parent) > SECCLASS_PROCESS, PROCESS__PTRACE, NULL); > } > > +/* > + * Decide whether it should be possible to read non-readable VMAs and write > + * non-writable VMAs via /proc/self/mem. > + * This only applies to systems configured with PROC_MEM_FORCE_ALWAYS, and only > + * triggers on accesses that are not visible to selinux_ptrace_access_check(). It might be worth mentioning may_access_mm() here, and obviously propagate the suggested name change introspection -> opened_by_owner or fd_from_order maybe even? > + * > + * 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_introspect_mem_foll_force(const struct cred *subject) > +{ > + struct av_decision avd; > + int rc; > + u32 sid = cred_sid(subject); > + > + /* Allow if the process is generally allowed to have executable anonymous memory. */ > + rc = avc_has_perm_noaudit(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, 0, &avd); But does it make sense for the shared-by-fd case? In that case you're now updating execmem for another process's memory right? > + > + /* Also allow if selinux_ptrace_access_check() would allow it. */ > + if (rc) > + rc = avc_has_perm_noaudit(sid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, 0, &avd); > + return rc; > +} > + > static int selinux_capget(const struct task_struct *target, kernel_cap_t *effective, > kernel_cap_t *inheritable, kernel_cap_t *permitted) > { > @@ -7558,6 +7584,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(introspect_mem_foll_force, selinux_introspect_mem_foll_force), > LSM_HOOK_INIT(capget, selinux_capget), > LSM_HOOK_INIT(capset, selinux_capset), > LSM_HOOK_INIT(capable, selinux_capable), > > -- > 2.55.0.737.g08866a6d13-goog > -- Cheers, Lorenzo