diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2007-11-05 11:36:16 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2007-11-05 11:36:16 +0000 |
commit | 89b57fcf01d895a82a24b97548116e924bca5efc (patch) | |
tree | cccec36134d2cab5ce1eabb67dcaab5981e9beb0 /sys/kern/kern_fork.c | |
parent | 762d6411cf512409e42fe8c66aa54c8975725f76 (diff) | |
download | src-89b57fcf01d895a82a24b97548116e924bca5efc.tar.gz src-89b57fcf01d895a82a24b97548116e924bca5efc.zip |
Fix for the panic("vm_thread_new: kstack allocation failed") and
silent NULL pointer dereference in the i386 and sparc64 pmap_pinit()
when the kmem_alloc_nofault() failed to allocate address space. Both
functions now return error instead of panicing or dereferencing NULL.
As consequence, vmspace_exec() and vmspace_unshare() returns the errno
int. struct vmspace arg was added to vm_forkproc() to avoid dealing
with failed allocation when most of the fork1() job is already done.
The kernel stack for the thread is now set up in the thread_alloc(),
that itself may return NULL. Also, allocation of the first process
thread is performed in the fork1() to properly deal with stack
allocation failure. proc_linkup() is separated into proc_linkup()
called from fork1(), and proc_linkup0(), that is used to set up the
kernel process (was known as swapper).
In collaboration with: Peter Holm
Reviewed by: jhb
Notes
Notes:
svn path=/head/; revision=173361
Diffstat (limited to 'sys/kern/kern_fork.c')
-rw-r--r-- | sys/kern/kern_fork.c | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 917de57ca742..ab7ca8b41206 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -195,6 +195,7 @@ fork1(td, flags, pages, procp) struct filedesc_to_leader *fdtol; struct thread *td2; struct sigacts *newsigacts; + struct vmspace *vm2; int error; /* Can't copy and clear. */ @@ -218,7 +219,9 @@ fork1(td, flags, pages, procp) PROC_UNLOCK(p1); } - vm_forkproc(td, NULL, NULL, flags); + error = vm_forkproc(td, NULL, NULL, NULL, flags); + if (error) + goto norfproc_fail; /* * Close all file descriptors. @@ -236,6 +239,7 @@ fork1(td, flags, pages, procp) if (flags & RFFDG) fdunshare(p1, td); +norfproc_fail: if (((p1->p_flag & (P_HADTHREADS|P_SYSTEM)) == P_HADTHREADS) && (flags & (RFCFDG | RFFDG))) { PROC_LOCK(p1); @@ -243,7 +247,7 @@ fork1(td, flags, pages, procp) PROC_UNLOCK(p1); } *procp = NULL; - return (0); + return (error); } /* @@ -254,6 +258,32 @@ fork1(td, flags, pages, procp) /* Allocate new proc. */ newproc = uma_zalloc(proc_zone, M_WAITOK); + if (TAILQ_EMPTY(&newproc->p_threads)) { + td2 = thread_alloc(); + if (td2 == NULL) { + error = ENOMEM; + goto fail1; + } + proc_linkup(newproc, td2); + sched_newproc(newproc, td2); + } else + td2 = FIRST_THREAD_IN_PROC(newproc); + + /* Allocate and switch to an alternate kstack if specified. */ + if (pages != 0) { + if (!vm_thread_new_altkstack(td2, pages)) { + error = ENOMEM; + goto fail1; + } + } + if ((flags & RFMEM) == 0) { + vm2 = vmspace_fork(p1->p_vmspace); + if (vm2 == NULL) { + error = ENOMEM; + goto fail1; + } + } else + vm2 = NULL; #ifdef MAC mac_proc_init(newproc); #endif @@ -380,7 +410,6 @@ again: lastpid = trypid; p2 = newproc; - td2 = FIRST_THREAD_IN_PROC(newproc); p2->p_state = PRS_NEW; /* protect against others */ p2->p_pid = trypid; /* @@ -456,9 +485,6 @@ again: * Start by zeroing the section of proc that is zero-initialized, * then copy the section that is copied directly from the parent. */ - /* Allocate and switch to an alternate kstack if specified. */ - if (pages != 0) - vm_thread_new_altkstack(td2, pages); PROC_LOCK(p2); PROC_LOCK(p1); @@ -630,7 +656,7 @@ again: * Finish creating the child process. It will return via a different * execution path later. (ie: directly into user mode) */ - vm_forkproc(td, p2, td2, flags); + vm_forkproc(td, p2, td2, vm2, flags); if (flags == (RFFDG | RFPROC)) { PCPU_INC(cnt.v_forks); @@ -713,6 +739,7 @@ fail: #ifdef MAC mac_proc_destroy(newproc); #endif +fail1: uma_zfree(proc_zone, newproc); pause("fork", hz / 2); return (error); |