Module Name: src Committed By: rmind Date: Wed May 2 23:33:11 UTC 2012
Modified Files: src/sys/compat/netbsd32: netbsd32_execve.c src/sys/kern: kern_exec.c src/sys/sys: exec.h Log Message: Revert posix_spawn() clean up for now, there are some bugs. To generate a diff of this commit: cvs rdiff -u -r1.35 -r1.36 src/sys/compat/netbsd32/netbsd32_execve.c cvs rdiff -u -r1.351 -r1.352 src/sys/kern/kern_exec.c cvs rdiff -u -r1.136 -r1.137 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/netbsd32/netbsd32_execve.c diff -u src/sys/compat/netbsd32/netbsd32_execve.c:1.35 src/sys/compat/netbsd32/netbsd32_execve.c:1.36 --- src/sys/compat/netbsd32/netbsd32_execve.c:1.35 Mon Apr 30 21:19:58 2012 +++ src/sys/compat/netbsd32/netbsd32_execve.c Wed May 2 23:33:11 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: netbsd32_execve.c,v 1.35 2012/04/30 21:19:58 rmind Exp $ */ +/* $NetBSD: netbsd32_execve.c,v 1.36 2012/05/02 23:33:11 rmind Exp $ */ /* * Copyright (c) 1998, 2001 Matthew R. Green @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.35 2012/04/30 21:19:58 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.36 2012/05/02 23:33:11 rmind Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -178,6 +178,7 @@ netbsd32_posix_spawn(struct lwp *l, struct posix_spawn_file_actions *fa = NULL; struct posix_spawnattr *sa = NULL; pid_t pid; + bool child_ok = false; error = check_posix_spawn(l); if (error) { @@ -190,7 +191,7 @@ netbsd32_posix_spawn(struct lwp *l, error = netbsd32_posix_spawn_fa_alloc(&fa, SCARG_P32(uap, file_actions)); if (error) - goto fail; + goto error_exit; } /* copyin posix_spawnattr struct */ @@ -198,17 +199,17 @@ netbsd32_posix_spawn(struct lwp *l, sa = kmem_alloc(sizeof(*sa), KM_SLEEP); error = copyin(SCARG_P32(uap, attrp), sa, sizeof(*sa)); if (error) - goto fail; + goto error_exit; } /* * Do the spawn */ - error = do_posix_spawn(l, &pid, SCARG_P32(uap, path), fa, + error = do_posix_spawn(l, &pid, &child_ok, SCARG_P32(uap, path), fa, sa, SCARG_P32(uap, argv), SCARG_P32(uap, envp), netbsd32_execve_fetch_element); if (error) - goto fail; + goto error_exit; if (error == 0 && SCARG_P32(uap, pid) != NULL) error = copyout(&pid, SCARG_P32(uap, pid), sizeof(pid)); @@ -216,14 +217,17 @@ netbsd32_posix_spawn(struct lwp *l, *retval = error; return 0; -fail: - (void)chgproccnt(kauth_cred_getuid(l->l_cred), -1); - atomic_dec_uint(&nprocs); - - if (sa) - kmem_free(sa, sizeof(*sa)); - if (fa) - posix_spawn_fa_free(fa, fa->len); + error_exit: + if (!child_ok) { + (void)chgproccnt(kauth_cred_getuid(l->l_cred), -1); + atomic_dec_uint(&nprocs); + + if (sa) + kmem_free(sa, sizeof(*sa)); + if (fa) + posix_spawn_fa_free(fa, fa->len); + } + *retval = error; return 0; } Index: src/sys/kern/kern_exec.c diff -u src/sys/kern/kern_exec.c:1.351 src/sys/kern/kern_exec.c:1.352 --- src/sys/kern/kern_exec.c:1.351 Mon Apr 30 21:19:58 2012 +++ src/sys/kern/kern_exec.c Wed May 2 23:33:11 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exec.c,v 1.351 2012/04/30 21:19:58 rmind Exp $ */ +/* $NetBSD: kern_exec.c,v 1.352 2012/05/02 23:33:11 rmind 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.351 2012/04/30 21:19:58 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.352 2012/05/02 23:33:11 rmind Exp $"); #include "opt_exec.h" #include "opt_ktrace.h" @@ -211,8 +211,9 @@ struct emul emul_netbsd = { * Exec lock. Used to control access to execsw[] structures. * This must not be static so that netbsd32 can access it, too. */ -krwlock_t exec_lock __cacheline_aligned; -static kmutex_t sigobject_lock __cacheline_aligned; +krwlock_t exec_lock; + +static kmutex_t sigobject_lock; /* * Data used between a loadvm and execve part of an "exec" operation @@ -295,6 +296,7 @@ static struct pool_allocator exec_palloc * exec header unmodified. */ int +/*ARGSUSED*/ check_exec(struct lwp *l, struct exec_package *epp, struct pathbuf *pb) { int error, i; @@ -500,7 +502,7 @@ sys_execve(struct lwp *l, const struct s SCARG(uap, envp), execve_fetch_element); } -int +int sys_fexecve(struct lwp *l, const struct sys_fexecve_args *uap, register_t *retval) { @@ -562,43 +564,6 @@ exec_autoload(void) #endif } -static void -execve_free_vmspace(struct execve_data *ed) -{ - - /* - * Free the vmspace-creation commands and release their references. - */ - kill_vmcmds(&ed->ed_pack.ep_vmcmds); - - /* Kill any opened file descriptor, if necessary. */ - if (ed->ed_pack.ep_flags & EXEC_HASFD) { - ed->ed_pack.ep_flags &= ~EXEC_HASFD; - fd_close(ed->ed_pack.ep_fd); - } - - /* Close and put the executed file. */ - vn_lock(ed->ed_pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); - VOP_CLOSE(ed->ed_pack.ep_vp, FREAD, curlwp->l_cred); - vput(ed->ed_pack.ep_vp); - pool_put(&exec_pool, ed->ed_argp); -} - -static void -execve_free_data(struct execve_data *ed) -{ - - kmem_free(ed->ed_pack.ep_hdr, ed->ed_pack.ep_hdrlen); - if (ed->ed_pack.ep_emul_root != NULL) - vrele(ed->ed_pack.ep_emul_root); - if (ed->ed_pack.ep_interp != NULL) - vrele(ed->ed_pack.ep_interp); - - pathbuf_stringcopy_put(ed->ed_pathbuf, ed->ed_pathstring); - pathbuf_destroy(ed->ed_pathbuf); - PNBUF_PUT(ed->ed_resolvedpathbuf); -} - static int execve_loadvm(struct lwp *l, const char *path, char * const *args, char * const *envs, execve_fetch_element_t fetch_element, @@ -610,12 +575,11 @@ execve_loadvm(struct lwp *l, const char size_t i, len; struct exec_fakearg *tmpfap; u_int modgen; - bool freevm; KASSERT(data != NULL); p = l->l_proc; - modgen = 0; + modgen = 0; SDT_PROBE(proc,,,exec, path, 0, 0, 0, 0); @@ -635,7 +599,6 @@ execve_loadvm(struct lwp *l, const char * to call exec in order to do something useful. */ retry: - freevm = false; if (p->p_flag & PK_SUGID) { if (kauth_authorize_process(l->l_cred, KAUTH_PROCESS_RLIMIT, p, KAUTH_ARG(KAUTH_REQ_PROCESS_RLIMIT_BYPASS), @@ -647,6 +610,14 @@ execve_loadvm(struct lwp *l, const char } /* + * Drain existing references and forbid new ones. The process + * should be left alone until we're done here. This is necessary + * to avoid race conditions - e.g. in ptrace() - that might allow + * a local user to illicitly obtain elevated privileges. + */ + rw_enter(&p->p_reflock, RW_WRITER); + + /* * Init the namei data to point the file user's program name. * This is done here rather than in check_exec(), so that it's * possible to override this settings if any of makecmd/probe @@ -655,7 +626,9 @@ execve_loadvm(struct lwp *l, const char */ error = pathbuf_copyin(path, &data->ed_pathbuf); if (error) { - return error; + DPRINTF(("%s: pathbuf_copyin path @%p %d\n", __func__, + path, error)); + goto clrflg; } data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf); @@ -684,13 +657,6 @@ execve_loadvm(struct lwp *l, const char data->ed_pack.ep_esch = NULL; data->ed_pack.ep_pax_flags = 0; - /* - * Drain existing references and forbid new ones. The process - * should be left alone until we're done here. This is necessary - * to avoid race conditions - e.g. in ptrace() - that might allow - * a local user to illicitly obtain elevated privileges. - */ - rw_enter(&p->p_reflock, RW_WRITER); rw_enter(&exec_lock, RW_READER); /* see if we can run it. */ @@ -699,9 +665,8 @@ execve_loadvm(struct lwp *l, const char DPRINTF(("%s: check exec failed %d\n", __func__, error)); } - goto bad; + goto freehdr; } - freevm = true; /* XXX -- THE FOLLOWING SECTION NEEDS MAJOR CLEANUP */ @@ -837,14 +802,35 @@ execve_loadvm(struct lwp *l, const char return 0; -bad: + bad: + /* free the vmspace-creation commands, and release their references */ + kill_vmcmds(&data->ed_pack.ep_vmcmds); + /* kill any opened file descriptor, if necessary */ + if (data->ed_pack.ep_flags & EXEC_HASFD) { + data->ed_pack.ep_flags &= ~EXEC_HASFD; + fd_close(data->ed_pack.ep_fd); + } + /* close and put the exec'd file */ + vn_lock(data->ed_pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); + VOP_CLOSE(data->ed_pack.ep_vp, FREAD, l->l_cred); + vput(data->ed_pack.ep_vp); + pool_put(&exec_pool, data->ed_argp); + + freehdr: + kmem_free(data->ed_pack.ep_hdr, data->ed_pack.ep_hdrlen); + if (data->ed_pack.ep_emul_root != NULL) + vrele(data->ed_pack.ep_emul_root); + if (data->ed_pack.ep_interp != NULL) + vrele(data->ed_pack.ep_interp); + rw_exit(&exec_lock); - rw_exit(&p->p_reflock); - if (freevm) { - execve_free_vmspace(data); - } - execve_free_data(data); + pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring); + pathbuf_destroy(data->ed_pathbuf); + PNBUF_PUT(data->ed_resolvedpathbuf); + + clrflg: + rw_exit(&p->p_reflock); if (modgen != module_gen && error == ENOEXEC) { modgen = module_gen; @@ -856,11 +842,41 @@ bad: return error; } +static void +execve_free_data(struct execve_data *data) +{ + + /* free the vmspace-creation commands, and release their references */ + kill_vmcmds(&data->ed_pack.ep_vmcmds); + /* kill any opened file descriptor, if necessary */ + if (data->ed_pack.ep_flags & EXEC_HASFD) { + data->ed_pack.ep_flags &= ~EXEC_HASFD; + fd_close(data->ed_pack.ep_fd); + } + + /* close and put the exec'd file */ + vn_lock(data->ed_pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); + VOP_CLOSE(data->ed_pack.ep_vp, FREAD, curlwp->l_cred); + vput(data->ed_pack.ep_vp); + pool_put(&exec_pool, data->ed_argp); + + kmem_free(data->ed_pack.ep_hdr, data->ed_pack.ep_hdrlen); + if (data->ed_pack.ep_emul_root != NULL) + vrele(data->ed_pack.ep_emul_root); + if (data->ed_pack.ep_interp != NULL) + vrele(data->ed_pack.ep_interp); + + pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring); + pathbuf_destroy(data->ed_pathbuf); + PNBUF_PUT(data->ed_resolvedpathbuf); +} + static int execve_runproc(struct lwp *l, struct execve_data * restrict data, bool no_local_exec_lock, bool is_spawn) { - struct proc *p = l->l_proc; + int error = 0; + struct proc *p; size_t i; char *stack, *dp; const char *commandname; @@ -870,7 +886,6 @@ execve_runproc(struct lwp *l, struct exe struct vmspace *vm; ksiginfo_t ksi; ksiginfoq_t kq; - int error = 0; /* * In case of a posix_spawn operation, the child doing the exec @@ -878,9 +893,13 @@ execve_runproc(struct lwp *l, struct exe * will do this instead. */ KASSERT(no_local_exec_lock || rw_lock_held(&exec_lock)); - KASSERT(!no_local_exec_lock || is_spawn); - KASSERT(rw_lock_held(&p->p_reflock)); KASSERT(data != NULL); + if (data == NULL) + return (EINVAL); + + p = l->l_proc; + if (no_local_exec_lock) + KASSERT(is_spawn); base_vcp = NULL; @@ -1385,17 +1404,25 @@ execve_runproc(struct lwp *l, struct exe if (!no_local_exec_lock) rw_exit(&exec_lock); + pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring); + pathbuf_destroy(data->ed_pathbuf); + PNBUF_PUT(data->ed_resolvedpathbuf); + /* * the old process doesn't exist anymore. exit gracefully. * get rid of the (new) address space we have created, if any, get rid * of our namei data and vnode, and exit noting failure */ uvm_deallocate(&vm->vm_map, VM_MIN_ADDRESS, - VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); + VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); exec_free_emul_arg(&data->ed_pack); pool_put(&exec_pool, data->ed_argp); - execve_free_data(data); + kmem_free(data->ed_pack.ep_hdr, data->ed_pack.ep_hdrlen); + if (data->ed_pack.ep_emul_root != NULL) + vrele(data->ed_pack.ep_emul_root); + if (data->ed_pack.ep_interp != NULL) + vrele(data->ed_pack.ep_interp); /* Acquire the sched-state mutex (exit1() will release it). */ if (!is_spawn) { @@ -1790,12 +1817,6 @@ spawn_return(void *arg) bool have_reflock; bool parent_is_waiting = true; - error = 0; - - /* don't allow debugger access yet */ - rw_enter(&l->l_proc->p_reflock, RW_WRITER); - have_reflock = true; - /* * Check if we can release parent early. * We either need to have no sed_attrs, or sed_attrs does not @@ -1815,6 +1836,11 @@ spawn_return(void *arg) mutex_exit(&spawn_data->sed_mtx_child); } + /* 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 */ if (spawn_data->sed_actions != NULL) { for (i = 0; i < spawn_data->sed_actions->len; i++) { @@ -1927,13 +1953,14 @@ spawn_return(void *arg) } } - /* Now perform the real exec (will drop the locks). */ + /* now do the real exec */ error = execve_runproc(l, &spawn_data->sed_exec, parent_is_waiting, true); have_reflock = false; - if (error && error != EJUSTRETURN) { + if (error == EJUSTRETURN) + error = 0; + else if (error) goto report_error; - } if (parent_is_waiting) { mutex_enter(&spawn_data->sed_mtx_child); @@ -1959,7 +1986,6 @@ spawn_return(void *arg) * so release/free both here. */ rw_exit(&l->l_proc->p_reflock); - execve_free_vmspace(&spawn_data->sed_exec); execve_free_data(&spawn_data->sed_exec); } @@ -1976,20 +2002,18 @@ spawn_return(void *arg) /* release our refcount on the data */ spawn_exec_data_release(spawn_data); + /* done, exit */ + mutex_enter(l->l_proc->p_lock); /* - * POSIX explicitly requires an exit code of 127 if we report + * Posix explicitly asks for an exit code of 127 if we report * errors from the child process - so, unfortunately, there * is no way to report a more exact error code. * A NetBSD specific workaround is POSIX_SPAWN_RETURNERROR as * flag bit in the attrp argument to posix_spawn(2), see above. */ - mutex_enter(l->l_proc->p_lock); exit1(l, W_EXITCODE(127, 0)); } -/* - * posix_spawn_fa_free: free file action structures. - */ void posix_spawn_fa_free(struct posix_spawn_file_actions *fa, size_t len) { @@ -2005,22 +2029,16 @@ posix_spawn_fa_free(struct posix_spawn_f kmem_free(fa, sizeof(*fa)); } -/* - * posix_spawn_fa_alloc: allocation file action structures and copy-in from - * the user space. Note: we re-use the same structure in the kernel; care - * is taken to reset userspace pointers. - */ static int posix_spawn_fa_alloc(struct posix_spawn_file_actions **fap, const struct posix_spawn_file_actions *ufa) { struct posix_spawn_file_actions *fa; struct posix_spawn_file_actions_entry *fae; - size_t i = 0, totalsize; char *pbuf = NULL; int error; + size_t i = 0; - /* Allocate structure containing file action entries. */ fa = kmem_alloc(sizeof(*fa), KM_SLEEP); error = copyin(ufa, fa, sizeof(*fa)); if (error) { @@ -2028,34 +2046,30 @@ posix_spawn_fa_alloc(struct posix_spawn_ fa->len = 0; goto out; } + if (fa->len == 0) { kmem_free(fa, sizeof(*fa)); return 0; } fa->size = fa->len; - totalsize = fa->len * sizeof(*fae); - - /* Allocate memory block needed for file action entries. */ - fae = kmem_alloc(totalsize, KM_SLEEP); - error = copyin(fa->fae, fae, totalsize); - fa->fae = fae; + size_t fal = fa->len * sizeof(*fae); + fae = fa->fae; + fa->fae = kmem_alloc(fal, KM_SLEEP); + error = copyin(fae, fa->fae, fal); if (error) goto out; - /* Allocate a path and copy-in each entry. */ pbuf = PNBUF_GET(); for (; i < fa->len; i++) { - size_t len; - fae = &fa->fae[i]; if (fae->fae_action != FAE_OPEN) continue; - error = copyinstr(fae->fae_path, pbuf, MAXPATHLEN, &len); + error = copyinstr(fae->fae_path, pbuf, MAXPATHLEN, &fal); if (error) goto out; - fae->fae_path = kmem_alloc(len, KM_SLEEP); - memcpy(fae->fae_path, pbuf, len); + fae->fae_path = kmem_alloc(fal, KM_SLEEP); + memcpy(fae->fae_path, pbuf, fal); } PNBUF_PUT(pbuf); @@ -2111,16 +2125,20 @@ check_posix_spawn(struct lwp *l1) } int -do_posix_spawn(struct lwp *l1, pid_t *pid_res, const char *path, - struct posix_spawn_file_actions *fa, struct posix_spawnattr *sa, - char *const *argv, char *const *envp, execve_fetch_element_t fetch) +do_posix_spawn(struct lwp *l1, pid_t *pid_res, bool *child_ok, const char *path, + struct posix_spawn_file_actions *fa, + struct posix_spawnattr *sa, + char *const *argv, char *const *envp, + execve_fetch_element_t fetch) { + struct proc *p1, *p2; struct lwp *l2; int error; struct spawn_exec_data *spawn_data; vaddr_t uaddr; pid_t pid; + bool have_exec_lock = false; p1 = l1->l_proc; @@ -2129,6 +2147,20 @@ do_posix_spawn(struct lwp *l1, pid_t *pi spawn_data->sed_refcnt = 1; /* only parent so far */ cv_init(&spawn_data->sed_cv_child_ready, "pspawn"); mutex_init(&spawn_data->sed_mtx_child, MUTEX_DEFAULT, IPL_NONE); + mutex_enter(&spawn_data->sed_mtx_child); + + /* + * Do the first part of the exec now, collect state + * in spawn_data. + */ + error = execve_loadvm(l1, path, argv, + envp, fetch, &spawn_data->sed_exec); + if (error == EJUSTRETURN) + error = 0; + else if (error) + goto error_exit; + + have_exec_lock = true; /* * Allocate virtual address space for the U-area now, while it @@ -2137,26 +2169,10 @@ do_posix_spawn(struct lwp *l1, pid_t *pi */ uaddr = uvm_uarea_alloc(); if (__predict_false(uaddr == 0)) { - spawn_exec_data_release(spawn_data); - return ENOMEM; - } - - /* - * Do the first part of the exec now, collect state in spawn_data. - */ - mutex_enter(&spawn_data->sed_mtx_child); - error = execve_loadvm(l1, path, argv, - envp, fetch, &spawn_data->sed_exec); - if (error && error != EJUSTRETURN) { - uvm_uarea_free(uaddr); - mutex_exit(&spawn_data->sed_mtx_child); - spawn_exec_data_release(spawn_data); - return error; + error = ENOMEM; + goto error_exit; } - KASSERT(rw_lock_held(&p1->p_reflock)); - KASSERT(rw_lock_held(&exec_lock)); - error = 0; - + /* * Allocate new proc. Borrow proc0 vmspace for it, we will * replace it with its own before returning to userland @@ -2270,13 +2286,14 @@ do_posix_spawn(struct lwp *l1, pid_t *pi cpu_proc_fork(p1, p2); /* - * Prepare remaining parts of spawn data. Child gets a reference. + * Prepare remaining parts of spawn data */ spawn_data->sed_actions = fa; spawn_data->sed_attrs = sa; + spawn_data->sed_parent = p1; - spawn_data->sed_refcnt++; + /* create LWP */ lwp_create(l1, p2, uaddr, 0, NULL, 0, spawn_return, spawn_data, &l2, l1->l_class); l2->l_ctxlink = NULL; /* reset ucontext link */ @@ -2305,6 +2322,8 @@ do_posix_spawn(struct lwp *l1, pid_t *pi kauth_cred_free(ocred); } + *child_ok = true; + spawn_data->sed_refcnt = 2; /* child gets it as well */ #if 0 l2->l_nopreempt = 1; /* start it non-preemptable */ #endif @@ -2351,14 +2370,25 @@ do_posix_spawn(struct lwp *l1, pid_t *pi cv_wait(&spawn_data->sed_cv_child_ready, &spawn_data->sed_mtx_child); error = spawn_data->sed_error; mutex_exit(&spawn_data->sed_mtx_child); + spawn_exec_data_release(spawn_data); rw_exit(&p1->p_reflock); rw_exit(&exec_lock); - - spawn_exec_data_release(spawn_data); + have_exec_lock = false; *pid_res = pid; return error; + + error_exit: + if (have_exec_lock) { + execve_free_data(&spawn_data->sed_exec); + rw_exit(&p1->p_reflock); + rw_exit(&exec_lock); + } + mutex_exit(&spawn_data->sed_mtx_child); + spawn_exec_data_release(spawn_data); + + return error; } int @@ -2378,6 +2408,7 @@ sys_posix_spawn(struct lwp *l1, const st struct posix_spawn_file_actions *fa = NULL; struct posix_spawnattr *sa = NULL; pid_t pid; + bool child_ok = false; error = check_posix_spawn(l1); if (error) { @@ -2389,7 +2420,7 @@ sys_posix_spawn(struct lwp *l1, const st if (SCARG(uap, file_actions) != NULL) { error = posix_spawn_fa_alloc(&fa, SCARG(uap, file_actions)); if (error) - goto fail; + goto error_exit; } /* copyin posix_spawnattr struct */ @@ -2397,16 +2428,16 @@ sys_posix_spawn(struct lwp *l1, const st sa = kmem_alloc(sizeof(*sa), KM_SLEEP); error = copyin(SCARG(uap, attrp), sa, sizeof(*sa)); if (error) - goto fail; + goto error_exit; } /* * Do the spawn */ - error = do_posix_spawn(l1, &pid, SCARG(uap, path), fa, sa, + error = do_posix_spawn(l1, &pid, &child_ok, SCARG(uap, path), fa, sa, SCARG(uap, argv), SCARG(uap, envp), execve_fetch_element); if (error) - goto fail; + goto error_exit; if (error == 0 && SCARG(uap, pid) != NULL) error = copyout(&pid, SCARG(uap, pid), sizeof(pid)); @@ -2414,14 +2445,17 @@ sys_posix_spawn(struct lwp *l1, const st *retval = error; return 0; -fail: - (void)chgproccnt(kauth_cred_getuid(l1->l_cred), -1); - atomic_dec_uint(&nprocs); - - if (sa) - kmem_free(sa, sizeof(*sa)); - if (fa) - posix_spawn_fa_free(fa, fa->len); + error_exit: + if (!child_ok) { + (void)chgproccnt(kauth_cred_getuid(l1->l_cred), -1); + atomic_dec_uint(&nprocs); + + if (sa) + kmem_free(sa, sizeof(*sa)); + if (fa) + posix_spawn_fa_free(fa, fa->len); + } + *retval = error; return 0; } Index: src/sys/sys/exec.h diff -u src/sys/sys/exec.h:1.136 src/sys/sys/exec.h:1.137 --- src/sys/sys/exec.h:1.136 Mon Apr 30 21:19:58 2012 +++ src/sys/sys/exec.h Wed May 2 23:33:11 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: exec.h,v 1.136 2012/04/30 21:19:58 rmind Exp $ */ +/* $NetBSD: exec.h,v 1.137 2012/05/02 23:33:11 rmind Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -301,7 +301,7 @@ struct posix_spawn_file_actions; struct posix_spawnattr; int check_posix_spawn (struct lwp *); void posix_spawn_fa_free(struct posix_spawn_file_actions *, size_t); -int do_posix_spawn(struct lwp *, pid_t *, const char *, +int do_posix_spawn(struct lwp *, pid_t *, bool*, const char *, struct posix_spawn_file_actions *, struct posix_spawnattr *, char *const *argv, char *const *, execve_fetch_element_t);