[PATCH v2 3/3] selinux: require PROCESS__PTRACE for FOLL_FORCE introspection
Jann Horn <[email protected]>
| Newsgroups | gmane.linux.file-systems,gmane.linux.kernel.lsm,gmane.linux.kernel.mm |
|---|---|
| Message-ID | <[email protected]> |
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. Signed-off-by: Jann Horn <[email protected]> --- security/selinux/hooks.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 18dd28b2bb13..2c2c60e9e0fe 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2157,6 +2157,27 @@ 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() + * 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_opened_by_owner(const struct cred *subject) +{ + struct av_decision avd; + u32 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 +7579,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_opened_by_owner, selinux_mem_foll_force_opened_by_owner), LSM_HOOK_INIT(capget, selinux_capget), LSM_HOOK_INIT(capset, selinux_capset), LSM_HOOK_INIT(capable, selinux_capable), -- 2.55.0.860.g4b6b3295ed-goog