svn commit: r311886 - head/libexec/rtld-elf

Konstantin Belousov kib at FreeBSD.org
Tue Jan 10 19:26:56 UTC 2017


Author: kib
Date: Tue Jan 10 19:26:55 2017
New Revision: 311886
URL: https://svnweb.freebsd.org/changeset/base/311886

Log:
  Fix acquisition of nested write compat rtld locks.
  
  Obtaining compat rtld lock in write mode sets process signal mask to
  block all signals.  Previous mask is stored in the global variable
  oldsigmask.  If a lock is write-locked while another lock is already
  write-locked, oldsigmask is overwritten by the total mask and on the
  last unlock, all signals except traps appear to be blocked.
  
  Fix this by counting the write-lock nested level, and only storing to
  oldsigmask/restoring from it at the outermost level.
  
  Masking signals disables involuntary preemption for libc_r, and there
  could be no voluntary context switches in the locked code
  (dl_iterate_phdr(3) keeps a lock around user callback, but it was
  added long after libc_r was renounced).  Due to this, remembering the
  level in the global variable after the lock is obtained should be
  safe, because no two libc_r threads can acquire different write locks
  in parallel.
  
  PR:	215826
  Reported by:	kami
  Tested by:	yamagi at yamagi.org (previous version)
  To be reviewed by:	kan
  Sponsored by:	The FreeBSD Foundation
  MFC after:	2 weeks

Modified:
  head/libexec/rtld-elf/rtld_lock.c

Modified: head/libexec/rtld-elf/rtld_lock.c
==============================================================================
--- head/libexec/rtld-elf/rtld_lock.c	Tue Jan 10 19:16:50 2017	(r311885)
+++ head/libexec/rtld-elf/rtld_lock.c	Tue Jan 10 19:26:55 2017	(r311886)
@@ -64,7 +64,7 @@ typedef struct Struct_Lock {
 } Lock;
 
 static sigset_t fullsigmask, oldsigmask;
-static int thread_flag;
+static int thread_flag, wnested;
 
 static void *
 def_lock_create(void)
@@ -117,29 +117,34 @@ def_rlock_acquire(void *lock)
 static void
 def_wlock_acquire(void *lock)
 {
-    Lock *l = (Lock *)lock;
-    sigset_t tmp_oldsigmask;
+	Lock *l;
+	sigset_t tmp_oldsigmask;
 
-    for ( ; ; ) {
-	sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
-	if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG))
-	    break;
-	sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
-    }
-    oldsigmask = tmp_oldsigmask;
+	l = (Lock *)lock;
+	for (;;) {
+		sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
+		if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG))
+			break;
+		sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
+	}
+	if (atomic_fetchadd_int(&wnested, 1) == 0)
+		oldsigmask = tmp_oldsigmask;
 }
 
 static void
 def_lock_release(void *lock)
 {
-    Lock *l = (Lock *)lock;
+	Lock *l;
 
-    if ((l->lock & WAFLAG) == 0)
-    	atomic_add_rel_int(&l->lock, -RC_INCR);
-    else {
-    	atomic_add_rel_int(&l->lock, -WAFLAG);
-    	sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
-    }
+	l = (Lock *)lock;
+	if ((l->lock & WAFLAG) == 0)
+		atomic_add_rel_int(&l->lock, -RC_INCR);
+	else {
+		assert(wnested > 0);
+		atomic_add_rel_int(&l->lock, -WAFLAG);
+		if (atomic_fetchadd_int(&wnested, -1) == 1)
+			sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
+	}
 }
 
 static int
@@ -373,12 +378,12 @@ _rtld_atfork_pre(int *locks)
 		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.
+	 * Warning: this did not worked well with the rtld compat
+	 * locks above, when the thread signal mask was corrupted (set
+	 * to all signals blocked) if two locks were taken
+	 * simultaneously in the write mode.  The caller of the
+	 * _rtld_atfork_pre() must provide the working implementation
+	 * of the locks anyway, and libthr locks are fine.
 	 */
 	wlock_acquire(rtld_phdr_lock, &ls[0]);
 	wlock_acquire(rtld_bind_lock, &ls[1]);


More information about the svn-src-head mailing list