linux processes sharing VM space (via clone() call) must also

share same 'break' value used for brk()/sbrk(), otherwise application SIGSEGVs
quickly once different threads try to adjust data segment size

this fixes linux Mozilla crashes with SuSE 9.1 libraries, and possibly
other linux applications using real threads
This commit is contained in:
jdolecek 2004-08-08 09:40:50 +00:00
parent a37fba2a04
commit a714ac8294
3 changed files with 70 additions and 30 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: linux_emuldata.h,v 1.5 2003/06/23 17:29:25 erh Exp $ */ /* $NetBSD: linux_emuldata.h,v 1.6 2004/08/08 09:40:50 jdolecek Exp $ */
/*- /*-
* Copyright (c) 1998,2002 The NetBSD Foundation, Inc. * Copyright (c) 1998,2002 The NetBSD Foundation, Inc.
@ -45,12 +45,18 @@
* stored in the emuldata field of the proc * stored in the emuldata field of the proc
* structure. * structure.
*/ */
struct linux_emuldata_shared {
caddr_t p_break; /* Processes' idea of break */
int refs;
};
struct linux_emuldata { struct linux_emuldata {
#if notyet #if notyet
sigset_t ps_siginfo; /* Which signals have a RT handler */ sigset_t ps_siginfo; /* Which signals have a RT handler */
#endif #endif
int debugreg[8]; /* GDB information for ptrace - for use, */ int debugreg[8]; /* GDB information for ptrace - for use, */
/* see ../arch/i386/linux_ptrace.c */ /* see ../arch/i386/linux_ptrace.c */
caddr_t p_break; /* Processes' idea of break */ struct linux_emuldata_shared *s;
}; };
#endif /* !_COMMON_LINUX_EMULDATA_H */ #endif /* !_COMMON_LINUX_EMULDATA_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: linux_exec.c,v 1.70 2003/12/20 19:01:30 fvdl Exp $ */ /* $NetBSD: linux_exec.c,v 1.71 2004/08/08 09:40:50 jdolecek Exp $ */
/*- /*-
* Copyright (c) 1994, 1995, 1998, 2000 The NetBSD Foundation, Inc. * Copyright (c) 1994, 1995, 1998, 2000 The NetBSD Foundation, Inc.
@ -38,7 +38,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: linux_exec.c,v 1.70 2003/12/20 19:01:30 fvdl Exp $"); __KERNEL_RCSID(0, "$NetBSD: linux_exec.c,v 1.71 2004/08/08 09:40:50 jdolecek Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -75,9 +75,9 @@ extern const char * const linux_syscallnames[];
extern char linux_sigcode[], linux_esigcode[]; extern char linux_sigcode[], linux_esigcode[];
static void linux_e_proc_exec __P((struct proc *, struct exec_package *)); static void linux_e_proc_exec __P((struct proc *, struct exec_package *));
static void linux_e_proc_fork __P((struct proc *, struct proc *)); static void linux_e_proc_fork __P((struct proc *, struct proc *, int));
static void linux_e_proc_exit __P((struct proc *)); static void linux_e_proc_exit __P((struct proc *));
static void linux_e_proc_init __P((struct proc *, struct vmspace *)); static void linux_e_proc_init __P((struct proc *, struct proc *, int));
/* /*
* Execve(2). Just check the alternate emulation path, and pass it on * Execve(2). Just check the alternate emulation path, and pass it on
@ -147,25 +147,54 @@ const struct emul emul_linux = {
}; };
static void static void
linux_e_proc_init(p, vmspace) linux_e_proc_init(p, parent, forkflags)
struct proc *p; struct proc *p, *parent;
struct vmspace *vmspace; int forkflags;
{ {
if (!p->p_emuldata) { struct linux_emuldata *e = p->p_emuldata;
struct linux_emuldata_shared *s;
if (!e) {
/* allocate new Linux emuldata */ /* allocate new Linux emuldata */
MALLOC(p->p_emuldata, void *, sizeof(struct linux_emuldata), MALLOC(e, void *, sizeof(struct linux_emuldata),
M_EMULDATA, M_WAITOK); M_EMULDATA, M_WAITOK);
} else {
e->s->refs++;
if (e->s->refs == 0)
FREE(e->s, M_EMULDATA);
} }
memset(p->p_emuldata, '\0', sizeof(struct linux_emuldata)); memset(e, '\0', sizeof(struct linux_emuldata));
/* Set the process idea of the break to the real value */ if (forkflags & CLONE_VM) {
((struct linux_emuldata*)(p->p_emuldata))->p_break = struct linux_emuldata *e2 = parent->p_emuldata;
vmspace->vm_daddr + ctob(vmspace->vm_dsize); s = e2->s;
s->refs++;
} else {
struct vmspace *vm;
MALLOC(s, void *, sizeof(struct linux_emuldata_shared),
M_EMULDATA, M_WAITOK);
s->refs = 1;
/*
* Set the process idea of the break to the real value.
* For fork, we use parent's vmspace since our's
* is not setup at the time of this call and is going
* to be copy of parent's anyway. For exec, just
* use our own vmspace.
*/
vm = (parent) ? parent->p_vmspace : p->p_vmspace;
s->p_break = vm->vm_daddr + ctob(vm->vm_dsize);
}
e->s = s;
p->p_emuldata = e;
} }
/* /*
* Allocate per-process structures. Called when executing Linux * Allocate new per-process structures. Called when executing Linux
* process. We can reuse the old emuldata - if it's not null, * process. We can reuse the old emuldata - if it's not null,
* the executed process is of same emulation as original forked one. * the executed process is of same emulation as original forked one.
*/ */
@ -175,7 +204,7 @@ linux_e_proc_exec(p, epp)
struct exec_package *epp; struct exec_package *epp;
{ {
/* exec, use our vmspace */ /* exec, use our vmspace */
linux_e_proc_init(p, p->p_vmspace); linux_e_proc_init(p, NULL, 0);
} }
/* /*
@ -185,8 +214,13 @@ static void
linux_e_proc_exit(p) linux_e_proc_exit(p)
struct proc *p; struct proc *p;
{ {
struct linux_emuldata *e = p->p_emuldata;
/* free Linux emuldata and set the pointer to null */ /* free Linux emuldata and set the pointer to null */
FREE(p->p_emuldata, M_EMULDATA); e->s->refs--;
if (e->s->refs == 0)
FREE(e->s, M_EMULDATA);
FREE(e, M_EMULDATA);
p->p_emuldata = NULL; p->p_emuldata = NULL;
} }
@ -194,16 +228,16 @@ linux_e_proc_exit(p)
* Emulation fork hook. * Emulation fork hook.
*/ */
static void static void
linux_e_proc_fork(p, parent) linux_e_proc_fork(p, parent, forkflags)
struct proc *p, *parent; struct proc *p, *parent;
int forkflags;
{ {
/* /*
* It could be desirable to copy some stuff from parent's * The new process might share some vmspace-related stuff
* emuldata. We don't need anything like that for now. * with parent, depending on fork flags (CLONE_VM et.al).
* So just allocate new emuldata for the new process. * Force allocation of new base emuldata, and share the
* VM-related parts only if necessary.
*/ */
p->p_emuldata = NULL; p->p_emuldata = NULL;
linux_e_proc_init(p, parent, forkflags);
/* fork, use parent's vmspace (our vmspace may not be setup yet) */
linux_e_proc_init(p, parent->p_vmspace);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: linux_misc.c,v 1.126 2004/08/01 22:44:10 jdolecek Exp $ */ /* $NetBSD: linux_misc.c,v 1.127 2004/08/08 09:40:50 jdolecek Exp $ */
/*- /*-
* Copyright (c) 1995, 1998, 1999 The NetBSD Foundation, Inc. * Copyright (c) 1995, 1998, 1999 The NetBSD Foundation, Inc.
@ -64,7 +64,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: linux_misc.c,v 1.126 2004/08/01 22:44:10 jdolecek Exp $"); __KERNEL_RCSID(0, "$NetBSD: linux_misc.c,v 1.127 2004/08/08 09:40:50 jdolecek Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -283,9 +283,9 @@ linux_sys_brk(l, v, retval)
SCARG(&oba, nsize) = nbrk; SCARG(&oba, nsize) = nbrk;
if ((caddr_t) nbrk > vm->vm_daddr && sys_obreak(l, &oba, retval) == 0) if ((caddr_t) nbrk > vm->vm_daddr && sys_obreak(l, &oba, retval) == 0)
ed->p_break = (char*)nbrk; ed->s->p_break = (char*)nbrk;
else else
nbrk = ed->p_break; nbrk = ed->s->p_break;
retval[0] = (register_t)nbrk; retval[0] = (register_t)nbrk;