Module Name: src Committed By: sborrill Date: Mon Feb 20 21:54:57 UTC 2012
Modified Files: src/include [netbsd-6]: spawn.h src/sys/kern [netbsd-6]: kern_exec.c src/sys/uvm [netbsd-6]: uvm_glue.c src/tests/lib/libc/gen/posix_spawn [netbsd-6]: t_fileactions.c Log Message: Pull up the following revisions(s) (requested by martin in ticket #14): include/spawn.h: revision 1.2 sys/kern/kern_exec.c: revision 1.341 sys/uvm/uvm_glue.c: revision 1.157 tests/lib/libc/gen/posix_spawn/t_fileactions.c: revision 1.3 posix_spawn: fix kernel bug when passing empty fileactions (PR kern/46038) and add a test case for this. Fix potential race condition, doublefreeing of memory and memory leaks in error cases. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.1.2.1 src/include/spawn.h cvs rdiff -u -r1.339 -r1.339.2.1 src/sys/kern/kern_exec.c cvs rdiff -u -r1.156 -r1.156.2.1 src/sys/uvm/uvm_glue.c cvs rdiff -u -r1.2 -r1.2.2.1 \ src/tests/lib/libc/gen/posix_spawn/t_fileactions.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/include/spawn.h diff -u src/include/spawn.h:1.1 src/include/spawn.h:1.1.2.1 --- src/include/spawn.h:1.1 Sat Feb 11 23:31:24 2012 +++ src/include/spawn.h Mon Feb 20 21:54:55 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: spawn.h,v 1.1 2012/02/11 23:31:24 martin Exp $ */ +/* $NetBSD: spawn.h,v 1.1.2.1 2012/02/20 21:54:55 sborrill Exp $ */ /*- * Copyright (c) 2008 Ed Schouten <e...@freebsd.org> @@ -33,6 +33,7 @@ #include <sys/spawn.h> +__BEGIN_DECLS /* * Spawn routines * Index: src/sys/kern/kern_exec.c diff -u src/sys/kern/kern_exec.c:1.339 src/sys/kern/kern_exec.c:1.339.2.1 --- src/sys/kern/kern_exec.c:1.339 Sun Feb 12 20:11:03 2012 +++ src/sys/kern/kern_exec.c Mon Feb 20 21:54:56 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exec.c,v 1.339 2012/02/12 20:11:03 martin Exp $ */ +/* $NetBSD: kern_exec.c,v 1.339.2.1 2012/02/20 21:54:56 sborrill Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -59,7 +59,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.339 2012/02/12 20:11:03 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.339.2.1 2012/02/20 21:54:56 sborrill Exp $"); #include "opt_exec.h" #include "opt_ktrace.h" @@ -1351,8 +1351,7 @@ execve_runproc(struct lwp *l, struct exe ktremul(); /* Allow new references from the debugger/procfs. */ - if (!proc_is_new) - rw_exit(&p->p_reflock); + rw_exit(&p->p_reflock); rw_exit(&exec_lock); mutex_enter(proc_lock); @@ -1777,6 +1776,20 @@ spawn_return(void *arg) size_t i; const struct posix_spawn_file_actions_entry *fae; register_t retval; + bool have_reflock; + + /* + * The following actions may block, so we need a temporary + * vmspace - borrow the kernel one + */ + KPREEMPT_DISABLE(l); + l->l_proc->p_vmspace = proc0.p_vmspace; + pmap_activate(l); + KPREEMPT_ENABLE(l); + + /* don't allow debugger access yet */ + rw_enter(&l->l_proc->p_reflock, RW_WRITER); + have_reflock = true; error = 0; /* handle posix_spawn_file_actions */ @@ -1891,18 +1904,17 @@ spawn_return(void *arg) } } - if (spawn_data->sed_actions != NULL) { - for (i = 0; i < spawn_data->sed_actions_len; i++) { - fae = &spawn_data->sed_actions[i]; - if (fae->fae_action == FAE_OPEN) - kmem_free(fae->fae_path, - strlen(fae->fae_path)+1); - } - } + /* stop using kernel vmspace */ + KPREEMPT_DISABLE(l); + pmap_deactivate(l); + l->l_proc->p_vmspace = NULL; + KPREEMPT_ENABLE(l); + /* now do the real exec */ rw_enter(&exec_lock, RW_READER); error = execve_runproc(l, &spawn_data->sed_exec); + have_reflock = false; if (error == EJUSTRETURN) error = 0; else if (error) @@ -1920,13 +1932,15 @@ spawn_return(void *arg) return; report_error: - if (spawn_data->sed_actions != NULL) { - for (i = 0; i < spawn_data->sed_actions_len; i++) { - fae = &spawn_data->sed_actions[i]; - if (fae->fae_action == FAE_OPEN) - kmem_free(fae->fae_path, - strlen(fae->fae_path)+1); - } + if (have_reflock) + rw_exit(&l->l_proc->p_reflock); + + /* stop using kernel vmspace (if we haven't already) */ + if (l->l_proc->p_vmspace) { + KPREEMPT_DISABLE(l); + pmap_deactivate(l); + l->l_proc->p_vmspace = NULL; + KPREEMPT_ENABLE(l); } /* @@ -2007,27 +2021,31 @@ sys_posix_spawn(struct lwp *l1, const st fa->fae = NULL; goto error_exit; } - ufa = fa->fae; - fa->fae = kmem_alloc(fa->len * - sizeof(struct posix_spawn_file_actions_entry), KM_SLEEP); - error = copyin(ufa, fa->fae, - fa->len * sizeof(struct posix_spawn_file_actions_entry)); - if (error) - goto error_exit; - for (i = 0; i < fa->len; i++) { - if (fa->fae[i].fae_action == FAE_OPEN) { - char buf[PATH_MAX]; - error = copyinstr(fa->fae[i].fae_path, buf, - sizeof(buf), NULL); - if (error) - break; - fa->fae[i].fae_path = kmem_alloc(strlen(buf)+1, - KM_SLEEP); - if (fa->fae[i].fae_path == NULL) { - error = ENOMEM; - break; + if (fa->len) { + ufa = fa->fae; + fa->fae = kmem_alloc(fa->len * + sizeof(struct posix_spawn_file_actions_entry), + KM_SLEEP); + error = copyin(ufa, fa->fae, + fa->len * + sizeof(struct posix_spawn_file_actions_entry)); + if (error) + goto error_exit; + for (i = 0; i < fa->len; i++) { + if (fa->fae[i].fae_action == FAE_OPEN) { + char buf[PATH_MAX]; + error = copyinstr(fa->fae[i].fae_path, + buf, sizeof(buf), NULL); + if (error) + break; + fa->fae[i].fae_path = kmem_alloc( + strlen(buf)+1, KM_SLEEP); + if (fa->fae[i].fae_path == NULL) { + error = ENOMEM; + break; + } + strcpy(fa->fae[i].fae_path, buf); } - strcpy(fa->fae[i].fae_path, buf); } } } @@ -2183,8 +2201,10 @@ sys_posix_spawn(struct lwp *l1, const st * Prepare remaining parts of spawn data */ if (fa != NULL) { - spawn_data->sed_actions_len = fa->len; - spawn_data->sed_actions = fa->fae; + if (fa->len) { + spawn_data->sed_actions_len = fa->len; + spawn_data->sed_actions = fa->fae; + } kmem_free(fa, sizeof(*fa)); fa = NULL; } @@ -2246,7 +2266,6 @@ sys_posix_spawn(struct lwp *l1, const st #ifdef __HAVE_SYSCALL_INTERN (*p2->p_emul->e_syscall_intern)(p2); #endif - rw_exit(&p1->p_reflock); /* * Make child runnable, set start time, and add to run queue except @@ -2271,15 +2290,25 @@ sys_posix_spawn(struct lwp *l1, const st mutex_exit(&spawn_data->sed_mtx_child); error = spawn_data->sed_error; + rw_exit(&p1->p_reflock); rw_exit(&exec_lock); have_exec_lock = false; - if (spawn_data->sed_actions != NULL) + if (spawn_data->sed_actions != NULL) { + for (i = 0; i < spawn_data->sed_actions_len; i++) { + if (spawn_data->sed_actions[i].fae_action == FAE_OPEN) + kmem_free(spawn_data->sed_actions[i].fae_path, + strlen(spawn_data->sed_actions[i].fae_path) + +1); + } kmem_free(spawn_data->sed_actions, - spawn_data->sed_actions_len * sizeof(*spawn_data->sed_actions)); + spawn_data->sed_actions_len + * sizeof(*spawn_data->sed_actions)); + } if (spawn_data->sed_attrs != NULL) - kmem_free(spawn_data->sed_attrs, sizeof(*spawn_data->sed_attrs)); + kmem_free(spawn_data->sed_attrs, + sizeof(*spawn_data->sed_attrs)); cv_destroy(&spawn_data->sed_cv_child_ready); mutex_destroy(&spawn_data->sed_mtx_child); @@ -2297,8 +2326,15 @@ sys_posix_spawn(struct lwp *l1, const st rw_exit(&exec_lock); if (fa != NULL) { - if (fa->fae != NULL) + if (fa->fae != NULL) { + for (i = 0; i < fa->len; i++) { + if (fa->fae->fae_action == FAE_OPEN + && fa->fae->fae_path != NULL) + kmem_free(fa->fae->fae_path, + strlen(fa->fae->fae_path)+1); + } kmem_free(fa->fae, fa->len * sizeof(*fa->fae)); + } kmem_free(fa, sizeof(*fa)); } Index: src/sys/uvm/uvm_glue.c diff -u src/sys/uvm/uvm_glue.c:1.156 src/sys/uvm/uvm_glue.c:1.156.2.1 --- src/sys/uvm/uvm_glue.c:1.156 Sun Feb 12 11:18:04 2012 +++ src/sys/uvm/uvm_glue.c Mon Feb 20 21:54:57 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: uvm_glue.c,v 1.156 2012/02/12 11:18:04 martin Exp $ */ +/* $NetBSD: uvm_glue.c,v 1.156.2.1 2012/02/20 21:54:57 sborrill Exp $ */ /* * Copyright (c) 1997 Charles D. Cranor and Washington University. @@ -62,7 +62,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uvm_glue.c,v 1.156 2012/02/12 11:18:04 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uvm_glue.c,v 1.156.2.1 2012/02/20 21:54:57 sborrill Exp $"); #include "opt_kgdb.h" #include "opt_kstack.h" @@ -416,14 +416,13 @@ uvm_proc_exit(struct proc *p) KASSERT(p == l->l_proc); ovm = p->p_vmspace; - if (__predict_false(ovm == NULL)) - return; /* * borrow proc0's address space. */ KPREEMPT_DISABLE(l); - pmap_deactivate(l); + if (__predict_true(ovm != NULL)) + pmap_deactivate(l); p->p_vmspace = proc0.p_vmspace; pmap_activate(l); KPREEMPT_ENABLE(l); Index: src/tests/lib/libc/gen/posix_spawn/t_fileactions.c diff -u src/tests/lib/libc/gen/posix_spawn/t_fileactions.c:1.2 src/tests/lib/libc/gen/posix_spawn/t_fileactions.c:1.2.2.1 --- src/tests/lib/libc/gen/posix_spawn/t_fileactions.c:1.2 Tue Feb 14 00:13:54 2012 +++ src/tests/lib/libc/gen/posix_spawn/t_fileactions.c Mon Feb 20 21:54:57 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: t_fileactions.c,v 1.2 2012/02/14 00:13:54 martin Exp $ */ +/* $NetBSD: t_fileactions.c,v 1.2.2.1 2012/02/20 21:54:57 sborrill Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -279,12 +279,57 @@ ATF_TC_BODY(t_spawn_fileactions, tc) ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); } +ATF_TC(t_spawn_empty_fileactions); + +ATF_TC_HEAD(t_spawn_empty_fileactions, tc) +{ + atf_tc_set_md_var(tc, "descr", + "posix_spawn with empty fileactions (PR kern/46038)"); + atf_tc_set_md_var(tc, "require.progs", "/bin/cat"); +} + +ATF_TC_BODY(t_spawn_empty_fileactions, tc) +{ + int status, err; + pid_t pid; + char * const args[2] = { __UNCONST("cat"), NULL }; + posix_spawn_file_actions_t fa; + size_t insize, outsize; + + /* + * try a "cat < testfile > checkfile", but set up stdin/stdout + * already in the parent and pass empty file actions to the child. + */ + make_testfile(TESTFILE); + unlink(CHECKFILE); + + freopen(TESTFILE, "r", stdin); + freopen(CHECKFILE, "w", stdout); + + posix_spawn_file_actions_init(&fa); + err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL); + posix_spawn_file_actions_destroy(&fa); + + ATF_REQUIRE(err == 0); + + /* ok, wait for the child to finish */ + waitpid(pid, &status, 0); + ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); + + /* now check that input and output have the same size */ + insize = filesize(TESTFILE); + outsize = filesize(CHECKFILE); + ATF_REQUIRE(insize == strlen(TESTCONTENT)); + ATF_REQUIRE(insize == outsize); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, t_spawn_fileactions); ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent); ATF_TP_ADD_TC(tp, t_spawn_reopen); ATF_TP_ADD_TC(tp, t_spawn_openmode); + ATF_TP_ADD_TC(tp, t_spawn_empty_fileactions); return atf_no_error(); }