aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Burkholder <jake@FreeBSD.org>2002-03-04 05:07:22 +0000
committerJake Burkholder <jake@FreeBSD.org>2002-03-04 05:07:22 +0000
commit34aef253aac66c5ec76ec97b5a3fefd430ee175a (patch)
tree476f120e8a7f5372e9b518990eaa9bb14877bb8a
parent63c6b757ab2aef5cdcd97e0347852bfaec73822f (diff)
downloadsrc-34aef253aac66c5ec76ec97b5a3fefd430ee175a.tar.gz
src-34aef253aac66c5ec76ec97b5a3fefd430ee175a.zip
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
Notes
Notes: svn path=/head/; revision=91612
-rw-r--r--sys/sparc64/sparc64/vm_machdep.c48
1 files changed, 40 insertions, 8 deletions
diff --git a/sys/sparc64/sparc64/vm_machdep.c b/sys/sparc64/sparc64/vm_machdep.c
index 18f528c66d36..414fec45e317 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.