Hi, Alexander pointed me at a problem where under suitable conditions a process with rump syscall hijacking would crash when after vfork() and sent the attached test program. Under further examination, it turned out that the problem is due to libpthread and lwpctl.
Having pthread linked causes malloc to use pthread routines instead of the libc stubs. Now, the vfork() child will use a pointer to the parent's lwpctl area and thinks it is running on LWPCTL_CPU_NONE (-1). When malloc uses this to index the arena map, it unsurprisingly gets total garbage back. The following patch makes a vfork child update the parent's lwpctl area while the child is running. Comments or better ideas? -- älä karot toivorikkauttas, kyl rätei ja lumpui piisaa
#include <stddef.h> #include <stdlib.h> #include <unistd.h> int main() { malloc(1); switch(vfork()) { case -1: return EXIT_FAILURE; case 0: malloc(1); _exit(EXIT_FAILURE); default: ; } return EXIT_SUCCESS; }
Index: kern/kern_exec.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_exec.c,v retrieving revision 1.305 diff -p -u -r1.305 kern_exec.c --- kern/kern_exec.c 18 Jan 2011 08:21:03 -0000 1.305 +++ kern/kern_exec.c 14 Feb 2011 12:25:28 -0000 @@ -979,6 +979,7 @@ execve1(struct lwp *l, const char *path, mutex_enter(proc_lock); p->p_lflag &= ~PL_PPWAIT; cv_broadcast(&p->p_pptr->p_waitcv); + l->l_lwpctl = NULL; /* borrowed from parent */ mutex_exit(proc_lock); } Index: kern/kern_exit.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_exit.c,v retrieving revision 1.231 diff -p -u -r1.231 kern_exit.c --- kern/kern_exit.c 18 Dec 2010 01:36:19 -0000 1.231 +++ kern/kern_exit.c 14 Feb 2011 12:25:28 -0000 @@ -343,6 +343,7 @@ exit1(struct lwp *l, int rv) if (p->p_lflag & PL_PPWAIT) { p->p_lflag &= ~PL_PPWAIT; cv_broadcast(&p->p_pptr->p_waitcv); + l->l_lwpctl = NULL; /* borrowed from parent */ } if (SESS_LEADER(p)) { Index: kern/kern_lwp.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_lwp.c,v retrieving revision 1.154 diff -p -u -r1.154 kern_lwp.c --- kern/kern_lwp.c 17 Jan 2011 08:26:58 -0000 1.154 +++ kern/kern_lwp.c 14 Feb 2011 12:25:29 -0000 @@ -696,6 +696,12 @@ lwp_create(lwp_t *l1, proc_t *p2, vaddr_ l2->l_pflag = LP_MPSAFE; TAILQ_INIT(&l2->l_ld_locks); + /* For vfork, borrow parent's lwpctl context */ + if (flags & LWP_VFORK && l1->l_lwpctl) { + l2->l_lwpctl = l1->l_lwpctl; + l2->l_flag |= LW_LWPCTL; + } + /* * If not the first LWP in the process, grab a reference to the * descriptor table. @@ -1376,6 +1382,17 @@ lwp_userret(struct lwp *l) KASSERT(0); /* NOTREACHED */ } + + /* update lwpctl process (for vfork child_return) */ + if (l->l_flag & LW_LWPCTL) { + lwp_lock(l); + l->l_flag &= ~LW_LWPCTL; + lwp_unlock(l); + KPREEMPT_DISABLE(l); + l->l_lwpctl->lc_curcpu = (int)cpu_index(l->l_cpu); + l->l_lwpctl->lc_pctr++; + KPREEMPT_ENABLE(l); + } } #ifdef KERN_SA @@ -1529,6 +1546,10 @@ lwp_ctl_alloc(vaddr_t *uaddr) l = curlwp; p = l->l_proc; + /* don't allow a vforked process to create lwp ctls */ + if (p->p_lflag & PL_PPWAIT) + return EBUSY; + if (l->l_lcpage != NULL) { lcp = l->l_lcpage; *uaddr = lcp->lcp_uaddr + (vaddr_t)l->l_lwpctl - lcp->lcp_kaddr; @@ -1653,11 +1674,16 @@ lwp_ctl_alloc(vaddr_t *uaddr) void lwp_ctl_free(lwp_t *l) { + struct proc *p = l->l_proc; lcproc_t *lp; lcpage_t *lcp; u_int map, offset; - lp = l->l_proc->p_lwpctl; + /* don't free a lwp context we borrowed for vfork */ + if (p->p_lflag & PL_PPWAIT) + return; + + lp = p->p_lwpctl; KASSERT(lp != NULL); lcp = l->l_lcpage; Index: sys/lwp.h =================================================================== RCS file: /cvsroot/src/sys/sys/lwp.h,v retrieving revision 1.142 diff -p -u -r1.142 lwp.h --- sys/lwp.h 28 Jan 2011 16:58:27 -0000 1.142 +++ sys/lwp.h 14 Feb 2011 12:25:29 -0000 @@ -214,6 +214,7 @@ extern lwp_t lwp0; /* LWP for proc0. * /* These flags are kept in l_flag. */ #define LW_IDLE 0x00000001 /* Idle lwp. */ +#define LW_LWPCTL 0x00000002 /* Adjust lwpctl in userret */ #define LW_SINTR 0x00000080 /* Sleep is interruptible. */ #define LW_SA_SWITCHING 0x00000100 /* SA LWP in context switch */ #define LW_SYSTEM 0x00000200 /* Kernel thread */ @@ -258,7 +259,7 @@ extern lwp_t lwp0; /* LWP for proc0. * * user. */ #define LW_USERRET (LW_WEXIT|LW_PENDSIG|LW_WREBOOT|LW_WSUSPEND|LW_WCORE|\ - LW_SA_BLOCKING|LW_SA_UPCALL) + LW_SA_BLOCKING|LW_SA_UPCALL|LW_LWPCTL) /* * Status values.