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