Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 24 May 2014 10:23:06 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r266609 - in head: lib/libthr/thread libexec/rtld-elf
Message-ID:  <201405241023.s4OAN6BF014684@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Sat May 24 10:23:06 2014
New Revision: 266609
URL: http://svnweb.freebsd.org/changeset/base/266609

Log:
  Right now, the rtld prefork hook locks the rtld bind lock in the read
  mode.  This allows the binder to be functional in the child after the
  fork (assuming no lazy loading of a filter is needed), but other rtld
  services which require write lock on rtld_bind_lock cause deadlock, if
  called by child.
  
  Change the _rtld_atfork() to lock the bind lock in write mode, making
  the rtld fully functional after the fork.
  
  Pre-resolve the symbols which are called by the libthr' fork()
  interposer, since dynamic resolution causes deadlock due to the
  rtld_bind_lock already owned in the write mode.
  
  Reported and tested by:	pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	2 weeks

Modified:
  head/lib/libthr/thread/thr_fork.c
  head/lib/libthr/thread/thr_rtld.c
  head/libexec/rtld-elf/rtld_lock.c

Modified: head/lib/libthr/thread/thr_fork.c
==============================================================================
--- head/lib/libthr/thread/thr_fork.c	Sat May 24 10:20:56 2014	(r266608)
+++ head/lib/libthr/thread/thr_fork.c	Sat May 24 10:23:06 2014	(r266609)
@@ -57,6 +57,7 @@
  *
  */
 
+#include <sys/syscall.h>
 #include "namespace.h"
 #include <errno.h>
 #include <link.h>
@@ -174,8 +175,15 @@ _fork(void)
 		was_threaded = 0;
 	}
 
-	/* Fork a new process: */
-	if ((ret = __sys_fork()) == 0) {
+	/*
+	 * Fork a new process.
+	 * There is no easy way to pre-resolve the __sys_fork symbol
+	 * without performing the fork.  Use the syscall(2)
+	 * indirection, the syscall symbol is resolved in
+	 * _thr_rtld_init() with side-effect free call.
+	 */
+	ret = syscall(SYS_fork);
+	if (ret == 0) {
 		/* Child process */
 		errsave = errno;
 		curthread->cancel_pending = 0;
@@ -250,6 +258,5 @@ _fork(void)
 	}
 	errno = errsave;
 
-	/* Return the process ID: */
 	return (ret);
 }

Modified: head/lib/libthr/thread/thr_rtld.c
==============================================================================
--- head/lib/libthr/thread/thr_rtld.c	Sat May 24 10:20:56 2014	(r266608)
+++ head/lib/libthr/thread/thr_rtld.c	Sat May 24 10:23:06 2014	(r266609)
@@ -32,10 +32,12 @@
   */
 #include <sys/cdefs.h>
 #include <sys/mman.h>
+#include <sys/syscall.h>
 #include <link.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "libc_private.h"
 #include "rtld_lock.h"
 #include "thr_private.h"
 
@@ -207,7 +209,24 @@ _thr_rtld_init(void)
 	li.thread_set_flag = _thr_rtld_set_flag;
 	li.thread_clr_flag = _thr_rtld_clr_flag;
 	li.at_fork = NULL;
-	
+
+	/*
+	 * Preresolve the symbols needed for the fork interposer.  We
+	 * call _rtld_atfork_pre() and _rtld_atfork_post() with NULL
+	 * argument to indicate that no actual locking inside the
+	 * functions should happen.  Neither rtld compat locks nor
+	 * libthr rtld locks cannot work there:
+	 * - compat locks do not handle the case of two locks taken
+	 *   in write mode (the signal mask for the thread is corrupted);
+	 * - libthr locks would work, but locked rtld_bind_lock prevents
+	 *   symbol resolution for _rtld_atfork_post.
+	 */
+	_rtld_atfork_pre(NULL);
+	_rtld_atfork_post(NULL);
+	_malloc_prefork();
+	_malloc_postfork();
+	syscall(SYS_getpid);
+
 	/* mask signals, also force to resolve __sys_sigprocmask PLT */
 	_thr_signal_block(curthread);
 	_rtld_thread_init(&li);

Modified: head/libexec/rtld-elf/rtld_lock.c
==============================================================================
--- head/libexec/rtld-elf/rtld_lock.c	Sat May 24 10:20:56 2014	(r266608)
+++ head/libexec/rtld-elf/rtld_lock.c	Sat May 24 10:23:06 2014	(r266609)
@@ -365,8 +365,19 @@ _rtld_atfork_pre(int *locks)
 {
 	RtldLockState ls[2];
 
+	if (locks == NULL)
+		return;
+
+	/*
+	 * Warning: this does not work with the rtld compat locks
+	 * above, since the thread signal mask is corrupted (set to
+	 * all signals blocked) if two locks are taken in write mode.
+	 * The caller of the _rtld_atfork_pre() must provide the
+	 * working implementation of the locks, and libthr locks are
+	 * fine.
+	 */
 	wlock_acquire(rtld_phdr_lock, &ls[0]);
-	rlock_acquire(rtld_bind_lock, &ls[1]);
+	wlock_acquire(rtld_bind_lock, &ls[1]);
 
 	/* XXXKIB: I am really sorry for this. */
 	locks[0] = ls[1].lockstate;
@@ -378,6 +389,9 @@ _rtld_atfork_post(int *locks)
 {
 	RtldLockState ls[2];
 
+	if (locks == NULL)
+		return;
+
 	bzero(ls, sizeof(ls));
 	ls[0].lockstate = locks[2];
 	ls[1].lockstate = locks[0];



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405241023.s4OAN6BF014684>