Module Name: src Committed By: ryo Date: Fri Nov 26 08:06:12 UTC 2021
Modified Files: src/sys/compat/common: kern_sig_16.c src/sys/compat/netbsd32: netbsd32_compat_16.c src/sys/kern: kern_exec.c src/sys/sys: exec.h Log Message: Fix anonymous memory object leak for sigcode. - Repeating "modload compat_linux && /emul/linux/bin/ls && modunload compat_linux" will reproduce this problem. - It cause in exec_sigcode_map(), anon-object for sigcode was created at first exec, but it remained even after exec_remove. - Fixed that the anon-object for sigcode is created at exec_add(), and the anon-object reference is removed at exec_remove(). - sigobject_lock is no longer needed since it is locked by exec_lock. - The compat_16 module rewrites the e_sigcode entry in emul_netbsd directly and does not use exec_add()/exec_remove(), so it needs to call sigcode_alloc()/sigcode_free() on its own. To generate a diff of this commit: cvs rdiff -u -r1.6 -r1.7 src/sys/compat/common/kern_sig_16.c cvs rdiff -u -r1.3 -r1.4 src/sys/compat/netbsd32/netbsd32_compat_16.c cvs rdiff -u -r1.513 -r1.514 src/sys/kern/kern_exec.c cvs rdiff -u -r1.160 -r1.161 src/sys/sys/exec.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/compat/common/kern_sig_16.c diff -u src/sys/compat/common/kern_sig_16.c:1.6 src/sys/compat/common/kern_sig_16.c:1.7 --- src/sys/compat/common/kern_sig_16.c:1.6 Sat May 23 23:42:41 2020 +++ src/sys/compat/common/kern_sig_16.c Fri Nov 26 08:06:11 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_sig_16.c,v 1.6 2020/05/23 23:42:41 ad Exp $ */ +/* $NetBSD: kern_sig_16.c,v 1.7 2021/11/26 08:06:11 ryo Exp $ */ /*- * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -66,13 +66,14 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_sig_16.c,v 1.6 2020/05/23 23:42:41 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_sig_16.c,v 1.7 2021/11/26 08:06:11 ryo Exp $"); #if defined(_KERNEL_OPT) #include "opt_compat_netbsd.h" #endif #include <sys/param.h> +#include <sys/exec.h> #include <sys/kernel.h> #include <sys/rwlock.h> #include <sys/signalvar.h> @@ -92,8 +93,6 @@ __KERNEL_RCSID(0, "$NetBSD: kern_sig_16. #include <compat/common/compat_mod.h> -extern krwlock_t exec_lock; - #if !defined(__amd64__) || defined(COMPAT_NETBSD32) #define COMPAT_SIGCONTEXT extern char sigcode[], esigcode[]; @@ -155,11 +154,17 @@ kern_sig_16_init(void) emul_netbsd.e_sigcode = sigcode; emul_netbsd.e_esigcode = esigcode; emul_netbsd.e_sigobject = &emul_netbsd_object; + error = exec_sigcode_alloc(&emul_netbsd); + if (error) { + emul_netbsd.e_sigcode = NULL; + emul_netbsd.e_esigcode = NULL; + emul_netbsd.e_sigobject = NULL; + } rw_exit(&exec_lock); MODULE_HOOK_SET(sendsig_sigcontext_16_hook, sendsig_sigcontext); #endif - return 0; + return error; } int @@ -195,9 +200,7 @@ kern_sig_16_fini(void) * is reference counted so will die eventually. */ rw_enter(&exec_lock, RW_WRITER); - if (emul_netbsd_object != NULL) { - (*emul_netbsd_object->pgops->pgo_detach)(emul_netbsd_object); - } + exec_sigcode_free(&emul_netbsd); emul_netbsd_object = NULL; emul_netbsd.e_sigcode = NULL; emul_netbsd.e_esigcode = NULL; Index: src/sys/compat/netbsd32/netbsd32_compat_16.c diff -u src/sys/compat/netbsd32/netbsd32_compat_16.c:1.3 src/sys/compat/netbsd32/netbsd32_compat_16.c:1.4 --- src/sys/compat/netbsd32/netbsd32_compat_16.c:1.3 Sun Dec 15 16:48:26 2019 +++ src/sys/compat/netbsd32/netbsd32_compat_16.c Fri Nov 26 08:06:11 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: netbsd32_compat_16.c,v 1.3 2019/12/15 16:48:26 tsutsui Exp $ */ +/* $NetBSD: netbsd32_compat_16.c,v 1.4 2021/11/26 08:06:11 ryo Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -29,12 +29,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: netbsd32_compat_16.c,v 1.3 2019/12/15 16:48:26 tsutsui Exp $"); +__KERNEL_RCSID(0, "$NetBSD: netbsd32_compat_16.c,v 1.4 2021/11/26 08:06:11 ryo Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/dirent.h> +#include <sys/exec.h> #include <sys/lwp.h> #include <sys/syscallargs.h> #include <sys/syscallvar.h> @@ -51,19 +52,33 @@ MODULE(MODULE_CLASS_EXEC, compat_netbsd3 static int compat_netbsd32_16_modcmd(modcmd_t cmd, void *arg) { + int error; switch (cmd) { case MODULE_CMD_INIT: + rw_enter(&exec_lock, RW_WRITER); emul_netbsd32.e_sigcode = netbsd32_sigcode; emul_netbsd32.e_esigcode = netbsd32_esigcode; emul_netbsd32.e_sigobject = &emul_netbsd32_object; + error = exec_sigcode_alloc(&emul_netbsd); + if (error) { + emul_netbsd32.e_sigcode = NULL; + emul_netbsd32.e_esigcode = NULL; + emul_netbsd32.e_sigobject = NULL; + } + rw_exit(&exec_lock); + if (error) + return error; netbsd32_machdep_md_16_init(); return 0; case MODULE_CMD_FINI: + rw_enter(&exec_lock, RW_WRITER); + exec_sigcode_free(&emul_netbsd); emul_netbsd32.e_sigcode = NULL; emul_netbsd32.e_esigcode = NULL; emul_netbsd32.e_sigobject = NULL; + rw_exit(&exec_lock); netbsd32_machdep_md_16_fini(); return 0; Index: src/sys/kern/kern_exec.c diff -u src/sys/kern/kern_exec.c:1.513 src/sys/kern/kern_exec.c:1.514 --- src/sys/kern/kern_exec.c:1.513 Thu Nov 25 10:31:50 2021 +++ src/sys/kern/kern_exec.c Fri Nov 26 08:06:12 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exec.c,v 1.513 2021/11/25 10:31:50 ryo Exp $ */ +/* $NetBSD: kern_exec.c,v 1.514 2021/11/26 08:06:12 ryo Exp $ */ /*- * Copyright (c) 2008, 2019, 2020 The NetBSD Foundation, Inc. @@ -62,7 +62,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.513 2021/11/25 10:31:50 ryo Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.514 2021/11/26 08:06:12 ryo Exp $"); #include "opt_exec.h" #include "opt_execfmt.h" @@ -250,8 +250,6 @@ struct emul emul_netbsd = { */ krwlock_t exec_lock __cacheline_aligned; -static kmutex_t sigobject_lock __cacheline_aligned; - /* * Data used between a loadvm and execve part of an "exec" operation */ @@ -1815,7 +1813,7 @@ int exec_add(struct execsw *esp, int count) { struct exec_entry *it; - int i; + int i, error = 0; if (count == 0) { return 0; @@ -1840,8 +1838,23 @@ exec_add(struct execsw *esp, int count) for (i = 0; i < count; i++) { it = kmem_alloc(sizeof(*it), KM_SLEEP); it->ex_sw = &esp[i]; + error = exec_sigcode_alloc(it->ex_sw->es_emul); + if (error != 0) { + kmem_free(it, sizeof(*it)); + break; + } LIST_INSERT_HEAD(&ex_head, it, ex_list); } + /* If even one fails, remove them all back. */ + if (error != 0) { + for (i--; i >= 0; i--) { + it = LIST_FIRST(&ex_head); + LIST_REMOVE(it, ex_list); + exec_sigcode_free(it->ex_sw->es_emul); + kmem_free(it, sizeof(*it)); + } + return error; + } /* update execsw[] */ exec_init(0); @@ -1886,6 +1899,7 @@ exec_remove(struct execsw *esp, int coun next = LIST_NEXT(it, ex_list); if (it->ex_sw == &esp[i]) { LIST_REMOVE(it, ex_list); + exec_sigcode_free(it->ex_sw->es_emul); kmem_free(it, sizeof(*it)); break; } @@ -1919,7 +1933,6 @@ exec_init(int init_boot) vaddr_t vmin = 0, vmax; rw_init(&exec_lock); - mutex_init(&sigobject_lock, MUTEX_DEFAULT, IPL_NONE); exec_map = uvm_km_suballoc(kernel_map, &vmin, &vmax, maxexec*NCARGS, VM_MAP_PAGEABLE, false, NULL); pool_init(&exec_pool, NCARGS, 0, 0, PR_NOALIGN|PR_NOTOUCH, @@ -1986,22 +1999,25 @@ exec_init(int init_boot) return 0; } -static int -exec_sigcode_map(struct proc *p, const struct emul *e) +int +exec_sigcode_alloc(const struct emul *e) { vaddr_t va; vsize_t sz; int error; struct uvm_object *uobj; - sz = (vaddr_t)e->e_esigcode - (vaddr_t)e->e_sigcode; + KASSERT(rw_lock_held(&exec_lock)); - if (e->e_sigobject == NULL || sz == 0) { + if (e == NULL || e->e_sigobject == NULL) + return 0; + + sz = (vaddr_t)e->e_esigcode - (vaddr_t)e->e_sigcode; + if (sz == 0) return 0; - } /* - * If we don't have a sigobject for this emulation, create one. + * Create a sigobject for this emulation. * * sigobject is an anonymous memory object (just like SYSV shared * memory) that we keep a permanent reference to and that we map @@ -2011,33 +2027,69 @@ exec_sigcode_map(struct proc *p, const s * We map it with PROT_READ|PROT_EXEC into the process just * the way sys_mmap() would map it. */ - - uobj = *e->e_sigobject; - if (uobj == NULL) { - mutex_enter(&sigobject_lock); - if ((uobj = *e->e_sigobject) == NULL) { - uobj = uao_create(sz, 0); - (*uobj->pgops->pgo_reference)(uobj); - va = vm_map_min(kernel_map); - if ((error = uvm_map(kernel_map, &va, round_page(sz), - uobj, 0, 0, - UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, - UVM_INH_SHARE, UVM_ADV_RANDOM, 0)))) { - printf("kernel mapping failed %d\n", error); - (*uobj->pgops->pgo_detach)(uobj); - mutex_exit(&sigobject_lock); - return error; - } - memcpy((void *)va, e->e_sigcode, sz); + if (*e->e_sigobject == NULL) { + uobj = uao_create(sz, 0); + (*uobj->pgops->pgo_reference)(uobj); + va = vm_map_min(kernel_map); + if ((error = uvm_map(kernel_map, &va, round_page(sz), + uobj, 0, 0, + UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, + UVM_INH_SHARE, UVM_ADV_RANDOM, 0)))) { + printf("sigcode kernel mapping failed %d\n", error); + (*uobj->pgops->pgo_detach)(uobj); + return error; + } + memcpy((void *)va, e->e_sigcode, sz); #ifdef PMAP_NEED_PROCWR - pmap_procwr(&proc0, va, sz); + pmap_procwr(&proc0, va, sz); #endif - uvm_unmap(kernel_map, va, va + round_page(sz)); - *e->e_sigobject = uobj; - } - mutex_exit(&sigobject_lock); + uvm_unmap(kernel_map, va, va + round_page(sz)); + *e->e_sigobject = uobj; + KASSERT(uobj->uo_refs == 1); + } else { + /* if already created, reference++ */ + uobj = *e->e_sigobject; + (*uobj->pgops->pgo_reference)(uobj); } + return 0; +} + +void +exec_sigcode_free(const struct emul *e) +{ + struct uvm_object *uobj; + + KASSERT(rw_lock_held(&exec_lock)); + + if (e == NULL || e->e_sigobject == NULL) + return; + + uobj = *e->e_sigobject; + if (uobj == NULL) + return; + + if (uobj->uo_refs == 1) + *e->e_sigobject = NULL; /* I'm the last person to reference. */ + (*uobj->pgops->pgo_detach)(uobj); +} + +static int +exec_sigcode_map(struct proc *p, const struct emul *e) +{ + vaddr_t va; + vsize_t sz; + int error; + struct uvm_object *uobj; + + sz = (vaddr_t)e->e_esigcode - (vaddr_t)e->e_sigcode; + if (e->e_sigobject == NULL || sz == 0) + return 0; + + uobj = *e->e_sigobject; + if (uobj == NULL) + return 0; + /* Just a hint to uvm_map where to put it. */ va = e->e_vm_default_addr(p, (vaddr_t)p->p_vmspace->vm_daddr, round_page(sz), p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN); Index: src/sys/sys/exec.h diff -u src/sys/sys/exec.h:1.160 src/sys/sys/exec.h:1.161 --- src/sys/sys/exec.h:1.160 Wed Nov 10 16:19:48 2021 +++ src/sys/sys/exec.h Fri Nov 26 08:06:12 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: exec.h,v 1.160 2021/11/10 16:19:48 msaitoh Exp $ */ +/* $NetBSD: exec.h,v 1.161 2021/11/26 08:06:12 ryo Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -289,6 +289,8 @@ int cpu_coredump32(struct lwp *, struct int exec_add(struct execsw *, int); int exec_remove(struct execsw *, int); +int exec_sigcode_alloc(const struct emul *); +void exec_sigcode_free(const struct emul *); void new_vmcmd(struct exec_vmcmd_set *, int (*)(struct lwp *, struct exec_vmcmd *),