diff options
author | kib <kib@FreeBSD.org> | 2007-11-05 11:36:16 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2007-11-05 11:36:16 +0000 |
commit | 9ae733819b7cdf0eef51ba1b18d39feb136a9dbf (patch) | |
tree | cccec36134d2cab5ce1eabb67dcaab5981e9beb0 | |
parent | f3f033b9b90ea07350fbe7483af3d9636cb31d1d (diff) | |
download | FreeBSD-src-9ae733819b7cdf0eef51ba1b18d39feb136a9dbf.zip FreeBSD-src-9ae733819b7cdf0eef51ba1b18d39feb136a9dbf.tar.gz |
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
41 files changed, 222 insertions, 93 deletions
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index d79e87f..b1d9637 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -1137,7 +1137,7 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); preload_metadata = (caddr_t)(uintptr_t)(modulep + KERNBASE); preload_bootstrap_relocate(KERNBASE); diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c index bd255b9..ee33bce 100644 --- a/sys/amd64/amd64/pmap.c +++ b/sys/amd64/amd64/pmap.c @@ -1186,7 +1186,7 @@ pmap_pinit0(pmap_t pmap) * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { vm_page_t pml4pg; @@ -1216,6 +1216,8 @@ pmap_pinit(pmap_t pmap) pmap->pm_active = 0; TAILQ_INIT(&pmap->pm_pvchunk); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + + return (1); } /* diff --git a/sys/arm/arm/pmap.c b/sys/arm/arm/pmap.c index 2dbbe6e..4978460 100644 --- a/sys/arm/arm/pmap.c +++ b/sys/arm/arm/pmap.c @@ -3813,7 +3813,7 @@ pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot) * such as one in a vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { PDEBUG(1, printf("pmap_pinit: pmap = %08x\n", (uint32_t) pmap)); @@ -3832,6 +3832,7 @@ pmap_pinit(pmap_t pmap) pmap_enter(pmap, vector_page, PHYS_TO_VM_PAGE(systempage.pv_pa), VM_PROT_READ, 1); } + return (1); } diff --git a/sys/arm/at91/kb920x_machdep.c b/sys/arm/at91/kb920x_machdep.c index 2647e61..f58a327 100644 --- a/sys/arm/at91/kb920x_machdep.c +++ b/sys/arm/at91/kb920x_machdep.c @@ -449,7 +449,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/sa11x0/assabet_machdep.c b/sys/arm/sa11x0/assabet_machdep.c index 482f3a5..195a4fb 100644 --- a/sys/arm/sa11x0/assabet_machdep.c +++ b/sys/arm/sa11x0/assabet_machdep.c @@ -422,7 +422,7 @@ initarm(void *arg, void *arg2) /* Set stack for exception handlers */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/xscale/i80321/ep80219_machdep.c b/sys/arm/xscale/i80321/ep80219_machdep.c index 2eee7e1..c0dda49 100644 --- a/sys/arm/xscale/i80321/ep80219_machdep.c +++ b/sys/arm/xscale/i80321/ep80219_machdep.c @@ -426,7 +426,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/xscale/i80321/iq31244_machdep.c b/sys/arm/xscale/i80321/iq31244_machdep.c index 2e4433e..ef8d0eb 100644 --- a/sys/arm/xscale/i80321/iq31244_machdep.c +++ b/sys/arm/xscale/i80321/iq31244_machdep.c @@ -424,7 +424,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/xscale/i8134x/crb_machdep.c b/sys/arm/xscale/i8134x/crb_machdep.c index d29693f..0c6475a 100644 --- a/sys/arm/xscale/i8134x/crb_machdep.c +++ b/sys/arm/xscale/i8134x/crb_machdep.c @@ -409,7 +409,7 @@ initarm(void *arg, void *arg2) #ifdef KSE proc_linkup(&proc0, &ksegrp0, &thread0); #else - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); #endif thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) diff --git a/sys/arm/xscale/ixp425/avila_machdep.c b/sys/arm/xscale/ixp425/avila_machdep.c index c02ddd3..71d7f71 100644 --- a/sys/arm/xscale/ixp425/avila_machdep.c +++ b/sys/arm/xscale/ixp425/avila_machdep.c @@ -490,7 +490,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/compat/pecoff/imgact_pecoff.c b/sys/compat/pecoff/imgact_pecoff.c index ae23210..cf1212a 100644 --- a/sys/compat/pecoff/imgact_pecoff.c +++ b/sys/compat/pecoff/imgact_pecoff.c @@ -416,7 +416,11 @@ exec_pecoff_coff_prep_zmagic(struct image_params * imgp, wp = (void *) ((char *) ap + sizeof(struct coff_aouthdr)); error = pecoff_read_from(FIRST_THREAD_IN_PROC(imgp->proc), imgp->vp, peofs + PECOFF_HDR_SIZE, (caddr_t) sh, scnsiz); - exec_new_vmspace(imgp, &pecoff_sysvec); + if (error) + return (error); + error = exec_new_vmspace(imgp, &pecoff_sysvec); + if (error) + return (error); vmspace = imgp->proc->p_vmspace; for (i = 0; i < fp->f_nscns; i++) { prot = VM_PROT_WRITE; /* XXX for relocation? */ diff --git a/sys/compat/svr4/imgact_svr4.c b/sys/compat/svr4/imgact_svr4.c index 4c71020..64c2a84 100644 --- a/sys/compat/svr4/imgact_svr4.c +++ b/sys/compat/svr4/imgact_svr4.c @@ -120,7 +120,9 @@ exec_svr4_imgact(imgp) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(imgp, &svr4_sysvec); + error = exec_new_vmspace(imgp, &svr4_sysvec); + if (error) + goto fail; vmspace = imgp->proc->p_vmspace; /* diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 55ec4be..eacbb48 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -2088,7 +2088,7 @@ init386(first) * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); metadata_missing = 0; if (bootinfo.bi_modulep) { diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c index b83fd1c..343760a 100644 --- a/sys/i386/i386/pmap.c +++ b/sys/i386/i386/pmap.c @@ -1227,7 +1227,7 @@ pmap_pinit0(pmap_t pmap) * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { vm_page_t m, ptdpg[NPGPTD]; @@ -1244,6 +1244,11 @@ pmap_pinit(pmap_t pmap) if (pmap->pm_pdir == NULL) { pmap->pm_pdir = (pd_entry_t *)kmem_alloc_nofault(kernel_map, NBPTD); + + if (pmap->pm_pdir == NULL) { + PMAP_LOCK_DESTROY(pmap); + return (0); + } #ifdef PAE pmap->pm_pdpt = uma_zalloc(pdptzone, M_WAITOK | M_ZERO); KASSERT(((vm_offset_t)pmap->pm_pdpt & @@ -1297,6 +1302,8 @@ pmap_pinit(pmap_t pmap) pmap->pm_active = 0; TAILQ_INIT(&pmap->pm_pvchunk); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + + return (1); } /* diff --git a/sys/i386/ibcs2/imgact_coff.c b/sys/i386/ibcs2/imgact_coff.c index b078e56..74a01f4 100644 --- a/sys/i386/ibcs2/imgact_coff.c +++ b/sys/i386/ibcs2/imgact_coff.c @@ -337,7 +337,9 @@ exec_coff_imgact(imgp) VOP_UNLOCK(imgp->vp, 0, td); - exec_new_vmspace(imgp, &ibcs2_svr3_sysvec); + error = exec_new_vmspace(imgp, &ibcs2_svr3_sysvec); + if (error) + goto fail; vmspace = imgp->proc->p_vmspace; for (i = 0; i < nscns; i++) { diff --git a/sys/i386/linux/imgact_linux.c b/sys/i386/linux/imgact_linux.c index 268936d..ce067d5 100644 --- a/sys/i386/linux/imgact_linux.c +++ b/sys/i386/linux/imgact_linux.c @@ -119,7 +119,9 @@ exec_linux_imgact(struct image_params *imgp) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(imgp, &linux_sysvec); + error = exec_new_vmspace(imgp, &linux_sysvec); + if (error) + goto fail; vmspace = imgp->proc->p_vmspace; /* diff --git a/sys/ia64/ia64/machdep.c b/sys/ia64/ia64/machdep.c index 6f92b70..b0854fc 100644 --- a/sys/ia64/ia64/machdep.c +++ b/sys/ia64/ia64/machdep.c @@ -789,7 +789,7 @@ ia64_init(void) msgbufp = (struct msgbuf *)pmap_steal_memory(MSGBUF_SIZE); msgbufinit(msgbufp, MSGBUF_SIZE); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); /* * Init mapping for kernel stack for proc 0 */ diff --git a/sys/ia64/ia64/pmap.c b/sys/ia64/ia64/pmap.c index 7a50848..253211c 100644 --- a/sys/ia64/ia64/pmap.c +++ b/sys/ia64/ia64/pmap.c @@ -710,7 +710,7 @@ pmap_pinit0(struct pmap *pmap) * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ -void +int pmap_pinit(struct pmap *pmap) { int i; @@ -721,6 +721,7 @@ pmap_pinit(struct pmap *pmap) pmap->pm_active = 0; TAILQ_INIT(&pmap->pm_pvlist); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + return (1); } /*************************************************** diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c index 7f202d9..45b39c2 100644 --- a/sys/kern/imgact_aout.c +++ b/sys/kern/imgact_aout.c @@ -198,9 +198,11 @@ exec_aout_imgact(imgp) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(imgp, &aout_sysvec); + error = exec_new_vmspace(imgp, &aout_sysvec); vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) + return (error); /* * The vm space can be changed by exec_new_vmspace diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index b992f2b..619be4c 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -666,10 +666,12 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) */ VOP_UNLOCK(imgp->vp, 0, td); - exec_new_vmspace(imgp, sv); + error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) + return (error); vmspace = imgp->proc->p_vmspace; diff --git a/sys/kern/imgact_gzip.c b/sys/kern/imgact_gzip.c index 60a0f6c..3cd1dab 100644 --- a/sys/kern/imgact_gzip.c +++ b/sys/kern/imgact_gzip.c @@ -239,9 +239,13 @@ do_aout_hdr(struct imgact_gzip * gz) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(gz->ip, &aout_sysvec); + error = exec_new_vmspace(gz->ip, &aout_sysvec); vn_lock(gz->ip->vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) { + gz->where = __LINE__; + return (error); + } vmspace = gz->ip->proc->p_vmspace; diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index d2798db..80ef703 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -914,7 +914,9 @@ exec_new_vmspace(imgp, sv) pmap_remove_pages(vmspace_pmap(vmspace)); vm_map_remove(map, vm_map_min(map), vm_map_max(map)); } else { - vmspace_exec(p, sv->sv_minuser, sv->sv_maxuser); + error = vmspace_exec(p, sv->sv_minuser, sv->sv_maxuser); + if (error) + return (error); vmspace = p->p_vmspace; map = &vmspace->vm_map; } diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 917de57..ab7ca8b 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); diff --git a/sys/kern/kern_kse.c b/sys/kern/kern_kse.c index 4174bde..b044605 100644 --- a/sys/kern/kern_kse.c +++ b/sys/kern/kern_kse.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sched.h> #include <sys/signalvar.h> #include <sys/sleepqueue.h> +#include <sys/syslog.h> #include <sys/kse.h> #include <sys/ktr.h> #include <vm/uma.h> @@ -64,7 +65,7 @@ TAILQ_HEAD(, kse_upcall) zombie_upcalls = TAILQ_HEAD_INITIALIZER(zombie_upcalls); static int thread_update_usr_ticks(struct thread *td); -static void thread_alloc_spare(struct thread *td); +static int thread_alloc_spare(struct thread *td); static struct thread *thread_schedule_upcall(struct thread *td, struct kse_upcall *ku); static struct kse_upcall *upcall_alloc(void); @@ -648,6 +649,16 @@ kse_create(struct thread *td, struct kse_create_args *uap) PROC_UNLOCK(p); } + /* + * For the first call this may not have been set. + * Of course nor may it actually be needed. + * thread_schedule_upcall() will look for it. + */ + if (td->td_standin == NULL) { + if (!thread_alloc_spare(td)) + return (ENOMEM); + } + /* * Even bound LWPs get a mailbox and an upcall to hold it. * XXX This should change. @@ -657,13 +668,6 @@ kse_create(struct thread *td, struct kse_create_args *uap) newku->ku_func = mbx.km_func; bcopy(&mbx.km_stack, &newku->ku_stack, sizeof(stack_t)); - /* - * For the first call this may not have been set. - * Of course nor may it actually be needed. - * thread_schedule_upcall() will look for it. - */ - if (td->td_standin == NULL) - thread_alloc_spare(td); PROC_LOCK(p); PROC_SLOCK(p); /* @@ -989,20 +993,23 @@ error: * XXX BUG.. we need to get the cr ref after the thread has * checked and chenged its own, not 6 months before... */ -void +int thread_alloc_spare(struct thread *td) { struct thread *spare; if (td->td_standin) - return; + return (1); spare = thread_alloc(); + if (spare == NULL) + return (0); td->td_standin = spare; bzero(&spare->td_startzero, __rangeof(struct thread, td_startzero, td_endzero)); spare->td_proc = td->td_proc; spare->td_ucred = crhold(td->td_ucred); spare->td_flags = TDF_INMEM; + return (1); } /* @@ -1170,8 +1177,18 @@ thread_user_enter(struct thread *td) KASSERT(ku->ku_owner == td, ("wrong owner")); KASSERT(!TD_CAN_UNBIND(td), ("can unbind")); - if (td->td_standin == NULL) - thread_alloc_spare(td); + if (td->td_standin == NULL) { + if (!thread_alloc_spare(td)) { + PROC_LOCK(p); + if (kern_logsigexit) + log(LOG_INFO, + "pid %d (%s), uid %d: thread_alloc_spare failed\n", + p->p_pid, p->p_comm, + td->td_ucred ? td->td_ucred->cr_uid : -1); + sigexit(td, SIGSEGV); /* XXX ? */ + /* panic("thread_user_enter: thread_alloc_spare failed"); */ + } + } ku->ku_mflags = fuword32((void *)&ku->ku_mailbox->km_flags); tmbx = (void *)fuword((void *)&ku->ku_mailbox->km_curthread); if ((tmbx == NULL) || (tmbx == (void *)-1L) || @@ -1385,7 +1402,7 @@ out: * for when we re-enter the kernel. */ if (td->td_standin == NULL) - thread_alloc_spare(td); + thread_alloc_spare(td); /* XXX care of failure ? */ } ku->ku_mflags = 0; diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index f75112f..71e2b10 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -145,20 +145,21 @@ proc_dtor(void *mem, int size, void *arg) /* INVARIANTS checks go here */ p = (struct proc *)mem; td = FIRST_THREAD_IN_PROC(p); + if (td != NULL) { #ifdef INVARIANTS - KASSERT((p->p_numthreads == 1), - ("bad number of threads in exiting process")); - KASSERT((td != NULL), ("proc_dtor: bad thread pointer")); - KASSERT(STAILQ_EMPTY(&p->p_ktr), ("proc_dtor: non-empty p_ktr")); + KASSERT((p->p_numthreads == 1), + ("bad number of threads in exiting process")); + KASSERT(STAILQ_EMPTY(&p->p_ktr), ("proc_dtor: non-empty p_ktr")); #endif - /* Dispose of an alternate kstack, if it exists. - * XXX What if there are more than one thread in the proc? - * The first thread in the proc is special and not - * freed, so you gotta do this here. - */ - if (((p->p_flag & P_KTHREAD) != 0) && (td->td_altkstack != 0)) - vm_thread_dispose_altkstack(td); + /* Dispose of an alternate kstack, if it exists. + * XXX What if there are more than one thread in the proc? + * The first thread in the proc is special and not + * freed, so you gotta do this here. + */ + if (((p->p_flag & P_KTHREAD) != 0) && (td->td_altkstack != 0)) + vm_thread_dispose_altkstack(td); + } if (p->p_ksi != NULL) KASSERT(! KSI_ONQ(p->p_ksi), ("SIGCHLD queue")); } @@ -170,17 +171,14 @@ static int proc_init(void *mem, int size, int flags) { struct proc *p; - struct thread *td; p = (struct proc *)mem; p->p_sched = (struct p_sched *)&p[1]; - td = thread_alloc(); bzero(&p->p_mtx, sizeof(struct mtx)); mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK); mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE); + TAILQ_INIT(&p->p_threads); /* all threads in proc */ p->p_stats = pstats_alloc(); - proc_linkup(p, td); - sched_newproc(p, td); return (0); } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 3b213a6..239c420 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -102,7 +102,7 @@ static uma_zone_t ksiginfo_zone = NULL; struct filterops sig_filtops = { 0, filt_sigattach, filt_sigdetach, filt_signal }; -static int kern_logsigexit = 1; +int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index de45d0a..6bf0ded 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -175,6 +175,8 @@ create_thread(struct thread *td, mcontext_t *ctx, /* Initialize our td */ newtd = thread_alloc(); + if (newtd == NULL) + return (ENOMEM); /* * Try the copyout as soon as we allocate the td so we don't diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c index 2a28823..cde764f 100644 --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -181,13 +181,12 @@ thread_init(void *mem, int size, int flags) td = (struct thread *)mem; - vm_thread_new(td, 0); - cpu_thread_setup(td); td->td_sleepqueue = sleepq_alloc(); td->td_turnstile = turnstile_alloc(); td->td_sched = (struct td_sched *)&td[1]; sched_newthread(td); umtx_thread_init(td); + td->td_kstack = 0; return (0); } @@ -203,7 +202,6 @@ thread_fini(void *mem, int size) turnstile_free(td->td_turnstile); sleepq_free(td->td_sleepqueue); umtx_thread_fini(td); - vm_thread_dispose(td); } /* @@ -215,10 +213,16 @@ thread_fini(void *mem, int size) * proc_init() */ void +proc_linkup0(struct proc *p, struct thread *td) +{ + TAILQ_INIT(&p->p_threads); /* all threads in proc */ + proc_linkup(p, td); +} + +void proc_linkup(struct proc *p, struct thread *td) { - TAILQ_INIT(&p->p_threads); /* all threads in proc */ #ifdef KSE TAILQ_INIT(&p->p_upcalls); /* upcall list */ #endif @@ -310,9 +314,18 @@ thread_reap(void) struct thread * thread_alloc(void) { + struct thread *td; thread_reap(); /* check if any zombies to get */ - return (uma_zalloc(thread_zone, M_WAITOK)); + + td = (struct thread *)uma_zalloc(thread_zone, M_WAITOK); + KASSERT(td->td_kstack == 0, ("thread_alloc got thread with kstack")); + if (!vm_thread_new(td, 0)) { + uma_zfree(thread_zone, td); + return (NULL); + } + cpu_thread_setup(td); + return (td); } @@ -324,6 +337,10 @@ thread_free(struct thread *td) { cpu_thread_clean(td); + if (td->td_altkstack != 0) + vm_thread_dispose_altkstack(td); + if (td->td_kstack != 0) + vm_thread_dispose(td); uma_zfree(thread_zone, td); } diff --git a/sys/pc98/pc98/machdep.c b/sys/pc98/pc98/machdep.c index a7407b9..3ec69be 100644 --- a/sys/pc98/pc98/machdep.c +++ b/sys/pc98/pc98/machdep.c @@ -1917,7 +1917,7 @@ init386(first) * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); /* * Initialize DMAC diff --git a/sys/powerpc/aim/machdep.c b/sys/powerpc/aim/machdep.c index 98c4051..0d771e1 100644 --- a/sys/powerpc/aim/machdep.c +++ b/sys/powerpc/aim/machdep.c @@ -287,7 +287,7 @@ powerpc_init(u_int startkernel, u_int endkernel, u_int basekernel, void *mdp) /* * Start initializing proc0 and thread0. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_frame = &frame0; /* diff --git a/sys/powerpc/powerpc/machdep.c b/sys/powerpc/powerpc/machdep.c index 98c4051..0d771e1 100644 --- a/sys/powerpc/powerpc/machdep.c +++ b/sys/powerpc/powerpc/machdep.c @@ -287,7 +287,7 @@ powerpc_init(u_int startkernel, u_int endkernel, u_int basekernel, void *mdp) /* * Start initializing proc0 and thread0. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_frame = &frame0; /* diff --git a/sys/powerpc/powerpc/pmap_dispatch.c b/sys/powerpc/powerpc/pmap_dispatch.c index 92ac514..9a9c1f5 100644 --- a/sys/powerpc/powerpc/pmap_dispatch.c +++ b/sys/powerpc/powerpc/pmap_dispatch.c @@ -193,10 +193,11 @@ pmap_page_init(vm_page_t m) MMU_PAGE_INIT(mmu_obj, m); } -void +int pmap_pinit(pmap_t pmap) { MMU_PINIT(mmu_obj, pmap); + return (1); } void diff --git a/sys/sparc64/sparc64/machdep.c b/sys/sparc64/sparc64/machdep.c index 889d676..2e6d05e 100644 --- a/sys/sparc64/sparc64/machdep.c +++ b/sys/sparc64/sparc64/machdep.c @@ -399,7 +399,7 @@ sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec) /* * Initialize proc0 stuff (p_contested needs to be done early). */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); proc0.p_md.md_sigtramp = NULL; proc0.p_md.md_utrap = NULL; thread0.td_kstack = kstack0; diff --git a/sys/sparc64/sparc64/pmap.c b/sys/sparc64/sparc64/pmap.c index eb61582..1c71c5f 100644 --- a/sys/sparc64/sparc64/pmap.c +++ b/sys/sparc64/sparc64/pmap.c @@ -1006,7 +1006,7 @@ pmap_pinit0(pmap_t pm) * Initialize a preallocated and zeroed pmap structure, such as one in a * vmspace structure. */ -void +int pmap_pinit(pmap_t pm) { vm_page_t ma[TSB_PAGES]; @@ -1021,6 +1021,10 @@ pmap_pinit(pmap_t pm) if (pm->pm_tsb == NULL) { pm->pm_tsb = (struct tte *)kmem_alloc_nofault(kernel_map, TSB_BSIZE); + if (pm->pm_tsb == NULL) { + PMAP_LOCK_DESTROY(pm); + return (0); + } } /* @@ -1044,6 +1048,7 @@ pmap_pinit(pmap_t pm) pm->pm_context[i] = -1; pm->pm_active = 0; bzero(&pm->pm_stats, sizeof(pm->pm_stats)); + return (1); } /* diff --git a/sys/sun4v/sun4v/machdep.c b/sys/sun4v/sun4v/machdep.c index eb3d3c9..16a508a 100644 --- a/sys/sun4v/sun4v/machdep.c +++ b/sys/sun4v/sun4v/machdep.c @@ -364,7 +364,7 @@ sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec) * Initialize proc0 stuff (p_contested needs to be done early). */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); proc0.p_md.md_sigtramp = NULL; proc0.p_md.md_utrap = NULL; frame0.tf_tstate = TSTATE_IE | TSTATE_PEF | TSTATE_PRIV; diff --git a/sys/sun4v/sun4v/pmap.c b/sys/sun4v/sun4v/pmap.c index 917eb1a..ecfe3db 100644 --- a/sys/sun4v/sun4v/pmap.c +++ b/sys/sun4v/sun4v/pmap.c @@ -1703,7 +1703,7 @@ pmap_pinit0(pmap_t pmap) * Initialize a preallocated and zeroed pmap structure, such as one in a * vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { int i; @@ -1723,6 +1723,7 @@ pmap_pinit(pmap_t pmap) TAILQ_INIT(&pmap->pm_pvlist); PMAP_LOCK_INIT(pmap); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + return (1); } /* diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 6c8bac7..8274342 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -839,6 +839,7 @@ void pargs_drop(struct pargs *pa); void pargs_free(struct pargs *pa); void pargs_hold(struct pargs *pa); void procinit(void); +void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); void proc_reparent(struct proc *child, struct proc *newparent); struct pstats *pstats_alloc(void); diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h index e78358c..dc09226 100644 --- a/sys/sys/signalvar.h +++ b/sys/sys/signalvar.h @@ -299,6 +299,7 @@ struct mtx; extern int sugid_coredump; /* Sysctl variable kern.sugid_coredump */ extern struct mtx sigio_lock; +extern int kern_logsigexit; /* Sysctl variable kern.logsigexit */ /* * Lock the pointers for a sigio object in the underlying objects of diff --git a/sys/vm/pmap.h b/sys/vm/pmap.h index 44c6a82..3d9045f 100644 --- a/sys/vm/pmap.h +++ b/sys/vm/pmap.h @@ -114,7 +114,7 @@ void pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size); boolean_t pmap_page_exists_quick(pmap_t pmap, vm_page_t m); void pmap_page_init(vm_page_t m); -void pmap_pinit(pmap_t); +int pmap_pinit(pmap_t); void pmap_pinit0(pmap_t); void pmap_protect(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); void pmap_qenter(vm_offset_t, vm_page_t *, int); diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h index 31f6e23..27580bd 100644 --- a/sys/vm/vm_extern.h +++ b/sys/vm/vm_extern.h @@ -70,14 +70,14 @@ int vm_fault(vm_map_t, vm_offset_t, vm_prot_t, int); void vm_fault_copy_entry(vm_map_t, vm_map_t, vm_map_entry_t, vm_map_entry_t); void vm_fault_unwire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t); int vm_fault_wire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t, boolean_t); -void vm_forkproc(struct thread *, struct proc *, struct thread *, int); +int vm_forkproc(struct thread *, struct proc *, struct thread *, struct vmspace *, int); void vm_waitproc(struct proc *); int vm_mmap(vm_map_t, vm_offset_t *, vm_size_t, vm_prot_t, vm_prot_t, int, objtype_t, void *, vm_ooffset_t); void vm_set_page_size(void); struct vmspace *vmspace_alloc(vm_offset_t, vm_offset_t); struct vmspace *vmspace_fork(struct vmspace *); -void vmspace_exec(struct proc *, vm_offset_t, vm_offset_t); -void vmspace_unshare(struct proc *); +int vmspace_exec(struct proc *, vm_offset_t, vm_offset_t); +int vmspace_unshare(struct proc *); void vmspace_exit(struct thread *); struct vmspace *vmspace_acquire_ref(struct proc *); void vmspace_free(struct vmspace *); @@ -92,8 +92,8 @@ struct sf_buf *vm_imgact_map_page(vm_object_t object, vm_ooffset_t offset); void vm_imgact_unmap_page(struct sf_buf *sf); void vm_thread_dispose(struct thread *td); void vm_thread_dispose_altkstack(struct thread *td); -void vm_thread_new(struct thread *td, int pages); -void vm_thread_new_altkstack(struct thread *td, int pages); +int vm_thread_new(struct thread *td, int pages); +int vm_thread_new_altkstack(struct thread *td, int pages); void vm_thread_swapin(struct thread *td); void vm_thread_swapout(struct thread *td); #endif /* _KERNEL */ diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c index caa8aa0..258b886 100644 --- a/sys/vm/vm_glue.c +++ b/sys/vm/vm_glue.c @@ -321,7 +321,7 @@ vm_imgact_unmap_page(struct sf_buf *sf) * This routine directly affects the fork perf for a process and * create performance for a thread. */ -void +int vm_thread_new(struct thread *td, int pages) { vm_object_t ksobj; @@ -338,18 +338,22 @@ vm_thread_new(struct thread *td, int pages) * Allocate an object for the kstack. */ ksobj = vm_object_allocate(OBJT_DEFAULT, pages); - td->td_kstack_obj = ksobj; /* * Get a kernel virtual address for this thread's kstack. */ ks = kmem_alloc_nofault(kernel_map, (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); - if (ks == 0) - panic("vm_thread_new: kstack allocation failed"); + if (ks == 0) { + printf("vm_thread_new: kstack allocation failed\n"); + vm_object_deallocate(ksobj); + return (0); + } + if (KSTACK_GUARD_PAGES != 0) { pmap_qremove(ks, KSTACK_GUARD_PAGES); ks += KSTACK_GUARD_PAGES * PAGE_SIZE; } + td->td_kstack_obj = ksobj; td->td_kstack = ks; /* * Knowing the number of pages allocated is useful when you @@ -372,6 +376,7 @@ vm_thread_new(struct thread *td, int pages) } VM_OBJECT_UNLOCK(ksobj); pmap_qenter(ks, ma, pages); + return (1); } /* @@ -403,6 +408,7 @@ vm_thread_dispose(struct thread *td) vm_object_deallocate(ksobj); kmem_free(kernel_map, ks - (KSTACK_GUARD_PAGES * PAGE_SIZE), (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); + td->td_kstack = 0; } /* @@ -468,7 +474,7 @@ vm_thread_swapin(struct thread *td) /* * Set up a variable-sized alternate kstack. */ -void +int vm_thread_new_altkstack(struct thread *td, int pages) { @@ -476,7 +482,7 @@ vm_thread_new_altkstack(struct thread *td, int pages) td->td_altkstack_obj = td->td_kstack_obj; td->td_altkstack_pages = td->td_kstack_pages; - vm_thread_new(td, pages); + return (vm_thread_new(td, pages)); } /* @@ -504,14 +510,16 @@ vm_thread_dispose_altkstack(struct thread *td) * ready to run. The new process is set up so that it returns directly * to user mode to avoid stack copying and relocation problems. */ -void -vm_forkproc(td, p2, td2, flags) +int +vm_forkproc(td, p2, td2, vm2, flags) struct thread *td; struct proc *p2; struct thread *td2; + struct vmspace *vm2; int flags; { struct proc *p1 = td->td_proc; + int error; if ((flags & RFPROC) == 0) { /* @@ -521,11 +529,13 @@ vm_forkproc(td, p2, td2, flags) */ if ((flags & RFMEM) == 0) { if (p1->p_vmspace->vm_refcnt > 1) { - vmspace_unshare(p1); + error = vmspace_unshare(p1); + if (error) + return (error); } } cpu_fork(td, p2, td2, flags); - return; + return (0); } if (flags & RFMEM) { @@ -538,7 +548,7 @@ vm_forkproc(td, p2, td2, flags) } if ((flags & RFMEM) == 0) { - p2->p_vmspace = vmspace_fork(p1->p_vmspace); + p2->p_vmspace = vm2; if (p1->p_vmspace->vm_shm) shmfork(p1, p2); } @@ -548,6 +558,7 @@ vm_forkproc(td, p2, td2, flags) * and make the child ready to run. */ cpu_fork(td, p2, td2, flags); + return (0); } /* diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index 4b31fb3..96e7411 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -197,7 +197,6 @@ vmspace_zfini(void *mem, int size) struct vmspace *vm; vm = (struct vmspace *)mem; - pmap_release(vmspace_pmap(vm)); vm_map_zfini(&vm->vm_map, sizeof(vm->vm_map)); } @@ -208,8 +207,8 @@ vmspace_zinit(void *mem, int size, int flags) vm = (struct vmspace *)mem; + vm->vm_map.pmap = NULL; (void)vm_map_zinit(&vm->vm_map, sizeof(vm->vm_map), flags); - pmap_pinit(vmspace_pmap(vm)); return (0); } @@ -272,6 +271,10 @@ vmspace_alloc(min, max) struct vmspace *vm; vm = uma_zalloc(vmspace_zone, M_WAITOK); + if (vm->vm_map.pmap == NULL && !pmap_pinit(vmspace_pmap(vm))) { + uma_zfree(vmspace_zone, vm); + return (NULL); + } CTR1(KTR_VM, "vmspace_alloc: %p", vm); _vm_map_init(&vm->vm_map, min, max); vm->vm_map.pmap = vmspace_pmap(vm); /* XXX */ @@ -321,6 +324,12 @@ vmspace_dofree(struct vmspace *vm) (void)vm_map_remove(&vm->vm_map, vm->vm_map.min_offset, vm->vm_map.max_offset); + /* + * XXX Comment out the pmap_release call for now. The + * vmspace_zone is marked as UMA_ZONE_NOFREE, and bugs cause + * pmap.resident_count to be != 0 on exit sometimes. + */ +/* pmap_release(vmspace_pmap(vm)); */ uma_zfree(vmspace_zone, vm); } @@ -2584,6 +2593,8 @@ vmspace_fork(struct vmspace *vm1) vm_map_lock(old_map); vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset); + if (vm2 == NULL) + goto unlock_and_return; vm2->vm_taddr = vm1->vm_taddr; vm2->vm_daddr = vm1->vm_daddr; vm2->vm_maxsaddr = vm1->vm_maxsaddr; @@ -2675,7 +2686,7 @@ vmspace_fork(struct vmspace *vm1) } old_entry = old_entry->next; } - +unlock_and_return: vm_map_unlock(old_map); return (vm2); @@ -3003,13 +3014,15 @@ Retry: * Unshare the specified VM space for exec. If other processes are * mapped to it, then create a new one. The new vmspace is null. */ -void +int vmspace_exec(struct proc *p, vm_offset_t minuser, vm_offset_t maxuser) { struct vmspace *oldvmspace = p->p_vmspace; struct vmspace *newvmspace; newvmspace = vmspace_alloc(minuser, maxuser); + if (newvmspace == NULL) + return (ENOMEM); newvmspace->vm_swrss = oldvmspace->vm_swrss; /* * This code is written like this for prototype purposes. The @@ -3024,27 +3037,31 @@ vmspace_exec(struct proc *p, vm_offset_t minuser, vm_offset_t maxuser) if (p == curthread->td_proc) /* XXXKSE ? */ pmap_activate(curthread); vmspace_free(oldvmspace); + return (0); } /* * Unshare the specified VM space for forcing COW. This * is called by rfork, for the (RFMEM|RFPROC) == 0 case. */ -void +int vmspace_unshare(struct proc *p) { struct vmspace *oldvmspace = p->p_vmspace; struct vmspace *newvmspace; if (oldvmspace->vm_refcnt == 1) - return; + return (0); newvmspace = vmspace_fork(oldvmspace); + if (newvmspace == NULL) + return (ENOMEM); PROC_VMSPACE_LOCK(p); p->p_vmspace = newvmspace; PROC_VMSPACE_UNLOCK(p); if (p == curthread->td_proc) /* XXXKSE ? */ pmap_activate(curthread); vmspace_free(oldvmspace); + return (0); } /* |