Date: Mon, 22 Feb 2010 01:40:42 GMT From: Gleb Kurtsou <gk@FreeBSD.org> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/144194: [patch] linuxulator: 2 exec bug fixes Message-ID: <201002220140.o1M1egIZ012011@www.freebsd.org> Resent-Message-ID: <201002220150.o1M1o3S7020272@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>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@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:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201002220140.o1M1egIZ012011>