diff options
author | jake <jake@FreeBSD.org> | 2002-03-04 05:07:22 +0000 |
---|---|---|
committer | jake <jake@FreeBSD.org> | 2002-03-04 05:07:22 +0000 |
commit | dd2207f5cd6095600f89c5258f3dafe916f98371 (patch) | |
tree | 476f120e8a7f5372e9b518990eaa9bb14877bb8a /sys/sparc64 | |
parent | 199a58a6972fa959f4d35c91efe27d0a1ccea2cf (diff) | |
download | FreeBSD-src-dd2207f5cd6095600f89c5258f3dafe916f98371.zip FreeBSD-src-dd2207f5cd6095600f89c5258f3dafe916f98371.tar.gz |
Fix obscure problems with vfork where part of the parent's stack could be
clobbered by the child. This is more complicated than usual because the
window that could get clobbered is pushed in kernel mode, so a lot of
registers would have to be saved in other registers in userland and we
don't have enough. What we do have is space in the pcb to temporarily
store user windows that were spilled in kernel mode, but could not be
immediately stored to the user stack. So we copy in the parent's topmost
window and save it in the pcb, and arrange for it to be copied back out
when the child is done frobbing the stack.
Reviewed by: tmm
Diffstat (limited to 'sys/sparc64')
-rw-r--r-- | sys/sparc64/sparc64/vm_machdep.c | 48 |
1 files changed, 40 insertions, 8 deletions
diff --git a/sys/sparc64/sparc64/vm_machdep.c b/sys/sparc64/sparc64/vm_machdep.c index 18f528c..414fec4 100644 --- a/sys/sparc64/sparc64/vm_machdep.c +++ b/sys/sparc64/sparc64/vm_machdep.c @@ -60,6 +60,7 @@ #include <vm/pmap.h> #include <vm/vm_map.h> #include <vm/vm_page.h> +#include <vm/vm_param.h> #include <machine/cache.h> #include <machine/cpu.h> @@ -95,7 +96,11 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) struct md_utrap *ut; struct trapframe *tf; struct frame *fp; - struct pcb *pcb; + struct pcb *pcb1; + struct pcb *pcb2; + vm_offset_t sp; + int error; + int i; KASSERT(td1 == curthread || td1 == &thread0, ("cpu_fork: p1 not curproc and not proc0")); @@ -108,29 +113,56 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) p2->p_md.md_utrap = ut; /* The pcb must be aligned on a 64-byte boundary. */ - pcb = (struct pcb *)((td2->td_kstack + KSTACK_PAGES * PAGE_SIZE - + pcb1 = td1->td_pcb; + pcb2 = (struct pcb *)((td2->td_kstack + KSTACK_PAGES * PAGE_SIZE - sizeof(struct pcb)) & ~0x3fUL); - td2->td_pcb = pcb; + td2->td_pcb = pcb2; /* * Ensure that p1's pcb is up to date. */ if ((td1->td_frame->tf_fprs & FPRS_FEF) != 0) { mtx_lock_spin(&sched_lock); - savefpctx(&td1->td_pcb->pcb_fpstate); + savefpctx(&pcb1->pcb_fpstate); mtx_unlock_spin(&sched_lock); } /* Make sure the copied windows are spilled. */ flushw(); /* Copy the pcb (this will copy the windows saved in the pcb, too). */ - bcopy(td1->td_pcb, pcb, sizeof(*pcb)); + bcopy(pcb1, pcb2, sizeof(*pcb1)); + + /* + * If we're creating a new user process and we're sharing the address + * space, the parent's top most frame must be saved in the pcb. The + * child will pop the frame when it returns to user mode, and may + * overwrite it with its own data causing much suffering for the + * parent. We check if its already in the pcb, and if not copy it + * in. Its unlikely that the copyin will fail, but if so there's not + * much we can do. The parent will likely crash soon anyway in that + * case. + */ + if ((flags & RFMEM) != 0 && td1 != &thread0) { + sp = td1->td_frame->tf_sp; + for (i = 0; i < pcb1->pcb_nsaved; i++) { + if (pcb1->pcb_rwsp[i] == sp) + break; + } + if (i == pcb1->pcb_nsaved) { + error = copyin((caddr_t)sp + SPOFF, &pcb1->pcb_rw[i], + sizeof(struct rwindow)); + if (error == 0) { + pcb1->pcb_rwsp[i] = sp; + pcb1->pcb_nsaved++; + } + } + } /* * Create a new fresh stack for the new process. * Copy the trap frame for the return to user mode as if from a * syscall. This copies most of the user mode register values. */ - tf = (struct trapframe *)pcb - 1; + tf = (struct trapframe *)pcb2 - 1; bcopy(td1->td_frame, tf, sizeof(*tf)); tf->tf_out[0] = 0; /* Child returns zero */ @@ -143,8 +175,8 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) fp->f_local[0] = (u_long)fork_return; fp->f_local[1] = (u_long)td2; fp->f_local[2] = (u_long)tf; - pcb->pcb_fp = (u_long)fp - SPOFF; - pcb->pcb_pc = (u_long)fork_trampoline - 8; + pcb2->pcb_fp = (u_long)fp - SPOFF; + pcb2->pcb_pc = (u_long)fork_trampoline - 8; /* * Now, cpu_switch() can schedule the new process. |