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 *),

Reply via email to