kern/144194: [patch] linuxulator: 2 exec bug fixes
Gleb Kurtsou
gk at FreeBSD.org
Mon Feb 22 01:50:03 UTC 2010
>Number: 144194
>Category: kern
>Synopsis: [patch] linuxulator: 2 exec bug fixes
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Mon Feb 22 01:50:02 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator: Gleb Kurtsou
>Release:
>Organization:
>Environment:
FreeBSD tops 9.0-CURRENT FreeBSD 9.0-CURRENT #17 r203556+e02bf32: Mon Feb 22 03:04:35 EET 2010 root at tops:/usr/obj/freebsd-src/local/sys/TOPS amd64
>Description:
1. After calling exec() in multithreaded linux program threads are not destroyed and continue running. They get killed after program being executed finishes
2. linux_exit_group doesn't return correct exit code when called from not from group leader. Which happens regularly using sun jvm. I've changed exit1() to allow process_exit event handler to change p->p_xstat, just like NetBSD does.
There's another PR for this bug: kern/141439
But in that PR doesn't kill group leader, it works because sun jvm calls exit_group once again. Expected behavior for exit_group is not to return to userspace.
Submitting it as a single PR because second patch (linux-exit-group-status-code.patch.txt) relays on the first one
>How-To-Repeat:
>Fix:
Patch attached with submission follows:
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# linux-exec-kill-threads.patch.txt
# linux-exit-group-status-code.patch.txt
#
echo x - linux-exec-kill-threads.patch.txt
sed 's/^X//' >linux-exec-kill-threads.patch.txt << 'ef694d85587f5b6ee852ea1905df69a1'
Xdiff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c
Xindex dc81553..4da2f33 100644
X--- a/sys/compat/linux/linux_emul.c
X+++ b/sys/compat/linux/linux_emul.c
X@@ -257,6 +257,9 @@ linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
X if (__predict_false(imgp->sysent == &elf_linux_sysvec
X && p->p_sysent != &elf_linux_sysvec))
X linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0);
X+ if (__predict_false(p->p_sysent == &elf_linux_sysvec))
X+ /* Kill threads regerdless of imgp->sysent value */
X+ linux_kill_threads(FIRST_THREAD_IN_PROC(p), SIGKILL);
X if (__predict_false(imgp->sysent != &elf_linux_sysvec
X && p->p_sysent == &elf_linux_sysvec)) {
X struct linux_emuldata *em;
X@@ -334,3 +337,29 @@ linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args
X EMUL_UNLOCK(&emul_lock);
X return 0;
X }
X+
X+void
X+linux_kill_threads(struct thread *td, int sig)
X+{
X+ struct linux_emuldata *em, *td_em, *tmp_em;
X+ struct proc *sp;
X+
X+ td_em = em_find(td->td_proc, EMUL_DONTLOCK);
X+
X+ KASSERT(td_em != NULL, ("linux_kill_threads: emuldata not found.\n"));
X+
X+ EMUL_SHARED_RLOCK(&emul_shared_lock);
X+ LIST_FOREACH_SAFE(em, &td_em->shared->threads, threads, tmp_em) {
X+ if (em->pid == td_em->pid)
X+ continue;
X+
X+ sp = pfind(em->pid);
X+ if ((sp->p_flag & P_WEXIT) == 0)
X+ psignal(sp, sig);
X+ PROC_UNLOCK(sp);
X+#ifdef DEBUG
X+ printf(LMSG("linux_kill_threads: kill PID %d\n"), em->pid);
X+#endif
X+ }
X+ EMUL_SHARED_RUNLOCK(&emul_shared_lock);
X+}
Xdiff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h
Xindex 8ce27d7..a46a262 100644
X--- a/sys/compat/linux/linux_emul.h
X+++ b/sys/compat/linux/linux_emul.h
X@@ -76,6 +76,7 @@ int linux_proc_init(struct thread *, pid_t, int);
X void linux_proc_exit(void *, struct proc *);
X void linux_schedtail(void *, struct proc *);
X void linux_proc_exec(void *, struct proc *, struct image_params *);
X+void linux_kill_threads(struct thread *, int);
X
X extern struct sx emul_shared_lock;
X extern struct mtx emul_lock;
Xdiff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
Xindex d2cf6b6..79e9c2b 100644
X--- a/sys/compat/linux/linux_misc.c
X+++ b/sys/compat/linux/linux_misc.c
X@@ -1695,34 +1695,15 @@ linux_setdomainname(struct thread *td, struct linux_setdomainname_args *args)
X int
X linux_exit_group(struct thread *td, struct linux_exit_group_args *args)
X {
X- struct linux_emuldata *em, *td_em, *tmp_em;
X- struct proc *sp;
X
X #ifdef DEBUG
X if (ldebug(exit_group))
X printf(ARGS(exit_group, "%i"), args->error_code);
X #endif
X
X- if (linux_use26(td)) {
X- td_em = em_find(td->td_proc, EMUL_DONTLOCK);
X-
X- KASSERT(td_em != NULL, ("exit_group: emuldata not found.\n"));
X-
X- EMUL_SHARED_RLOCK(&emul_shared_lock);
X- LIST_FOREACH_SAFE(em, &td_em->shared->threads, threads, tmp_em) {
X- if (em->pid == td_em->pid)
X- continue;
X-
X- sp = pfind(em->pid);
X- psignal(sp, SIGKILL);
X- PROC_UNLOCK(sp);
X-#ifdef DEBUG
X- printf(LMSG("linux_sys_exit_group: kill PID %d\n"), em->pid);
X-#endif
X- }
X+ if (linux_use26(td))
X+ linux_kill_threads(td, SIGKILL);
X
X- EMUL_SHARED_RUNLOCK(&emul_shared_lock);
X- }
X /*
X * XXX: we should send a signal to the parent if
X * SIGNAL_EXIT_GROUP is set. We ignore that (temporarily?)
ef694d85587f5b6ee852ea1905df69a1
echo x - linux-exit-group-status-code.patch.txt
sed 's/^X//' >linux-exit-group-status-code.patch.txt << '844c33058a401cb53a9012cb32028df6'
Xdiff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c
Xindex 4da2f33..c8c150b 100644
X--- a/sys/compat/linux/linux_emul.c
X+++ b/sys/compat/linux/linux_emul.c
X@@ -157,6 +157,7 @@ linux_proc_exit(void *arg __unused, struct proc *p)
X struct linux_emuldata *em;
X int error;
X struct thread *td = FIRST_THREAD_IN_PROC(p);
X+ int shared_flags, shared_xstat;
X int *child_clear_tid;
X struct proc *q, *nq;
X
X@@ -187,6 +188,8 @@ linux_proc_exit(void *arg __unused, struct proc *p)
X }
X
X EMUL_SHARED_WLOCK(&emul_shared_lock);
X+ shared_flags = em->shared->flags;
X+ shared_xstat = em->shared->xstat;
X LIST_REMOVE(em, threads);
X
X em->shared->refs--;
X@@ -196,6 +199,12 @@ linux_proc_exit(void *arg __unused, struct proc *p)
X } else
X EMUL_SHARED_WUNLOCK(&emul_shared_lock);
X
X+ if ((shared_flags & EMUL_SHARED_HASXSTAT) != 0) {
X+ PROC_LOCK(p);
X+ p->p_xstat = shared_xstat;
X+ PROC_UNLOCK(p);
X+ }
X+
X if (child_clear_tid != NULL) {
X struct linux_sys_futex_args cup;
X int null = 0;
Xdiff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h
Xindex a46a262..47a6989 100644
X--- a/sys/compat/linux/linux_emul.h
X+++ b/sys/compat/linux/linux_emul.h
X@@ -31,11 +31,16 @@
X #ifndef _LINUX_EMUL_H_
X #define _LINUX_EMUL_H_
X
X+#define EMUL_SHARED_HASXSTAT 0x01
X+
X struct linux_emuldata_shared {
X int refs;
X pid_t group_pid;
X
X LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */
X+
X+ int flags;
X+ int xstat;
X };
X
X /*
Xdiff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
Xindex 79e9c2b..c50bf1c 100644
X--- a/sys/compat/linux/linux_misc.c
X+++ b/sys/compat/linux/linux_misc.c
X@@ -1695,14 +1695,22 @@ linux_setdomainname(struct thread *td, struct linux_setdomainname_args *args)
X int
X linux_exit_group(struct thread *td, struct linux_exit_group_args *args)
X {
X+ struct linux_emuldata *em;
X
X #ifdef DEBUG
X if (ldebug(exit_group))
X printf(ARGS(exit_group, "%i"), args->error_code);
X #endif
X
X- if (linux_use26(td))
X- linux_kill_threads(td, SIGKILL);
X+ em = em_find(td->td_proc, EMUL_DONTLOCK);
X+ if (em->shared->refs > 1) {
X+ EMUL_SHARED_WLOCK(&emul_shared_lock);
X+ em->shared->flags |= EMUL_SHARED_HASXSTAT;
X+ em->shared->xstat = W_EXITCODE(args->error_code, 0);
X+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
X+ if (linux_use26(td))
X+ linux_kill_threads(td, SIGKILL);
X+ }
X
X /*
X * XXX: we should send a signal to the parent if
Xdiff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
Xindex af00f42..9c27e75 100644
X--- a/sys/kern/kern_exit.c
X+++ b/sys/kern/kern_exit.c
X@@ -204,6 +204,7 @@ exit1(struct thread *td, int rv)
X while (p->p_lock > 0)
X msleep(&p->p_lock, &p->p_mtx, PWAIT, "exithold", 0);
X
X+ p->p_xstat = rv; /* Let event handler change exit status */
X PROC_UNLOCK(p);
X /* Drain the limit callout while we don't have the proc locked */
X callout_drain(&p->p_limco);
X@@ -246,6 +247,7 @@ exit1(struct thread *td, int rv)
X * P_PPWAIT is set; we will wakeup the parent below.
X */
X PROC_LOCK(p);
X+ rv = p->p_xstat; /* Event handler could change exit status */
X stopprofclock(p);
X p->p_flag &= ~(P_TRACED | P_PPWAIT);
X
X@@ -452,7 +454,6 @@ exit1(struct thread *td, int rv)
X
X /* Save exit status. */
X PROC_LOCK(p);
X- p->p_xstat = rv;
X p->p_xthread = td;
X
X /* Tell the prison that we are gone. */
844c33058a401cb53a9012cb32028df6
exit
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list