This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch release/2.25/master updated. glibc-2.25-49-gbc5ace6


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, release/2.25/master has been updated
       via  bc5ace67fe9823757532e0273f6c1cdfda065433 (commit)
       via  4b3a995ad64bed54baf544281596a785d18789f6 (commit)
       via  92a0e0c6175cdc8d46d2f6efb51c62bd81bf897e (commit)
      from  02aaa3c749bf18a3dbafff4c1f0180f135cad7ed (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=bc5ace67fe9823757532e0273f6c1cdfda065433

commit bc5ace67fe9823757532e0273f6c1cdfda065433
Author: Carlos O'Donell <carlos@redhat.com>
Date:   Sat Jul 29 00:02:03 2017 -0400

    mutex: Fix robust mutex lock acquire (Bug 21778)
    
    65810f0ef05e8c9e333f17a44e77808b163ca298 fixed a robust mutex bug but
    introduced BZ 21778: if the CAS used to try to acquire a lock fails, the
    expected value is not updated, which breaks other cases in the loce
    acquisition loop.  The fix is to simply update the expected value with
    the value returned by the CAS, which ensures that behavior is as if the
    first case with the CAS never happened (if the CAS fails).
    
    This is a regression introduced in the last release.
    
    Tested on x86_64, i686, ppc64, ppc64le, s390x, aarch64, armv7hl.
    
    (cherry picked from commit 5920a4a624b1f4db310d1c44997b640e2a4653e5)

diff --git a/ChangeLog b/ChangeLog
index 01298da..13415f9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2017-07-29  Torvald Riegel  <triegel@redhat.com>
+	    Carlos O'Donell  <carlos@redhat.com>
+
+	[BZ 21778]
+	* nptl/pthread_mutex_timedlock.c (__pthread_mutex_timedlock): Update
+	oldval if the CAS fails.
+	* nptl/pthread_mutex_lock.c (__pthread_mutex_lock_full): Likewise.
+	* nptl/tst-mutex7.c: Add comments explaining template test.
+	(ROBUST, DELAY_NSEC, ROUNDS, N): New.
+	(tf, do_test): Use them.
+	* nptl/tst-mutex7robust.c: New file.
+	* nptl/Makefile (tests): Add new test.
+
 2017-07-28  Torvald Riegel  <triegel@redhat.com>
 	    Carlos O'Donell  <carlos@redhat.com>
 
diff --git a/NEWS b/NEWS
index 6963f33..f705771 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,7 @@ The following bugs are resolved with this release:
   [21298] rwlock can deadlock on frequent reader/writer phase switching
   [21386] Assertion in fork for distinct parent PID is incorrect
   [21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366)
+  [21778] Robust mutex may deadlock
   [21972] assert macro requires operator== (int) for its argument type
 
 Version 2.25
diff --git a/nptl/Makefile b/nptl/Makefile
index 0cc2873..2406776 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -224,6 +224,7 @@ tests = tst-typesizes \
 	tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 	tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \
 	tst-mutex7 tst-mutex8 tst-mutex9 tst-mutex5a tst-mutex7a \
+	tst-mutex7robust \
 	tst-mutexpi1 tst-mutexpi2 tst-mutexpi3 tst-mutexpi4 tst-mutexpi5 \
 	tst-mutexpi5a tst-mutexpi6 tst-mutexpi7 tst-mutexpi7a tst-mutexpi8 \
 	tst-mutexpi9 \
diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c
index dc9ca4c..4425927 100644
--- a/nptl/pthread_mutex_lock.c
+++ b/nptl/pthread_mutex_lock.c
@@ -197,11 +197,14 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
 	{
 	  /* Try to acquire the lock through a CAS from 0 (not acquired) to
 	     our TID | assume_other_futex_waiters.  */
-	  if (__glibc_likely ((oldval == 0)
-			      && (atomic_compare_and_exchange_bool_acq
-				  (&mutex->__data.__lock,
-				   id | assume_other_futex_waiters, 0) == 0)))
-	      break;
+	  if (__glibc_likely (oldval == 0))
+	    {
+	      oldval
+	        = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
+	            id | assume_other_futex_waiters, 0);
+	      if (__glibc_likely (oldval == 0))
+		break;
+	    }
 
 	  if ((oldval & FUTEX_OWNER_DIED) != 0)
 	    {
diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c
index a4beb7b..dd88cc4 100644
--- a/nptl/pthread_mutex_timedlock.c
+++ b/nptl/pthread_mutex_timedlock.c
@@ -154,11 +154,14 @@ pthread_mutex_timedlock (pthread_mutex_t *mutex,
 	{
 	  /* Try to acquire the lock through a CAS from 0 (not acquired) to
 	     our TID | assume_other_futex_waiters.  */
-	  if (__glibc_likely ((oldval == 0)
-			      && (atomic_compare_and_exchange_bool_acq
-				  (&mutex->__data.__lock,
-				   id | assume_other_futex_waiters, 0) == 0)))
-	      break;
+	  if (__glibc_likely (oldval == 0))
+	    {
+	      oldval
+	        = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
+	            id | assume_other_futex_waiters, 0);
+	      if (__glibc_likely (oldval == 0))
+		break;
+	    }
 
 	  if ((oldval & FUTEX_OWNER_DIED) != 0)
 	    {
diff --git a/nptl/tst-mutex7.c b/nptl/tst-mutex7.c
index a11afdb..08fe251 100644
--- a/nptl/tst-mutex7.c
+++ b/nptl/tst-mutex7.c
@@ -22,25 +22,41 @@
 #include <stdlib.h>
 #include <time.h>
 
-
+/* This test is a template for other tests to use.  Other tests define
+   the following macros to change the behaviour of the template test.
+   The test is very simple, it configures N threads given the parameters
+   below and then proceeds to go through mutex lock and unlock
+   operations in each thread as described before for the thread
+   function.  */
 #ifndef TYPE
 # define TYPE PTHREAD_MUTEX_DEFAULT
 #endif
-
+#ifndef ROBUST
+# define ROBUST PTHREAD_MUTEX_STALLED
+#endif
+#ifndef DELAY_NSEC
+# define DELAY_NSEC 11000
+#endif
+#ifndef ROUNDS
+# define ROUNDS 1000
+#endif
+#ifndef N
+# define N 100
+#endif
 
 static pthread_mutex_t lock;
 
-
-#define ROUNDS 1000
-#define N 100
-
-
+/* Each thread locks and the subsequently unlocks the lock, yielding
+   the smallest critical section possible.  After the unlock the thread
+   waits DELAY_NSEC nanoseconds before doing the lock and unlock again.
+   Every thread does this ROUNDS times.  The lock and unlock are
+   checked for errors.  */
 static void *
 tf (void *arg)
 {
   int nr = (long int) arg;
   int cnt;
-  struct timespec ts = { .tv_sec = 0, .tv_nsec = 11000 };
+  struct timespec ts = { .tv_sec = 0, .tv_nsec = DELAY_NSEC };
 
   for (cnt = 0; cnt < ROUNDS; ++cnt)
     {
@@ -56,13 +72,16 @@ tf (void *arg)
 	  return (void *) 1l;
 	}
 
-      nanosleep (&ts, NULL);
+      if ((ts.tv_sec > 0) || (ts.tv_nsec > 0))
+	nanosleep (&ts, NULL);
     }
 
   return NULL;
 }
 
-
+/* Setup and run N threads, where each thread does as described
+   in the above thread function.  The threads are given a minimal 1MiB
+   stack since they don't do anything between the lock and unlock.  */
 static int
 do_test (void)
 {
@@ -80,6 +99,12 @@ do_test (void)
       exit (1);
     }
 
+  if (pthread_mutexattr_setrobust (&a, ROBUST) != 0)
+    {
+      puts ("mutexattr_setrobust failed");
+      exit (1);
+    }
+
 #ifdef ENABLE_PI
   if (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT) != 0)
     {
diff --git a/nptl/tst-mutex7robust.c b/nptl/tst-mutex7robust.c
new file mode 100644
index 0000000..8221a61
--- /dev/null
+++ b/nptl/tst-mutex7robust.c
@@ -0,0 +1,7 @@
+/* Bug 21778: Fix oversight in robust mutex lock acquisition.  */
+#define TYPE PTHREAD_MUTEX_NORMAL
+#define ROBUST PTHREAD_MUTEX_ROBUST
+#define DELAY_NSEC 0
+#define ROUNDS 1000
+#define N 32
+#include "tst-mutex7.c"

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=4b3a995ad64bed54baf544281596a785d18789f6

commit 4b3a995ad64bed54baf544281596a785d18789f6
Author: Carlos O'Donell <carlos@systemhalted.org>
Date:   Mon Aug 28 15:04:39 2017 +0200

    rwlock: Fix explicit hand-over (bug 21298)
    
    Without this fix, the rwlock can fail to execute the explicit hand-over
    in certain cases (e.g., empty critical sections that switch quickly between
    read and write phases).  This can then lead to errors in how __wrphase_futex
    is accessed, which in turn can lead to deadlocks.
    
    (cherry picked from commit faf8c066df0d6bccb54bd74dd696eeb65e1b3bbc)

diff --git a/ChangeLog b/ChangeLog
index de780da..01298da 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2017-07-28  Torvald Riegel  <triegel@redhat.com>
+	    Carlos O'Donell  <carlos@redhat.com>
+
+	[BZ #21298]
+	* nptl/Makefile (tests): Add tst-rwlock20.
+	* nptl/pthread_rwlock_common.c (__pthread_rwlock_rdlock_full): Fix
+	explicit hand-over.
+	(__pthread_rwlock_wrlock_full): Likewise.
+	* nptl/tst-rwlock20.c: New file.
+
 2017-08-21  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #21972]
diff --git a/NEWS b/NEWS
index 6abe902..6963f33 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,7 @@ The following bugs are resolved with this release:
   [21209] Ignore and remove LD_HWCAP_MASK for AT_SECURE programs
   [21242] assert: Suppress pedantic warning caused by statement expression
   [21289] Fix symbol redirect for fts_set
+  [21298] rwlock can deadlock on frequent reader/writer phase switching
   [21386] Assertion in fork for distinct parent PID is incorrect
   [21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366)
   [21972] assert macro requires operator== (int) for its argument type
diff --git a/nptl/Makefile b/nptl/Makefile
index 6d48c0c..0cc2873 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -241,7 +241,7 @@ tests = tst-typesizes \
 	tst-rwlock4 tst-rwlock5 tst-rwlock6 tst-rwlock7 tst-rwlock8 \
 	tst-rwlock9 tst-rwlock10 tst-rwlock11 tst-rwlock12 tst-rwlock13 \
 	tst-rwlock14 tst-rwlock15 tst-rwlock16 tst-rwlock17 tst-rwlock18 \
-	tst-rwlock19 \
+	tst-rwlock19 tst-rwlock20 \
 	tst-once1 tst-once2 tst-once3 tst-once4 tst-once5 \
 	tst-key1 tst-key2 tst-key3 tst-key4 \
 	tst-sem1 tst-sem2 tst-sem3 tst-sem4 tst-sem5 tst-sem6 tst-sem7 \
diff --git a/nptl/pthread_rwlock_common.c b/nptl/pthread_rwlock_common.c
index 256508c..846687e 100644
--- a/nptl/pthread_rwlock_common.c
+++ b/nptl/pthread_rwlock_common.c
@@ -55,7 +55,6 @@
    lock acquisition attempts, so that new incoming readers do not prolong a
    phase in which readers have acquired the lock.
 
-
    The main components of the rwlock are a writer-only lock that allows only
    one of the concurrent writers to be the primary writer, and a
    single-writer-multiple-readers lock that decides between read phases, in
@@ -70,15 +69,16 @@
    ---------------------------
    #1    0   0   0   0   Lock is idle (and in a read phase).
    #2    0   0   >0  0   Readers have acquired the lock.
-   #3    0   1   0   0   Lock is not acquired; a writer is waiting for a write
-			 phase to start or will try to start one.
+   #3    0   1   0   0   Lock is not acquired; a writer will try to start a
+			 write phase.
    #4    0   1   >0  0   Readers have acquired the lock; a writer is waiting
 			 and explicit hand-over to the writer is required.
    #4a   0   1   >0  1   Same as #4 except that there are further readers
 			 waiting because the writer is to be preferred.
    #5    1   0   0   0   Lock is idle (and in a write phase).
-   #6    1   0   >0  0   Write phase; readers are waiting for a read phase to
-			 start or will try to start one.
+   #6    1   0   >0  0   Write phase; readers will try to start a read phase
+			 (requires explicit hand-over to all readers that
+			 do not start the read phase).
    #7    1   1   0   0   Lock is acquired by a writer.
    #8    1   1   >0  0   Lock acquired by a writer and readers are waiting;
 			 explicit hand-over to the readers is required.
@@ -375,9 +375,9 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
      complexity.  */
   if (__glibc_likely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
     return 0;
-
-  /* If there is no primary writer but we are in a write phase, we can try
-     to install a read phase ourself.  */
+  /* Otherwise, if we were in a write phase (states #6 or #8), we must wait
+     for explicit hand-over of the read phase; the only exception is if we
+     can start a read phase if there is no primary writer currently.  */
   while (((r & PTHREAD_RWLOCK_WRPHASE) != 0)
       && ((r & PTHREAD_RWLOCK_WRLOCKED) == 0))
     {
@@ -390,15 +390,18 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
 	{
 	  /* We started the read phase, so we are also responsible for
 	     updating the write-phase futex.  Relaxed MO is sufficient.
-	     Note that there can be no other reader that we have to wake
-	     because all other readers will see the read phase started by us
-	     (or they will try to start it themselves); if a writer started
-	     the read phase, we cannot have started it.  Furthermore, we
-	     cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
-	     overwrite the value set by the most recent writer (or the readers
-	     before it in case of explicit hand-over) and we know that there
-	     are no waiting readers.  */
-	  atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
+	     We have to do the same steps as a writer would when handing
+	     over the read phase to us because other readers cannot
+	     distinguish between us and the writer; this includes
+	     explicit hand-over and potentially having to wake other readers
+	     (but we can pretend to do the setting and unsetting of WRLOCKED
+	     atomically, and thus can skip this step).  */
+	  if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
+	      & PTHREAD_RWLOCK_FUTEX_USED) != 0)
+	    {
+	      int private = __pthread_rwlock_get_private (rwlock);
+	      futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
+	    }
 	  return 0;
 	}
       else
@@ -407,102 +410,98 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
 	}
     }
 
-  if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
+  /* We were in a write phase but did not install the read phase.  We cannot
+     distinguish between a writer and another reader starting the read phase,
+     so we must wait for explicit hand-over via __wrphase_futex.
+     However, __wrphase_futex might not have been set to 1 yet (either
+     because explicit hand-over to the writer is still ongoing, or because
+     the writer has started the write phase but has not yet updated
+     __wrphase_futex).  The least recent value of __wrphase_futex we can
+     read from here is the modification of the last read phase (because
+     we synchronize with the last reader in this read phase through
+     __readers; see the use of acquire MO on the fetch_add above).
+     Therefore, if we observe a value of 0 for __wrphase_futex, we need
+     to subsequently check that __readers now indicates a read phase; we
+     need to use acquire MO for this so that if we observe a read phase,
+     we will also see the modification of __wrphase_futex by the previous
+     writer.  We then need to load __wrphase_futex again and continue to
+     wait if it is not 0, so that we do not skip explicit hand-over.
+     Relaxed MO is sufficient for the load from __wrphase_futex because
+     we just use it as an indicator for when we can proceed; we use
+     __readers and the acquire MO accesses to it to eventually read from
+     the proper stores to __wrphase_futex.  */
+  unsigned int wpf;
+  bool ready = false;
+  for (;;)
     {
-      /* We are in a write phase, and there must be a primary writer because
-	 of the previous loop.  Block until the primary writer gives up the
-	 write phase.  This case requires explicit hand-over using
-	 __wrphase_futex.
-	 However, __wrphase_futex might not have been set to 1 yet (either
-	 because explicit hand-over to the writer is still ongoing, or because
-	 the writer has started the write phase but does not yet have updated
-	 __wrphase_futex).  The least recent value of __wrphase_futex we can
-	 read from here is the modification of the last read phase (because
-	 we synchronize with the last reader in this read phase through
-	 __readers; see the use of acquire MO on the fetch_add above).
-	 Therefore, if we observe a value of 0 for __wrphase_futex, we need
-	 to subsequently check that __readers now indicates a read phase; we
-	 need to use acquire MO for this so that if we observe a read phase,
-	 we will also see the modification of __wrphase_futex by the previous
-	 writer.  We then need to load __wrphase_futex again and continue to
-	 wait if it is not 0, so that we do not skip explicit hand-over.
-	 Relaxed MO is sufficient for the load from __wrphase_futex because
-	 we just use it as an indicator for when we can proceed; we use
-	 __readers and the acquire MO accesses to it to eventually read from
-	 the proper stores to __wrphase_futex.  */
-      unsigned int wpf;
-      bool ready = false;
-      for (;;)
+      while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
+	  | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
 	{
-	  while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
-	      | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
+	  int private = __pthread_rwlock_get_private (rwlock);
+	  if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
+	      && !atomic_compare_exchange_weak_relaxed
+		  (&rwlock->__data.__wrphase_futex,
+		   &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
+	    continue;
+	  int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
+	      1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
+	  if (err == ETIMEDOUT)
 	    {
-	      int private = __pthread_rwlock_get_private (rwlock);
-	      if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
-		  && !atomic_compare_exchange_weak_relaxed
-		      (&rwlock->__data.__wrphase_futex,
-		       &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
-		continue;
-	      int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
-		  1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
-	      if (err == ETIMEDOUT)
+	      /* If we timed out, we need to unregister.  If no read phase
+		 has been installed while we waited, we can just decrement
+		 the number of readers.  Otherwise, we just acquire the
+		 lock, which is allowed because we give no precise timing
+		 guarantees, and because the timeout is only required to
+		 be in effect if we would have had to wait for other
+		 threads (e.g., if futex_wait would time-out immediately
+		 because the given absolute time is in the past).  */
+	      r = atomic_load_relaxed (&rwlock->__data.__readers);
+	      while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
 		{
-		  /* If we timed out, we need to unregister.  If no read phase
-		     has been installed while we waited, we can just decrement
-		     the number of readers.  Otherwise, we just acquire the
-		     lock, which is allowed because we give no precise timing
-		     guarantees, and because the timeout is only required to
-		     be in effect if we would have had to wait for other
-		     threads (e.g., if futex_wait would time-out immediately
-		     because the given absolute time is in the past).  */
-		  r = atomic_load_relaxed (&rwlock->__data.__readers);
-		  while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
-		    {
-		      /* We don't need to make anything else visible to
-			 others besides unregistering, so relaxed MO is
-			 sufficient.  */
-		      if (atomic_compare_exchange_weak_relaxed
-			  (&rwlock->__data.__readers, &r,
-			   r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
-			return ETIMEDOUT;
-		      /* TODO Back-off.  */
-		    }
-		  /* Use the acquire MO fence to mirror the steps taken in the
-		     non-timeout case.  Note that the read can happen both
-		     in the atomic_load above as well as in the failure case
-		     of the CAS operation.  */
-		  atomic_thread_fence_acquire ();
-		  /* We still need to wait for explicit hand-over, but we must
-		     not use futex_wait anymore because we would just time out
-		     in this case and thus make the spin-waiting we need
-		     unnecessarily expensive.  */
-		  while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
-		      | PTHREAD_RWLOCK_FUTEX_USED)
-		      == (1 | PTHREAD_RWLOCK_FUTEX_USED))
-		    {
-		      /* TODO Back-off?  */
-		    }
-		  ready = true;
-		  break;
+		  /* We don't need to make anything else visible to
+		     others besides unregistering, so relaxed MO is
+		     sufficient.  */
+		  if (atomic_compare_exchange_weak_relaxed
+		      (&rwlock->__data.__readers, &r,
+		       r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
+		    return ETIMEDOUT;
+		  /* TODO Back-off.  */
 		}
-	      /* If we got interrupted (EINTR) or the futex word does not have the
-		 expected value (EAGAIN), retry.  */
+	      /* Use the acquire MO fence to mirror the steps taken in the
+		 non-timeout case.  Note that the read can happen both
+		 in the atomic_load above as well as in the failure case
+		 of the CAS operation.  */
+	      atomic_thread_fence_acquire ();
+	      /* We still need to wait for explicit hand-over, but we must
+		 not use futex_wait anymore because we would just time out
+		 in this case and thus make the spin-waiting we need
+		 unnecessarily expensive.  */
+	      while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
+		  | PTHREAD_RWLOCK_FUTEX_USED)
+		  == (1 | PTHREAD_RWLOCK_FUTEX_USED))
+		{
+		  /* TODO Back-off?  */
+		}
+	      ready = true;
+	      break;
 	    }
-	  if (ready)
-	    /* See below.  */
-	    break;
-	  /* We need acquire MO here so that we synchronize with the lock
-	     release of the writer, and so that we observe a recent value of
-	     __wrphase_futex (see below).  */
-	  if ((atomic_load_acquire (&rwlock->__data.__readers)
-	      & PTHREAD_RWLOCK_WRPHASE) == 0)
-	    /* We are in a read phase now, so the least recent modification of
-	       __wrphase_futex we can read from is the store by the writer
-	       with value 1.  Thus, only now we can assume that if we observe
-	       a value of 0, explicit hand-over is finished. Retry the loop
-	       above one more time.  */
-	    ready = true;
+	  /* If we got interrupted (EINTR) or the futex word does not have the
+	     expected value (EAGAIN), retry.  */
 	}
+      if (ready)
+	/* See below.  */
+	break;
+      /* We need acquire MO here so that we synchronize with the lock
+	 release of the writer, and so that we observe a recent value of
+	 __wrphase_futex (see below).  */
+      if ((atomic_load_acquire (&rwlock->__data.__readers)
+	  & PTHREAD_RWLOCK_WRPHASE) == 0)
+	/* We are in a read phase now, so the least recent modification of
+	   __wrphase_futex we can read from is the store by the writer
+	   with value 1.  Thus, only now we can assume that if we observe
+	   a value of 0, explicit hand-over is finished. Retry the loop
+	   above one more time.  */
+	ready = true;
     }
 
   return 0;
@@ -741,10 +740,23 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
 	  r = atomic_load_relaxed (&rwlock->__data.__readers);
 	}
       /* Our snapshot of __readers is up-to-date at this point because we
-	 either set WRLOCKED using a CAS or were handed over WRLOCKED from
+	 either set WRLOCKED using a CAS (and update r accordingly below,
+	 which was used as expected value for the CAS) or got WRLOCKED from
 	 another writer whose snapshot of __readers we inherit.  */
+      r |= PTHREAD_RWLOCK_WRLOCKED;
     }
 
+  /* We are the primary writer; enable blocking on __writers_futex.  Relaxed
+     MO is sufficient for futex words; acquire MO on the previous
+     modifications of __readers ensures that this store happens after the
+     store of value 0 by the previous primary writer.  */
+  atomic_store_relaxed (&rwlock->__data.__writers_futex,
+      1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
+
+  /* If we are in a write phase, we have acquired the lock.  */
+  if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
+    goto done;
+
   /* If we are in a read phase and there are no readers, try to start a write
      phase.  */
   while (((r & PTHREAD_RWLOCK_WRPHASE) == 0)
@@ -758,166 +770,156 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
 	  &r, r | PTHREAD_RWLOCK_WRPHASE))
 	{
 	  /* We have started a write phase, so need to enable readers to wait.
-	     See the similar case in__pthread_rwlock_rdlock_full.  */
+	     See the similar case in __pthread_rwlock_rdlock_full.  Unlike in
+	     that similar case, we are the (only) primary writer and so do
+	     not need to wake another writer.  */
 	  atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
-	  /* Make sure we fall through to the end of the function.  */
-	  r |= PTHREAD_RWLOCK_WRPHASE;
-	  break;
+
+	  goto done;
 	}
       /* TODO Back-off.  */
     }
 
-  /* We are the primary writer; enable blocking on __writers_futex.  Relaxed
-     MO is sufficient for futex words; acquire MO on the previous
-     modifications of __readers ensures that this store happens after the
-     store of value 0 by the previous primary writer.  */
-  atomic_store_relaxed (&rwlock->__data.__writers_futex,
-      1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
-
-  if (__glibc_unlikely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
+  /* We became the primary writer in a read phase and there were readers when
+     we did (because of the previous loop).  Thus, we have to wait for
+     explicit hand-over from one of these readers.
+     We basically do the same steps as for the similar case in
+     __pthread_rwlock_rdlock_full, except that we additionally might try
+     to directly hand over to another writer and need to wake up
+     other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING).  */
+  unsigned int wpf;
+  bool ready = false;
+  for (;;)
     {
-      /* We are not in a read phase and there are readers (because of the
-	 previous loop).  Thus, we have to wait for explicit hand-over from
-	 one of these readers.
-	 We basically do the same steps as for the similar case in
-	 __pthread_rwlock_rdlock_full, except that we additionally might try
-	 to directly hand over to another writer and need to wake up
-	 other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING).  */
-      unsigned int wpf;
-      bool ready = false;
-      for (;;)
+      while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
+	  | PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
 	{
-	  while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
-	      | PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
+	  int private = __pthread_rwlock_get_private (rwlock);
+	  if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
+	      && !atomic_compare_exchange_weak_relaxed
+		  (&rwlock->__data.__wrphase_futex, &wpf,
+		   PTHREAD_RWLOCK_FUTEX_USED))
+	    continue;
+	  int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
+	      PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
+	  if (err == ETIMEDOUT)
 	    {
-	      int private = __pthread_rwlock_get_private (rwlock);
-	      if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
-		  && !atomic_compare_exchange_weak_relaxed
-		      (&rwlock->__data.__wrphase_futex, &wpf,
-		       PTHREAD_RWLOCK_FUTEX_USED))
-		continue;
-	      int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
-		  PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
-	      if (err == ETIMEDOUT)
+	      if (rwlock->__data.__flags
+		  != PTHREAD_RWLOCK_PREFER_READER_NP)
 		{
-		  if (rwlock->__data.__flags
-		      != PTHREAD_RWLOCK_PREFER_READER_NP)
-		    {
-		      /* We try writer--writer hand-over.  */
-		      unsigned int w = atomic_load_relaxed
-			  (&rwlock->__data.__writers);
-		      if (w != 0)
-			{
-			  /* We are about to hand over WRLOCKED, so we must
-			     release __writers_futex too; otherwise, we'd have
-			     a pending store, which could at least prevent
-			     other threads from waiting using the futex
-			     because it could interleave with the stores
-			     by subsequent writers.  In turn, this means that
-			     we have to clean up when we do not hand over
-			     WRLOCKED.
-			     Release MO so that another writer that gets
-			     WRLOCKED from us can take over our view of
-			     __readers.  */
-			  unsigned int wf = atomic_exchange_relaxed
-			      (&rwlock->__data.__writers_futex, 0);
-			  while (w != 0)
-			    {
-			      if (atomic_compare_exchange_weak_release
-				  (&rwlock->__data.__writers, &w,
-				      w | PTHREAD_RWLOCK_WRHANDOVER))
-				{
-				  /* Wake other writers.  */
-				  if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
-				    futex_wake
-					(&rwlock->__data.__writers_futex, 1,
-					 private);
-				  return ETIMEDOUT;
-				}
-			      /* TODO Back-off.  */
-			    }
-			  /* We still own WRLOCKED and someone else might set
-			     a write phase concurrently, so enable waiting
-			     again.  Make sure we don't loose the flag that
-			     signals whether there are threads waiting on
-			     this futex.  */
-			  atomic_store_relaxed
-			      (&rwlock->__data.__writers_futex, wf);
-			}
-		    }
-		  /* If we timed out and we are not in a write phase, we can
-		     just stop being a primary writer.  Otherwise, we just
-		     acquire the lock.  */
-		  r = atomic_load_relaxed (&rwlock->__data.__readers);
-		  if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
+		  /* We try writer--writer hand-over.  */
+		  unsigned int w = atomic_load_relaxed
+		      (&rwlock->__data.__writers);
+		  if (w != 0)
 		    {
-		      /* We are about to release WRLOCKED, so we must release
-			 __writers_futex too; see the handling of
-			 writer--writer hand-over above.  */
+		      /* We are about to hand over WRLOCKED, so we must
+			 release __writers_futex too; otherwise, we'd have
+			 a pending store, which could at least prevent
+			 other threads from waiting using the futex
+			 because it could interleave with the stores
+			 by subsequent writers.  In turn, this means that
+			 we have to clean up when we do not hand over
+			 WRLOCKED.
+			 Release MO so that another writer that gets
+			 WRLOCKED from us can take over our view of
+			 __readers.  */
 		      unsigned int wf = atomic_exchange_relaxed
 			  (&rwlock->__data.__writers_futex, 0);
-		      while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
+		      while (w != 0)
 			{
-			  /* While we don't need to make anything from a
-			     caller's critical section visible to other
-			     threads, we need to ensure that our changes to
-			     __writers_futex are properly ordered.
-			     Therefore, use release MO to synchronize with
-			     subsequent primary writers.  Also wake up any
-			     waiting readers as they are waiting because of
-			     us.  */
 			  if (atomic_compare_exchange_weak_release
-			      (&rwlock->__data.__readers, &r,
-			       (r ^ PTHREAD_RWLOCK_WRLOCKED)
-			       & ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
+			      (&rwlock->__data.__writers, &w,
+				  w | PTHREAD_RWLOCK_WRHANDOVER))
 			    {
 			      /* Wake other writers.  */
 			      if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
 				futex_wake (&rwlock->__data.__writers_futex,
-				    1, private);
-			      /* Wake waiting readers.  */
-			      if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
-				futex_wake (&rwlock->__data.__readers,
-				    INT_MAX, private);
+					    1, private);
 			      return ETIMEDOUT;
 			    }
+			  /* TODO Back-off.  */
 			}
-		      /* We still own WRLOCKED and someone else might set a
-			 write phase concurrently, so enable waiting again.
-			 Make sure we don't loose the flag that signals
-			 whether there are threads waiting on this futex.  */
-		      atomic_store_relaxed (&rwlock->__data.__writers_futex,
-			  wf);
+		      /* We still own WRLOCKED and someone else might set
+			 a write phase concurrently, so enable waiting
+			 again.  Make sure we don't loose the flag that
+			 signals whether there are threads waiting on
+			 this futex.  */
+		      atomic_store_relaxed
+			  (&rwlock->__data.__writers_futex, wf);
 		    }
-		  /* Use the acquire MO fence to mirror the steps taken in the
-		     non-timeout case.  Note that the read can happen both
-		     in the atomic_load above as well as in the failure case
-		     of the CAS operation.  */
-		  atomic_thread_fence_acquire ();
-		  /* We still need to wait for explicit hand-over, but we must
-		     not use futex_wait anymore.  */
-		  while ((atomic_load_relaxed
-		      (&rwlock->__data.__wrphase_futex)
-		       | PTHREAD_RWLOCK_FUTEX_USED)
-		      == PTHREAD_RWLOCK_FUTEX_USED)
+		}
+	      /* If we timed out and we are not in a write phase, we can
+		 just stop being a primary writer.  Otherwise, we just
+		 acquire the lock.  */
+	      r = atomic_load_relaxed (&rwlock->__data.__readers);
+	      if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
+		{
+		  /* We are about to release WRLOCKED, so we must release
+		     __writers_futex too; see the handling of
+		     writer--writer hand-over above.  */
+		  unsigned int wf = atomic_exchange_relaxed
+		      (&rwlock->__data.__writers_futex, 0);
+		  while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
 		    {
-		      /* TODO Back-off.  */
+		      /* While we don't need to make anything from a
+			 caller's critical section visible to other
+			 threads, we need to ensure that our changes to
+			 __writers_futex are properly ordered.
+			 Therefore, use release MO to synchronize with
+			 subsequent primary writers.  Also wake up any
+			 waiting readers as they are waiting because of
+			 us.  */
+		      if (atomic_compare_exchange_weak_release
+			  (&rwlock->__data.__readers, &r,
+			   (r ^ PTHREAD_RWLOCK_WRLOCKED)
+			   & ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
+			{
+			  /* Wake other writers.  */
+			  if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
+			    futex_wake (&rwlock->__data.__writers_futex,
+				1, private);
+			  /* Wake waiting readers.  */
+			  if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
+			    futex_wake (&rwlock->__data.__readers,
+				INT_MAX, private);
+			  return ETIMEDOUT;
+			}
 		    }
-		  ready = true;
-		  break;
+		  /* We still own WRLOCKED and someone else might set a
+		     write phase concurrently, so enable waiting again.
+		     Make sure we don't loose the flag that signals
+		     whether there are threads waiting on this futex.  */
+		  atomic_store_relaxed (&rwlock->__data.__writers_futex, wf);
 		}
-	      /* If we got interrupted (EINTR) or the futex word does not have
-		 the expected value (EAGAIN), retry.  */
+	      /* Use the acquire MO fence to mirror the steps taken in the
+		 non-timeout case.  Note that the read can happen both
+		 in the atomic_load above as well as in the failure case
+		 of the CAS operation.  */
+	      atomic_thread_fence_acquire ();
+	      /* We still need to wait for explicit hand-over, but we must
+		 not use futex_wait anymore.  */
+	      while ((atomic_load_relaxed
+		  (&rwlock->__data.__wrphase_futex)
+		   | PTHREAD_RWLOCK_FUTEX_USED)
+		  == PTHREAD_RWLOCK_FUTEX_USED)
+		{
+		  /* TODO Back-off.  */
+		}
+	      ready = true;
+	      break;
 	    }
-	  /* See pthread_rwlock_rdlock_full.  */
-	  if (ready)
-	    break;
-	  if ((atomic_load_acquire (&rwlock->__data.__readers)
-	      & PTHREAD_RWLOCK_WRPHASE) != 0)
-	    ready = true;
+	  /* If we got interrupted (EINTR) or the futex word does not have
+	     the expected value (EAGAIN), retry.  */
 	}
+      /* See pthread_rwlock_rdlock_full.  */
+      if (ready)
+	break;
+      if ((atomic_load_acquire (&rwlock->__data.__readers)
+	  & PTHREAD_RWLOCK_WRPHASE) != 0)
+	ready = true;
     }
 
+ done:
   atomic_store_relaxed (&rwlock->__data.__cur_writer,
       THREAD_GETMEM (THREAD_SELF, tid));
   return 0;
diff --git a/nptl/tst-rwlock20.c b/nptl/tst-rwlock20.c
new file mode 100644
index 0000000..4aeea2b
--- /dev/null
+++ b/nptl/tst-rwlock20.c
@@ -0,0 +1,116 @@
+/* Test program for a read-phase / write-phase explicit hand-over.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <error.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <time.h>
+#include <atomic.h>
+#include <support/xthread.h>
+
+/* We realy want to set threads to 2 to reproduce this issue. The goal
+   is to have one primary writer and a single reader, and to hit the
+   bug that happens in the interleaving of those two phase transitions.
+   However, on most hardware, adding a second writer seems to help the
+   interleaving happen slightly more often, say 20% of the time.  On a
+   16 core ppc64 machine this fails 100% of the time with an unpatched
+   glibc.  On a 8 core x86_64 machine this fails ~93% of the time, but
+   it doesn't fail at all on a 4 core system, so having available
+   unloaded cores makes a big difference in reproducibility.  On an 8
+   core qemu/kvm guest the reproducer reliability drops to ~10%.  */
+#define THREADS 3
+
+#define KIND PTHREAD_RWLOCK_PREFER_READER_NP
+
+static pthread_rwlock_t lock;
+static int done = 0;
+
+static void*
+tf (void* arg)
+{
+  while (atomic_load_relaxed (&done) == 0)
+    {
+      int rcnt = 0;
+      int wcnt = 100;
+      if ((uintptr_t) arg == 0)
+	{
+	  rcnt = 1;
+	  wcnt = 1;
+	}
+
+      do
+	{
+	  if (wcnt)
+	    {
+	      xpthread_rwlock_wrlock (&lock);
+	      xpthread_rwlock_unlock (&lock);
+	      wcnt--;
+	  }
+	  if (rcnt)
+	    {
+	      xpthread_rwlock_rdlock (&lock);
+	      xpthread_rwlock_unlock (&lock);
+	      rcnt--;
+	  }
+	}
+      while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0));
+
+    }
+    return NULL;
+}
+
+
+
+static int
+do_test (void)
+{
+  pthread_t thr[THREADS];
+  int n;
+  pthread_rwlockattr_t attr;
+
+  xpthread_rwlockattr_init (&attr);
+  xpthread_rwlockattr_setkind_np (&attr, KIND);
+
+  xpthread_rwlock_init (&lock, &attr);
+
+  /* Make standard error the same as standard output.  */
+  dup2 (1, 2);
+
+  /* Make sure we see all message, even those on stdout.  */
+  setvbuf (stdout, NULL, _IONBF, 0);
+
+  for (n = 0; n < THREADS; ++n)
+    thr[n] = xpthread_create (NULL, tf, (void *) (uintptr_t) n);
+
+  struct timespec delay;
+  delay.tv_sec = 10;
+  delay.tv_nsec = 0;
+  nanosleep (&delay, NULL);
+  atomic_store_relaxed (&done, 1);
+
+  /* Wait for all the threads.  */
+  for (n = 0; n < THREADS; ++n)
+    xpthread_join (thr[n]);
+
+  return 0;
+}
+
+#include <support/test-driver.c>

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=92a0e0c6175cdc8d46d2f6efb51c62bd81bf897e

commit 92a0e0c6175cdc8d46d2f6efb51c62bd81bf897e
Author: Florian Weimer <fweimer@redhat.com>
Date:   Mon Aug 28 14:38:52 2017 +0200

    Synchronize support/ infrastructure with master
    
    This commit updates the support/ subdirectory to
    commit faf8c066df0d6bccb54bd74dd696eeb65e1b3bbc
    on the master branch.

diff --git a/scripts/backport-support.sh b/scripts/backport-support.sh
new file mode 100644
index 0000000..2ece7ce
--- /dev/null
+++ b/scripts/backport-support.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+# Create a patch which backports the support/ subdirectory.
+# Copyright (C) 2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# This script does not backport the Makefile tweaks outside the
+# support/ directory (which need to be backported separately), or the
+# changes to test-skeleton.c (which should not be backported).
+
+set -e
+
+export LC_ALL=C
+export GIT_CONFIG=/dev/null
+export GTT_CONFIG_NOSYSTEM=0
+export GIT_PAGER=
+
+usage () {
+    cat >&2 <<EOF
+usage: $0 {patch|commit}
+EOF
+    exit 1
+}
+
+if test $# -ne 1 ; then
+    usage
+fi
+
+command="$1"
+
+case "$command" in
+    patch|commit)
+    ;;
+    *)
+	usage
+	;;
+esac
+
+# The upstream branch to work on.
+branch=origin/master
+
+# The commit which added the support/ directory.
+initial_commit=c23de0aacbeaa7a091609b35764bed931475a16d
+
+# We backport the support directory and this script.  Directories need
+# to end in a /.
+patch_targets="support/ scripts/backport-support.sh"
+
+latest_commit="$(git log --max-count=1 --pretty=format:%H "$branch" -- \
+  $patch_targets)"
+
+# Simplify the branch name somewhat for reporting.
+branch_name="$(echo "$branch" | sed s,^origin/,,)"
+
+command_patch () {
+    cat <<EOF
+This patch creates the contents of the support/ directory up to this
+upstream commit on the $branch_name branch:
+
+EOF
+    git log --max-count=1 "$latest_commit"
+    echo
+    git diff "$initial_commit"^.."$latest_commit" $patch_targets
+    echo "# Before applying the patch, run this command:" >&2
+    echo "# rm -rf $patch_targets" >&2
+}
+
+command_commit () {
+    git status --porcelain | while read line ; do
+	echo "error: working copy is not clean, cannot commit" >&2
+	exit 1
+    done
+    for path in $patch_targets; do
+	echo "# Processing $path" >&2
+	case "$path" in
+	    [a-zA-Z0-9]*/)
+		# Directory.
+		git rm --cached --ignore-unmatch -r "$path"
+		rm -rf "$path"
+		git read-tree --prefix="$path" "$latest_commit":"$path"
+		git checkout "$path"
+		;;
+	    *)
+		# File.
+		git show "$latest_commit":"$path" > "$path"
+		git add "$path"
+	esac
+    done
+    git commit -m "Synchronize support/ infrastructure with $branch_name
+
+This commit updates the support/ subdirectory to
+commit $latest_commit
+on the $branch_name branch.
+"
+}
+
+command_$command
diff --git a/support/Makefile b/support/Makefile
index 2ace559..2ace3fa 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -35,7 +35,12 @@ libsupport-routines = \
   oom_error \
   resolv_test \
   set_fortify_handler \
+  support-xstat \
   support_become_root \
+  support_can_chroot \
+  support_capture_subprocess \
+  support_capture_subprocess_check \
+  support_chroot \
   support_enter_network_namespace \
   support_format_address_family \
   support_format_addrinfo \
@@ -43,17 +48,24 @@ libsupport-routines = \
   support_format_herrno \
   support_format_hostent \
   support_format_netent \
+  support_isolate_in_subprocess \
   support_record_failure \
   support_run_diff \
+  support_shared_allocate \
+  support_write_file_string \
   support_test_main \
   support_test_verify_impl \
   temp_file \
   write_message \
   xaccept \
+  xaccept4 \
   xasprintf \
   xbind \
   xcalloc \
+  xchroot \
+  xclose \
   xconnect \
+  xdup2 \
   xfclose \
   xfopen \
   xfork \
@@ -61,13 +73,18 @@ libsupport-routines = \
   xlisten \
   xmalloc \
   xmemstream \
+  xmkdir \
   xmmap \
+  xmprotect \
   xmunmap \
+  xopen \
+  xpipe \
   xpoll \
   xpthread_attr_destroy \
   xpthread_attr_init \
   xpthread_attr_setdetachstate \
   xpthread_attr_setstacksize \
+  xpthread_attr_setguardsize \
   xpthread_barrier_destroy \
   xpthread_barrier_init \
   xpthread_barrier_wait \
@@ -89,6 +106,12 @@ libsupport-routines = \
   xpthread_mutexattr_setrobust \
   xpthread_mutexattr_settype \
   xpthread_once \
+  xpthread_rwlock_init \
+  xpthread_rwlock_rdlock \
+  xpthread_rwlock_wrlock \
+  xpthread_rwlock_unlock \
+  xpthread_rwlockattr_init \
+  xpthread_rwlockattr_setkind_np \
   xpthread_sigmask \
   xpthread_spin_lock \
   xpthread_spin_unlock \
@@ -111,6 +134,8 @@ endif
 tests = \
   README-testing \
   tst-support-namespace \
+  tst-support_capture_subprocess \
+  tst-support_format_dns_packet \
   tst-support_record_failure \
 
 ifeq ($(run-built-tests),yes)
@@ -125,4 +150,6 @@ $(objpfx)tst-support_record_failure-2.out: tst-support_record_failure-2.sh \
 	$(evaluate-test)
 endif
 
+$(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so
+
 include ../Rules
diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
new file mode 100644
index 0000000..43caf9b
--- /dev/null
+++ b/support/capture_subprocess.h
@@ -0,0 +1,61 @@
+/* Capture output from a subprocess.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef SUPPORT_CAPTURE_SUBPROCESS_H
+#define SUPPORT_CAPTURE_SUBPROCESS_H
+
+#include <support/xmemstream.h>
+
+struct support_capture_subprocess
+{
+  struct xmemstream out;
+  struct xmemstream err;
+  int status;
+};
+
+/* Invoke CALLBACK (CLOSURE) in a subprocess and capture standard
+   output, standard error, and the exit status.  The out.buffer and
+   err.buffer members in the result are null-terminated strings which
+   can be examined by the caller (out.out and err.out are NULL).  */
+struct support_capture_subprocess support_capture_subprocess
+  (void (*callback) (void *), void *closure);
+
+/* Deallocate the subprocess data captured by
+   support_capture_subprocess.  */
+void support_capture_subprocess_free (struct support_capture_subprocess *);
+
+enum support_capture_allow
+{
+  /* No output is allowed.  */
+  sc_allow_none = 0x01,
+  /* Output to stdout is permitted.  */
+  sc_allow_stdout = 0x02,
+  /* Output to standard error is permitted.  */
+  sc_allow_stderr = 0x04,
+};
+
+/* Check that the subprocess exited with STATUS and that only the
+   allowed outputs happened.  ALLOWED is a combination of
+   support_capture_allow flags.  Report errors under the CONTEXT
+   message.  */
+void support_capture_subprocess_check (struct support_capture_subprocess *,
+                                       const char *context, int status,
+                                       int allowed)
+  __attribute__ ((nonnull (1, 2)));
+
+#endif /* SUPPORT_CAPTURE_SUBPROCESS_H */
diff --git a/support/check.h b/support/check.h
index 1d244a3..bdcd129 100644
--- a/support/check.h
+++ b/support/check.h
@@ -51,7 +51,7 @@ __BEGIN_DECLS
     if (expr)                                                   \
       ;                                                         \
     else                                                        \
-      support_test_verify_impl (-1, __FILE__, __LINE__, #expr); \
+      support_test_verify_impl (__FILE__, __LINE__, #expr);     \
   })
 
 /* Record a test failure and exit if EXPR evaluates to false.  */
@@ -60,7 +60,8 @@ __BEGIN_DECLS
     if (expr)                                                   \
       ;                                                         \
     else                                                        \
-      support_test_verify_impl (1, __FILE__, __LINE__, #expr);  \
+      support_test_verify_exit_impl                             \
+        (1, __FILE__, __LINE__, #expr);                         \
   })
 
 int support_print_failure_impl (const char *file, int line,
@@ -70,8 +71,11 @@ void support_exit_failure_impl (int exit_status,
                                 const char *file, int line,
                                 const char *format, ...)
   __attribute__ ((noreturn, nonnull (2), format (printf, 4, 5)));
-void support_test_verify_impl (int status, const char *file, int line,
+void support_test_verify_impl (const char *file, int line,
                                const char *expr);
+void support_test_verify_exit_impl (int status, const char *file, int line,
+                                    const char *expr)
+  __attribute__ ((noreturn));
 
 /* Record a test failure.  This function returns and does not
    terminate the process.  The failure counter is stored in a shared
diff --git a/support/namespace.h b/support/namespace.h
index 6bc82d6..859c2fd 100644
--- a/support/namespace.h
+++ b/support/namespace.h
@@ -35,6 +35,13 @@ __BEGIN_DECLS
    single-threaded processes.  */
 bool support_become_root (void);
 
+/* Return true if this process can perform a chroot operation.  In
+   general, this is only possible if support_become_root has been
+   called.  Note that the actual test is performed in a subprocess,
+   after fork, so that the file system root of the original process is
+   not changed.  */
+bool support_can_chroot (void);
+
 /* Enter a network namespace (and a UTS namespace if possible) and
    configure the loopback interface.  Return true if a network
    namespace could be created.  Print diagnostics to standard output.
@@ -48,6 +55,43 @@ bool support_enter_network_namespace (void);
    UTS namespace.  */
 bool support_in_uts_namespace (void);
 
+/* Invoke CALLBACK (CLOSURE) in a subprocess created using fork.
+   Terminate the calling process if the subprocess exits with a
+   non-zero exit status.  */
+void support_isolate_in_subprocess (void (*callback) (void *), void *closure);
+
+/* Describe the setup of a chroot environment, for
+   support_chroot_create below.  */
+struct support_chroot_configuration
+{
+  /* File contents.  The files are not created if the field is
+     NULL.  */
+  const char *resolv_conf;
+};
+
+/* The result of the creation of a chroot.  */
+struct support_chroot
+{
+  /* Path information.  All these paths are relative to the parent
+     chroot.  */
+
+  /* Path to the chroot directory.  */
+  char *path_chroot;
+
+  /* Path to the /etc/resolv.conf file.  */
+  char *path_resolv_conf;
+};
+
+/* Create a chroot environment.  The returned data should be freed
+   using support_chroot_free below.  The files will be deleted when
+   the process exits.  This function does not enter the chroot.  */
+struct support_chroot *support_chroot_create
+  (struct support_chroot_configuration);
+
+/* Deallocate the chroot information created by
+   support_chroot_create.  */
+void support_chroot_free (struct support_chroot *);
+
 __END_DECLS
 
 #endif
diff --git a/support/resolv_test.c b/support/resolv_test.c
index 6b3554f..1625dcf 100644
--- a/support/resolv_test.c
+++ b/support/resolv_test.c
@@ -32,9 +32,11 @@
 #include <support/test-driver.h>
 #include <support/xsocket.h>
 #include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
-/* Response builder. */
+/* Response builder.  */
 
 enum
   {
@@ -910,7 +912,7 @@ server_thread_tcp_client (void *arg)
         break;
     }
 
-  close (closure->client_socket);
+  xclose (closure->client_socket);
   free (closure);
   return NULL;
 }
@@ -931,7 +933,7 @@ server_thread_tcp (struct resolv_test *obj, int server_index)
       if (obj->termination_requested)
         {
           xpthread_mutex_unlock (&obj->lock);
-          close (client_socket);
+          xclose (client_socket);
           break;
         }
       xpthread_mutex_unlock (&obj->lock);
@@ -991,8 +993,8 @@ make_server_sockets (struct resolv_test_server *server)
              next local UDP address randomly.  */
           if (errno == EADDRINUSE)
             {
-              close (server->socket_udp);
-              close (server->socket_tcp);
+              xclose (server->socket_udp);
+              xclose (server->socket_tcp);
               continue;
             }
           FAIL_EXIT1 ("TCP bind: %m");
@@ -1002,6 +1004,29 @@ make_server_sockets (struct resolv_test_server *server)
     }
 }
 
+/* Like make_server_sockets, but the caller supplies the address to
+   use.  */
+static void
+make_server_sockets_for_address (struct resolv_test_server *server,
+                                 const struct sockaddr *addr)
+{
+  server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+  if (addr->sa_family == AF_INET)
+    server->address = *(const struct sockaddr_in *) addr;
+  else
+    /* We cannot store the server address in the socket.  This should
+       not matter if disable_redirect is used.  */
+    server->address = (struct sockaddr_in) { .sin_family = 0, };
+
+  xbind (server->socket_udp,
+         (struct sockaddr *)&server->address, sizeof (server->address));
+  xbind (server->socket_tcp,
+         (struct sockaddr *)&server->address, sizeof (server->address));
+  xlisten (server->socket_tcp, 5);
+}
+
 /* One-time initialization of NSS.  */
 static void
 resolv_redirect_once (void)
@@ -1062,11 +1087,17 @@ resolv_test_start (struct resolv_redirect_config config)
     .lock = PTHREAD_MUTEX_INITIALIZER,
   };
 
-  resolv_test_init ();
+  if (!config.disable_redirect)
+    resolv_test_init ();
 
   /* Create all the servers, to reserve the necessary ports.  */
   for (int server_index = 0; server_index < config.nscount; ++server_index)
-    make_server_sockets (obj->servers + server_index);
+    if (config.disable_redirect && config.server_address_overrides != NULL)
+      make_server_sockets_for_address
+        (obj->servers + server_index,
+         config.server_address_overrides[server_index]);
+    else
+      make_server_sockets (obj->servers + server_index);
 
   /* Start server threads.  Disable the server ports, as
      requested.  */
@@ -1075,7 +1106,7 @@ resolv_test_start (struct resolv_redirect_config config)
       struct resolv_test_server *server = obj->servers + server_index;
       if (config.servers[server_index].disable_udp)
         {
-          close (server->socket_udp);
+          xclose (server->socket_udp);
           server->socket_udp = -1;
         }
       else if (!config.single_thread_udp)
@@ -1083,7 +1114,7 @@ resolv_test_start (struct resolv_redirect_config config)
                                                   server_thread_udp);
       if (config.servers[server_index].disable_tcp)
         {
-          close (server->socket_tcp);
+          xclose (server->socket_tcp);
           server->socket_tcp = -1;
         }
       else
@@ -1093,6 +1124,9 @@ resolv_test_start (struct resolv_redirect_config config)
   if (config.single_thread_udp)
     start_server_thread_udp_single (obj);
 
+  if (config.disable_redirect)
+    return obj;
+
   int timeout = 1;
 
   /* Initialize libresolv.  */
@@ -1127,6 +1161,7 @@ resolv_test_start (struct resolv_redirect_config config)
     }
   for (int server_index = 0; server_index < config.nscount; ++server_index)
     {
+      TEST_VERIFY_EXIT (obj->servers[server_index].address.sin_port != 0);
       _res.nsaddr_list[server_index] = obj->servers[server_index].address;
       if (test_verbose)
         {
@@ -1164,7 +1199,7 @@ resolv_test_end (struct resolv_test *obj)
           xsendto (sock, "", 1, 0,
                    (struct sockaddr *) &obj->servers[server_index].address,
                    sizeof (obj->servers[server_index].address));
-          close (sock);
+          xclose (sock);
         }
       if (!obj->config.servers[server_index].disable_tcp)
         {
@@ -1172,7 +1207,7 @@ resolv_test_end (struct resolv_test *obj)
           xconnect (sock,
                     (struct sockaddr *) &obj->servers[server_index].address,
                     sizeof (obj->servers[server_index].address));
-          close (sock);
+          xclose (sock);
         }
     }
 
@@ -1187,12 +1222,12 @@ resolv_test_end (struct resolv_test *obj)
         {
           if (!obj->config.single_thread_udp)
             xpthread_join (obj->servers[server_index].thread_udp);
-          close (obj->servers[server_index].socket_udp);
+          xclose (obj->servers[server_index].socket_udp);
         }
       if (!obj->config.servers[server_index].disable_tcp)
         {
           xpthread_join (obj->servers[server_index].thread_tcp);
-          close (obj->servers[server_index].socket_tcp);
+          xclose (obj->servers[server_index].socket_tcp);
         }
     }
 
diff --git a/support/resolv_test.h b/support/resolv_test.h
index 6498751..b953dc1 100644
--- a/support/resolv_test.h
+++ b/support/resolv_test.h
@@ -93,6 +93,16 @@ struct resolv_redirect_config
      may results in more predictable ordering of queries and
      responses.  */
   bool single_thread_udp;
+
+  /* Do not rewrite the _res variable or change NSS defaults.  Use
+     server_address_overrides below to tell the testing framework on
+     which addresses to create the servers.  */
+  bool disable_redirect;
+
+  /* Use these addresses for creating the DNS servers.  The array must
+     have ns_count (or resolv_max_test_servers) sockaddr * elements if
+     not NULL.  */
+  const struct sockaddr *const *server_address_overrides;
 };
 
 /* Configure NSS to use, nss_dns only for aplicable databases, and try
diff --git a/support/support_test_verify_impl.c b/support/support-xstat.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/support-xstat.c
index 5bae38f..86a81ec 100644
--- a/support/support_test_verify_impl.c
+++ b/support/support-xstat.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* stat64 with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,15 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
+/* NB: Non-standard file name to avoid sysdeps override for xstat.  */
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xstat (const char *path, struct stat64 *result)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (stat64 (path, result) != 0)
+    FAIL_EXIT1 ("stat64 (\"%s\"): %m", path);
 }
diff --git a/support/support.h b/support/support.h
index 7292e2a..4b5f04c 100644
--- a/support/support.h
+++ b/support/support.h
@@ -44,6 +44,21 @@ void set_fortify_handler (void (*handler) (int sig));
 void oom_error (const char *function, size_t size)
   __attribute__ ((nonnull (1)));
 
+/* Return a pointer to a memory region of SIZE bytes.  The memory is
+   initialized to zero and will be shared with subprocesses (across
+   fork).  The returned pointer must be freed using
+   support_shared_free; it is not compatible with the malloc
+   functions.  */
+void *support_shared_allocate (size_t size);
+
+/* Deallocate a pointer returned by support_shared_allocate.  */
+void support_shared_free (void *);
+
+/* Write CONTENTS to the file PATH.  Create or truncate the file as
+   needed.  The file mode is 0666 masked by the umask.  Terminate the
+   process on error.  */
+void support_write_file_string (const char *path, const char *contents);
+
 /* Error-checking wrapper functions which terminate the process on
    error.  */
 
diff --git a/support/support_can_chroot.c b/support/support_can_chroot.c
new file mode 100644
index 0000000..0dfd2de
--- /dev/null
+++ b/support/support_can_chroot.c
@@ -0,0 +1,65 @@
+/* Return true if the process can perform a chroot operation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xunistd.h>
+
+static void
+callback (void *closure)
+{
+  int *result = closure;
+  struct stat64 before;
+  xstat ("/dev", &before);
+  if (chroot ("/dev") != 0)
+    {
+      *result = errno;
+      return;
+    }
+  struct stat64 after;
+  xstat ("/", &after);
+  TEST_VERIFY (before.st_dev == after.st_dev);
+  TEST_VERIFY (before.st_ino == after.st_ino);
+  *result = 0;
+}
+
+bool
+support_can_chroot (void)
+{
+  int *result = support_shared_allocate (sizeof (*result));
+  *result = 0;
+  support_isolate_in_subprocess (callback, result);
+  bool ok = *result == 0;
+  if (!ok)
+    {
+      static bool already_warned;
+      if (!already_warned)
+        {
+          already_warned = true;
+          errno = *result;
+          printf ("warning: this process does not support chroot: %m\n");
+        }
+    }
+  support_shared_free (result);
+  return ok;
+}
diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
new file mode 100644
index 0000000..030f124
--- /dev/null
+++ b/support/support_capture_subprocess.c
@@ -0,0 +1,108 @@
+/* Capture output from a subprocess.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/capture_subprocess.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <support/xsocket.h>
+
+static void
+transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
+{
+  if (pfd->revents != 0)
+    {
+      char buf[1024];
+      ssize_t ret = TEMP_FAILURE_RETRY (read (pfd->fd, buf, sizeof (buf)));
+      if (ret < 0)
+        {
+          support_record_failure ();
+          printf ("error: reading from subprocess %s: %m", what);
+          pfd->events = 0;
+          pfd->revents = 0;
+        }
+      else if (ret == 0)
+        {
+          /* EOF reached.  Stop listening.  */
+          pfd->events = 0;
+          pfd->revents = 0;
+        }
+      else
+        /* Store the data just read.   */
+        TEST_VERIFY (fwrite (buf, ret, 1, stream->out) == 1);
+    }
+}
+
+struct support_capture_subprocess
+support_capture_subprocess (void (*callback) (void *), void *closure)
+{
+  struct support_capture_subprocess result;
+  xopen_memstream (&result.out);
+  xopen_memstream (&result.err);
+
+  int stdout_pipe[2];
+  xpipe (stdout_pipe);
+  int stderr_pipe[2];
+  xpipe (stderr_pipe);
+
+  TEST_VERIFY (fflush (stdout) == 0);
+  TEST_VERIFY (fflush (stderr) == 0);
+
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      xclose (stdout_pipe[0]);
+      xclose (stderr_pipe[0]);
+      xdup2 (stdout_pipe[1], STDOUT_FILENO);
+      xdup2 (stderr_pipe[1], STDERR_FILENO);
+      callback (closure);
+      _exit (0);
+    }
+  xclose (stdout_pipe[1]);
+  xclose (stderr_pipe[1]);
+
+  struct pollfd fds[2] =
+    {
+      { .fd = stdout_pipe[0], .events = POLLIN },
+      { .fd = stderr_pipe[0], .events = POLLIN },
+    };
+
+  do
+    {
+      xpoll (fds, 2, -1);
+      transfer ("stdout", &fds[0], &result.out);
+      transfer ("stderr", &fds[1], &result.err);
+    }
+  while (fds[0].events != 0 || fds[1].events != 0);
+  xclose (stdout_pipe[0]);
+  xclose (stderr_pipe[0]);
+
+  xfclose_memstream (&result.out);
+  xfclose_memstream (&result.err);
+  xwaitpid (pid, &result.status, 0);
+  return result;
+}
+
+void
+support_capture_subprocess_free (struct support_capture_subprocess *p)
+{
+  free (p->out.buffer);
+  free (p->err.buffer);
+}
diff --git a/support/support_capture_subprocess_check.c b/support/support_capture_subprocess_check.c
new file mode 100644
index 0000000..e1cf73b
--- /dev/null
+++ b/support/support_capture_subprocess_check.c
@@ -0,0 +1,67 @@
+/* Verify capture output from a subprocess.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+
+static void
+print_context (const char *context, bool *failed)
+{
+  if (*failed)
+    /* Do not duplicate message.  */
+    return;
+  support_record_failure ();
+  printf ("error: subprocess failed: %s\n", context);
+}
+
+void
+support_capture_subprocess_check (struct support_capture_subprocess *proc,
+                                  const char *context, int status,
+                                  int allowed)
+{
+  TEST_VERIFY ((allowed & sc_allow_none)
+               || (allowed & sc_allow_stdout)
+               || (allowed & sc_allow_stderr));
+  TEST_VERIFY (!((allowed & sc_allow_none)
+                 && ((allowed & sc_allow_stdout)
+                     || (allowed & sc_allow_stderr))));
+
+  bool failed = false;
+  if (proc->status != status)
+    {
+      print_context (context, &failed);
+      printf ("error:   expected exit status: %d\n", status);
+      printf ("error:   actual exit status:   %d\n", proc->status);
+    }
+  if (!(allowed & sc_allow_stdout) && proc->out.length != 0)
+    {
+      print_context (context, &failed);
+      printf ("error:   unexpected output from subprocess\n");
+      fwrite (proc->out.buffer, proc->out.length, 1, stdout);
+      puts ("\n");
+    }
+  if (!(allowed & sc_allow_stderr) && proc->err.length != 0)
+    {
+      print_context (context, &failed);
+      printf ("error:   unexpected error output from subprocess\n");
+      fwrite (proc->err.buffer, proc->err.length, 1, stdout);
+      puts ("\n");
+    }
+}
diff --git a/support/support_chroot.c b/support/support_chroot.c
new file mode 100644
index 0000000..c0807b3
--- /dev/null
+++ b/support/support_chroot.c
@@ -0,0 +1,71 @@
+/* Setup a chroot environment for use within tests.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+
+struct support_chroot *
+support_chroot_create (struct support_chroot_configuration conf)
+{
+  struct support_chroot *chroot = xmalloc (sizeof (*chroot));
+
+  chroot->path_chroot = xasprintf ("%s/tst-resolv-res_init-XXXXXX", test_dir);
+  if (mkdtemp (chroot->path_chroot) == NULL)
+    FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", chroot->path_chroot);
+  add_temp_file (chroot->path_chroot);
+
+  /* Create the /etc directory in the chroot environment.  */
+  char *path_etc = xasprintf ("%s/etc", chroot->path_chroot);
+  xmkdir (path_etc, 0777);
+  add_temp_file (path_etc);
+
+  if (conf.resolv_conf != NULL)
+    {
+      /* Create an empty resolv.conf file.  */
+      chroot->path_resolv_conf = xasprintf ("%s/resolv.conf", path_etc);
+      add_temp_file (chroot->path_resolv_conf);
+      support_write_file_string (chroot->path_resolv_conf, conf.resolv_conf);
+    }
+  else
+    chroot->path_resolv_conf = NULL;
+
+  free (path_etc);
+
+  /* valgrind needs a temporary directory in the chroot.  */
+  {
+    char *path_tmp = xasprintf ("%s/tmp", chroot->path_chroot);
+    xmkdir (path_tmp, 0777);
+    add_temp_file (path_tmp);
+    free (path_tmp);
+  }
+
+  return chroot;
+}
+
+void
+support_chroot_free (struct support_chroot *chroot)
+{
+  free (chroot->path_chroot);
+  free (chroot->path_resolv_conf);
+  free (chroot);
+}
diff --git a/support/support_enter_network_namespace.c b/support/support_enter_network_namespace.c
index d2e78fe..28b0ee2 100644
--- a/support/support_enter_network_namespace.c
+++ b/support/support_enter_network_namespace.c
@@ -23,9 +23,10 @@
 #include <stdio.h>
 #include <string.h>
 #include <support/check.h>
+#include <support/xsocket.h>
+#include <support/xunistd.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#include <xsocket.h>
 
 static bool in_uts_namespace;
 
@@ -58,7 +59,7 @@ support_enter_network_namespace (void)
           req.ifr_flags |= IFF_UP | IFF_RUNNING;
           TEST_VERIFY_EXIT (ioctl (fd, SIOCSIFFLAGS, &req) == 0);
         }
-      close (fd);
+      xclose (fd);
 
       return !already_up;
     }
diff --git a/support/support_format_addrinfo.c b/support/support_format_addrinfo.c
index 262e0df..eedb030 100644
--- a/support/support_format_addrinfo.c
+++ b/support/support_format_addrinfo.c
@@ -39,8 +39,8 @@ socket_address_length (int family)
 }
 
 static void
-format_ai_flags (FILE *out, struct addrinfo *ai, int flag, const char *name,
-                 int * flags_printed)
+format_ai_flags_1 (FILE *out, struct addrinfo *ai, int flag, const char *name,
+                   int * flags_printed)
 {
   if ((ai->ai_flags & flag) != 0)
     fprintf (out, " %s", name);
@@ -48,14 +48,16 @@ format_ai_flags (FILE *out, struct addrinfo *ai, int flag, const char *name,
 }
 
 static void
-format_ai_one (FILE *out, struct addrinfo *ai, int *flags)
+format_ai_flags (FILE *out, struct addrinfo *ai)
 {
-  /* ai_flags */
-  if (ai->ai_flags != *flags)
+  if (ai == NULL)
+    return;
+
+  if (ai->ai_flags != 0)
     {
       fprintf (out, "flags:");
       int flags_printed = 0;
-#define FLAG(flag) format_ai_flags (out, ai, flag, #flag, &flags_printed)
+#define FLAG(flag) format_ai_flags_1 (out, ai, flag, #flag, &flags_printed)
       FLAG (AI_PASSIVE);
       FLAG (AI_CANONNAME);
       FLAG (AI_NUMERICHOST);
@@ -72,9 +74,47 @@ format_ai_one (FILE *out, struct addrinfo *ai, int *flags)
       if (remaining != 0)
         fprintf (out, " %08x", remaining);
       fprintf (out, "\n");
-      *flags = ai->ai_flags;
     }
 
+  /* Report flag mismatches within the list.  */
+  int flags = ai->ai_flags;
+  int index = 1;
+  ai = ai->ai_next;
+  while (ai != NULL)
+    {
+      if (ai->ai_flags != flags)
+        fprintf (out, "error: flags at %d: 0x%x expected, 0x%x actual\n",
+                 index, flags, ai->ai_flags);
+      ai = ai->ai_next;
+      ++index;
+    }
+}
+
+static void
+format_ai_canonname (FILE *out, struct addrinfo *ai)
+{
+  if (ai == NULL)
+    return;
+  if (ai->ai_canonname != NULL)
+    fprintf (out, "canonname: %s\n", ai->ai_canonname);
+
+  /* Report incorrectly set ai_canonname fields on subsequent list
+     entries.  */
+  int index = 1;
+  ai = ai->ai_next;
+  while (ai != NULL)
+    {
+      if (ai->ai_canonname != NULL)
+        fprintf (out, "error: canonname set at %d: %s\n",
+                 index, ai->ai_canonname);
+      ai = ai->ai_next;
+      ++index;
+    }
+}
+
+static void
+format_ai_one (FILE *out, struct addrinfo *ai)
+{
   {
     char type_buf[32];
     const char *type_str;
@@ -156,20 +196,16 @@ format_ai_one (FILE *out, struct addrinfo *ai, int *flags)
     else
       fprintf (out, " %s %u\n", buf, ntohs (port));
   }
-
-  /* ai_canonname */
-  if (ai->ai_canonname != NULL)
-    fprintf (out, "canonname: %s\n", ai->ai_canonname);
 }
 
 /* Format all the addresses in one address family.  */
 static void
-format_ai_family (FILE *out, struct addrinfo *ai, int family, int *flags)
+format_ai_family (FILE *out, struct addrinfo *ai, int family)
 {
   while (ai)
     {
       if (ai->ai_family == family)
-        format_ai_one (out, ai, flags);
+        format_ai_one (out, ai);
       ai = ai->ai_next;
     }
 }
@@ -192,9 +228,10 @@ support_format_addrinfo (struct addrinfo *ai, int ret)
     }
   else
     {
-      int flags = 0;
-      format_ai_family (mem.out, ai, AF_INET, &flags);
-      format_ai_family (mem.out, ai, AF_INET6, &flags);
+      format_ai_flags (mem.out, ai);
+      format_ai_canonname (mem.out, ai);
+      format_ai_family (mem.out, ai, AF_INET);
+      format_ai_family (mem.out, ai, AF_INET6);
     }
 
   xfclose_memstream (&mem);
diff --git a/support/support_format_dns_packet.c b/support/support_format_dns_packet.c
index 21fe7e5..2992c57 100644
--- a/support/support_format_dns_packet.c
+++ b/support/support_format_dns_packet.c
@@ -174,7 +174,7 @@ support_format_dns_packet (const unsigned char *buffer, size_t length)
           goto out;
         }
       /* Skip non-matching record types.  */
-      if (rtype != qtype || rclass != qclass)
+      if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass)
         continue;
       switch (rtype)
         {
@@ -186,22 +186,29 @@ support_format_dns_packet (const unsigned char *buffer, size_t length)
                        rdata.data[2],
                        rdata.data[3]);
           else
-            fprintf (mem.out, "error: A record of size %d: %s\n", rdlen, rname.name);
+            fprintf (mem.out, "error: A record of size %d: %s\n",
+                     rdlen, rname.name);
           break;
         case T_AAAA:
           {
-            char buf[100];
-            if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL)
-              fprintf (mem.out, "error: AAAA record decoding failed: %m\n");
+            if (rdlen == 16)
+              {
+                char buf[100];
+                if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL)
+                  fprintf (mem.out, "error: AAAA record decoding failed: %m\n");
+                else
+                  fprintf (mem.out, "address: %s\n", buf);
+              }
             else
-              fprintf (mem.out, "address: %s\n", buf);
+              fprintf (mem.out, "error: AAAA record of size %d: %s\n",
+                       rdlen, rname.name);
           }
           break;
         case T_CNAME:
         case T_PTR:
           {
             struct dname name;
-            if (extract_name (full, &in, &name))
+            if (extract_name (full, &rdata, &name))
               fprintf (mem.out, "name: %s\n", name.name);
             else
               fprintf (mem.out, "error: malformed CNAME/PTR record\n");
diff --git a/support/support_test_verify_impl.c b/support/support_isolate_in_subprocess.c
similarity index 62%
copy from support/support_test_verify_impl.c
copy to support/support_isolate_in_subprocess.c
index 5bae38f..cf48614 100644
--- a/support/support_test_verify_impl.c
+++ b/support/support_isolate_in_subprocess.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* Run a function in a subprocess.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -17,17 +17,22 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xunistd.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+support_isolate_in_subprocess (void (*callback) (void *), void *closure)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      /* Child process.  */
+      callback (closure);
+      _exit (0);
+    }
 
+  /* Parent process.  */
+  int status;
+  xwaitpid (pid, &status, 0);
+  if (status != 0)
+    FAIL_EXIT1 ("child process exited with status %d", status);
 }
diff --git a/support/support_run_diff.c b/support/support_run_diff.c
index 3085037..f5155de 100644
--- a/support/support_run_diff.c
+++ b/support/support_run_diff.c
@@ -24,8 +24,8 @@
 #include <support/check.h>
 #include <support/support.h>
 #include <support/temp_file.h>
+#include <support/xunistd.h>
 #include <sys/wait.h>
-#include <xunistd.h>
 
 static char *
 write_to_temp_file (const char *prefix, const char *str)
@@ -36,7 +36,7 @@ write_to_temp_file (const char *prefix, const char *str)
   TEST_VERIFY_EXIT (fd >= 0);
   free (template);
   xwrite (fd, str, strlen (str));
-  TEST_VERIFY_EXIT (close (fd) == 0);
+  xclose (fd);
   return name;
 }
 
diff --git a/support/support_shared_allocate.c b/support/support_shared_allocate.c
new file mode 100644
index 0000000..61d088e
--- /dev/null
+++ b/support/support_shared_allocate.c
@@ -0,0 +1,57 @@
+/* Allocate a memory region shared across processes.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+/* Header for the allocation.  It contains the size of the allocation
+   for subsequent unmapping.  */
+struct header
+{
+  size_t total_size;
+  char data[] __attribute__ ((aligned (__alignof__ (max_align_t))));
+};
+
+void *
+support_shared_allocate (size_t size)
+{
+  size_t total_size = size + offsetof (struct header, data);
+  if (total_size < size)
+    {
+      errno = ENOMEM;
+      oom_error (__func__, size);
+      return NULL;
+    }
+  else
+    {
+      struct header *result = xmmap (NULL, total_size, PROT_READ | PROT_WRITE,
+                                     MAP_ANONYMOUS | MAP_SHARED, -1);
+      result->total_size = total_size;
+      return &result->data;
+    }
+}
+
+void
+support_shared_free (void *data)
+{
+  struct header *header = data - offsetof (struct header, data);
+  xmunmap (header, header->total_size);
+}
diff --git a/support/support_test_main.c b/support/support_test_main.c
index 914d64f..3c411a4 100644
--- a/support/support_test_main.c
+++ b/support/support_test_main.c
@@ -211,7 +211,8 @@ support_test_main (int argc, char **argv, const struct test_config *config)
         mallopt (M_PERTURB, 42);
     }
 
-  while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1)
+  while ((opt = getopt_long (argc, argv, config->optstring, options, NULL))
+	 != -1)
     switch (opt)
       {
       case '?':
diff --git a/support/support_test_verify_impl.c b/support/support_test_verify_impl.c
index 5bae38f..55ab211 100644
--- a/support/support_test_verify_impl.c
+++ b/support/support_test_verify_impl.c
@@ -22,12 +22,16 @@
 #include <stdlib.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+support_test_verify_impl (const char *file, int line, const char *expr)
 {
   support_record_failure ();
   printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
+}
 
+void
+support_test_verify_exit_impl (int status, const char *file, int line,
+                               const char *expr)
+{
+  support_test_verify_impl (file, line, expr);
+  exit (status);
 }
diff --git a/support/support_test_verify_impl.c b/support/support_write_file_string.c
similarity index 55%
copy from support/support_test_verify_impl.c
copy to support/support_write_file_string.c
index 5bae38f..48e8959 100644
--- a/support/support_test_verify_impl.c
+++ b/support/support_write_file_string.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* Write a string to a file.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,24 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <fcntl.h>
+#include <string.h>
 #include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <xunistd.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+support_write_file_string (const char *path, const char *contents)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  int fd = xopen (path, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+  const char *end = contents + strlen (contents);
+  for (const char *p = contents; p < end; )
+    {
+      ssize_t ret = write (fd, p, end - p);
+      if (ret < 0)
+        FAIL_EXIT1 ("cannot write to \"%s\": %m", path);
+      if (ret == 0)
+        FAIL_EXIT1 ("zero-length write to \"%s\"", path);
+      p += ret;
+    }
+  xclose (fd);
 }
diff --git a/support/temp_file.c b/support/temp_file.c
index f06647a..fdb2477 100644
--- a/support/temp_file.c
+++ b/support/temp_file.c
@@ -25,16 +25,17 @@
 #include <support/support.h>
 
 #include <paths.h>
-#include <search.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 /* List of temporary files.  */
 static struct temp_name_list
 {
-  struct qelem q;
+  struct temp_name_list *next;
   char *name;
+  pid_t owner;
 } *temp_name_list;
 
 /* Location of the temporary files.  Set by the test skeleton via
@@ -50,10 +51,9 @@ add_temp_file (const char *name)
   if (newname != NULL)
     {
       newp->name = newname;
-      if (temp_name_list == NULL)
-	temp_name_list = (struct temp_name_list *) &newp->q;
-      else
-	insque (newp, temp_name_list);
+      newp->next = temp_name_list;
+      newp->owner = getpid ();
+      temp_name_list = newp;
     }
   else
     free (newp);
@@ -97,13 +97,22 @@ support_set_test_dir (const char *path)
 void
 support_delete_temp_files (void)
 {
+  pid_t pid = getpid ();
   while (temp_name_list != NULL)
     {
-      remove (temp_name_list->name);
+      /* Only perform the removal if the path was registed in the same
+	 process, as identified by the PID.  (This assumes that the
+	 parent process which registered the temporary file sticks
+	 around, to prevent PID reuse.)  */
+      if (temp_name_list->owner == pid)
+	{
+	  if (remove (temp_name_list->name) != 0)
+	    printf ("warning: could not remove temporary file: %s: %m\n",
+		    temp_name_list->name);
+	}
       free (temp_name_list->name);
 
-      struct temp_name_list *next
-	= (struct temp_name_list *) temp_name_list->q.q_forw;
+      struct temp_name_list *next = temp_name_list->next;
       free (temp_name_list);
       temp_name_list = next;
     }
@@ -116,9 +125,7 @@ support_print_temp_files (FILE *f)
     {
       struct temp_name_list *n;
       fprintf (f, "temp_files=(\n");
-      for (n = temp_name_list;
-           n != NULL;
-           n = (struct temp_name_list *) n->q.q_forw)
+      for (n = temp_name_list; n != NULL; n = n->next)
         fprintf (f, "  '%s'\n", n->name);
       fprintf (f, ")\n");
     }
diff --git a/support/test-driver.c b/support/test-driver.c
index 482066d..47c387c 100644
--- a/support/test-driver.c
+++ b/support/test-driver.c
@@ -93,6 +93,10 @@
    has this type:
 
      void CMDLINE_PROCESS (int);
+
+   If the program also to process custom default short command line
+   argument (similar to getopt) it must define CMDLINE_OPTSTRING
+   with the expected options (for instance "vb").
 */
 
 #include <support/test-driver.h>
@@ -151,6 +155,11 @@ main (int argc, char **argv)
 #ifdef CMDLINE_PROCESS
   test_config.cmdline_function = CMDLINE_PROCESS;
 #endif
+#ifdef CMDLINE_OPTSTRING
+  test_config.optstring = "+" CMDLINE_OPTSTRING;
+#else
+  test_config.optstring = "+";
+#endif
 
   return support_test_main (argc, argv, &test_config);
 }
diff --git a/support/test-driver.h b/support/test-driver.h
index af1971a..a8fe9c3 100644
--- a/support/test-driver.h
+++ b/support/test-driver.h
@@ -35,6 +35,7 @@ struct test_config
   int expected_status;   /* Expected exit status.  */
   int expected_signal;   /* If non-zero, expect termination by signal.  */
   char no_mallopt;       /* Boolean flag to disable mallopt.  */
+  const char *optstring; /* Short command line options.  */
 };
 
 enum
diff --git a/support/tst-support-namespace.c b/support/tst-support-namespace.c
index a50b074..dbe7cc0 100644
--- a/support/tst-support-namespace.c
+++ b/support/tst-support-namespace.c
@@ -16,18 +16,98 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <errno.h>
+#include <netdb.h>
 #include <stdio.h>
+#include <support/check.h>
 #include <support/namespace.h>
+#include <support/xsocket.h>
+#include <support/xunistd.h>
+
+/* Check that the loopback interface provides multiple addresses which
+   can be used to run independent servers.  */
+static void
+test_localhost_bind (void)
+{
+  printf ("info: testing loopback interface with multiple addresses\n");
+
+  /* Create the two server addresses.  */
+  static const struct addrinfo hints =
+    {
+      .ai_family = AF_INET,
+      .ai_socktype = SOCK_DGRAM,
+      .ai_protocol = IPPROTO_UDP,
+    };
+  struct addrinfo *ai[3];
+  TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.1", "53", &hints, ai + 0) == 0);
+  TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.2", "53", &hints, ai + 1) == 0);
+  TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.3", "53", &hints, ai + 2) == 0);
+
+  /* Create the server scokets and bind them to these addresses.  */
+  int sockets[3];
+  for (int i = 0; i < 3; ++i)
+    {
+      sockets[i] = xsocket
+        (ai[i]->ai_family, ai[i]->ai_socktype, ai[i]->ai_protocol);
+      xbind (sockets[i], ai[i]->ai_addr, ai[i]->ai_addrlen);
+    }
+
+  /* Send two packets to each server.  */
+  int client = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  for (int i = 0; i < 3; ++i)
+    {
+      TEST_VERIFY (sendto (client, &i, sizeof (i), 0,
+                           ai[i]->ai_addr, ai[i]->ai_addrlen) == sizeof (i));
+      int j = i + 256;
+      TEST_VERIFY (sendto (client, &j, sizeof (j), 0,
+                           ai[i]->ai_addr, ai[i]->ai_addrlen) == sizeof (j));
+    }
+
+  /* Check that the packets can be received with the expected
+     contents.  Note that the receive calls interleave differently,
+     which hopefully proves that the sockets are, indeed,
+     independent.  */
+  for (int i = 0; i < 3; ++i)
+    {
+      int buf;
+      TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), 0) == sizeof (buf));
+      TEST_VERIFY (buf == i);
+    }
+  for (int i = 0; i < 3; ++i)
+    {
+      int buf;
+      TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), 0) == sizeof (buf));
+      TEST_VERIFY (buf == i + 256);
+      /* Check that there is no more data to receive.  */
+      TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), MSG_DONTWAIT) == -1);
+      TEST_VERIFY (errno == EWOULDBLOCK || errno == EAGAIN);
+    }
+
+  /* Close all sockets and free the addresses.  */
+  for (int i = 0; i < 3; ++i)
+    {
+      freeaddrinfo (ai[i]);
+      xclose (sockets[i]);
+    }
+  xclose (client);
+}
+
 
 static int
 do_test (void)
 {
-  if (support_become_root ())
+  bool root = support_become_root ();
+  if (root)
     printf ("info: acquired root-like privileges\n");
-  if (support_enter_network_namespace ())
+  bool netns = support_enter_network_namespace ();
+  if (netns)
     printf ("info: entered network namespace\n");
   if (support_in_uts_namespace ())
     printf ("info: also entered UTS namespace\n");
+
+  if (root && netns)
+    test_localhost_bind ();
+
   return 0;
 }
 
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
new file mode 100644
index 0000000..5672fba
--- /dev/null
+++ b/support/tst-support_capture_subprocess.c
@@ -0,0 +1,188 @@
+/* Test capturing output from a subprocess.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* Write one byte at *P to FD and advance *P.  Do nothing if *P is
+   '\0'.  */
+static void
+transfer (const unsigned char **p, int fd)
+{
+  if (**p != '\0')
+    {
+      TEST_VERIFY (write (fd, *p, 1) == 1);
+      ++*p;
+    }
+}
+
+/* Determine the order in which stdout and stderr are written.  */
+enum write_mode { out_first, err_first, interleave,
+                  write_mode_last =  interleave };
+
+/* Describe what to write in the subprocess.  */
+struct test
+{
+  char *out;
+  char *err;
+  enum write_mode write_mode;
+  int signal;
+  int status;
+};
+
+/* For use with support_capture_subprocess.  */
+static void
+callback (void *closure)
+{
+  const struct test *test = closure;
+  bool mode_ok = false;
+  switch (test->write_mode)
+    {
+    case out_first:
+      TEST_VERIFY (fputs (test->out, stdout) >= 0);
+      TEST_VERIFY (fflush (stdout) == 0);
+      TEST_VERIFY (fputs (test->err, stderr) >= 0);
+      TEST_VERIFY (fflush (stderr) == 0);
+      mode_ok = true;
+      break;
+    case err_first:
+      TEST_VERIFY (fputs (test->err, stderr) >= 0);
+      TEST_VERIFY (fflush (stderr) == 0);
+      TEST_VERIFY (fputs (test->out, stdout) >= 0);
+      TEST_VERIFY (fflush (stdout) == 0);
+      mode_ok = true;
+      break;
+    case interleave:
+      {
+        const unsigned char *pout = (const unsigned char *) test->out;
+        const unsigned char *perr = (const unsigned char *) test->err;
+        do
+          {
+            transfer (&pout, STDOUT_FILENO);
+            transfer (&perr, STDERR_FILENO);
+          }
+        while (*pout != '\0' || *perr != '\0');
+      }
+      mode_ok = true;
+      break;
+    }
+  TEST_VERIFY (mode_ok);
+
+  if (test->signal != 0)
+    raise (test->signal);
+  exit (test->status);
+}
+
+/* Create a heap-allocated random string of letters.  */
+static char *
+random_string (size_t length)
+{
+  char *result = xmalloc (length + 1);
+  for (size_t i = 0; i < length; ++i)
+    result[i] = 'a' + (rand () % 26);
+  result[length] = '\0';
+  return result;
+}
+
+/* Check that the specific stream from the captured subprocess matches
+   expectations.  */
+static void
+check_stream (const char *what, const struct xmemstream *stream,
+              const char *expected)
+{
+  if (strcmp (stream->buffer, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: captured %s data incorrect\n"
+              "  expected: %s\n"
+              "  actual:   %s\n",
+              what, expected, stream->buffer);
+    }
+  if (stream->length != strlen (expected))
+    {
+      support_record_failure ();
+      printf ("error: captured %s data length incorrect\n"
+              "  expected: %zu\n"
+              "  actual:   %zu\n",
+              what, strlen (expected), stream->length);
+    }
+}
+
+static int
+do_test (void)
+{
+  const int lengths[] = {0, 1, 17, 512, 20000, -1};
+
+  /* Test multiple combinations of support_capture_subprocess.
+
+     length_idx_stdout: Index into the lengths array above,
+       controls how many bytes are written by the subprocess to
+       standard output.
+     length_idx_stderr: Same for standard error.
+     write_mode: How standard output and standard error writes are
+       ordered.
+     signal: Exit with no signal if zero, with SIGTERM if one.
+     status: Process exit status: 0 if zero, 3 if one.  */
+  for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0;
+       ++length_idx_stdout)
+    for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0;
+         ++length_idx_stderr)
+      for (int write_mode = 0; write_mode < write_mode_last; ++write_mode)
+        for (int signal = 0; signal < 2; ++signal)
+          for (int status = 0; status < 2; ++status)
+            {
+              struct test test =
+                {
+                  .out = random_string (lengths[length_idx_stdout]),
+                  .err = random_string (lengths[length_idx_stderr]),
+                  .write_mode = write_mode,
+                  .signal = signal * SIGTERM, /* 0 or SIGTERM.  */
+                  .status = status * 3,       /* 0 or 3.  */
+                };
+              TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
+              TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
+
+              struct support_capture_subprocess result
+                = support_capture_subprocess (callback, &test);
+              check_stream ("stdout", &result.out, test.out);
+              check_stream ("stderr", &result.err, test.err);
+              if (test.signal != 0)
+                {
+                  TEST_VERIFY (WIFSIGNALED (result.status));
+                  TEST_VERIFY (WTERMSIG (result.status) == test.signal);
+                }
+              else
+                {
+                  TEST_VERIFY (WIFEXITED (result.status));
+                  TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
+                }
+              support_capture_subprocess_free (&result);
+              free (test.out);
+              free (test.err);
+            }
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/tst-support_format_dns_packet.c b/support/tst-support_format_dns_packet.c
new file mode 100644
index 0000000..9c8589c
--- /dev/null
+++ b/support/tst-support_format_dns_packet.c
@@ -0,0 +1,101 @@
+/* Tests for the support_format_dns_packet function.
+   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/run_diff.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+check_packet (const void *buffer, size_t length,
+              const char *name, const char *expected)
+{
+  char *actual = support_format_dns_packet (buffer, length);
+  if (strcmp (actual, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: formatted packet does not match: %s\n", name);
+      support_run_diff ("expected", expected,
+                        "actual", actual);
+    }
+  free (actual);
+}
+
+static void
+test_aaaa_length (void)
+{
+  static const char packet[] =
+    /* Header: Response with two records.  */
+    "\x12\x34\x80\x00\x00\x01\x00\x02\x00\x00\x00\x00"
+    /* Question section.  www.example/IN/AAAA.  */
+    "\x03www\x07""example\x00\x00\x1c\x00\x01"
+    /* Answer section.  www.example AAAA [corrupted].  */
+    "\xc0\x0c"
+    "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x10"
+    "\x20\x01\x0d\xb8\x05\x06\x07\x08"
+    "\x11\x12\x13\x14\x15\x16\x17\x18"
+    /* www.example AAAA [corrupted].  */
+    "\xc0\x0c"
+    "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x11"
+    "\x01\x02\x03\x04\x05\x06\x07\x08"
+    "\x11\x12\x13\x14\x15\x16\x17\x18" "\xff";
+  check_packet (packet, sizeof (packet) - 1, __func__,
+                "name: www.example\n"
+                "address: 2001:db8:506:708:1112:1314:1516:1718\n"
+                "error: AAAA record of size 17: www.example\n");
+}
+
+static void
+test_multiple_cnames (void)
+{
+  static const char packet[] =
+    /* Header: Response with three records.  */
+    "\x12\x34\x80\x00\x00\x01\x00\x03\x00\x00\x00\x00"
+    /* Question section.  www.example/IN/A.  */
+    "\x03www\x07""example\x00\x00\x01\x00\x01"
+    /* Answer section.  www.example CNAME www1.example.  */
+    "\xc0\x0c"
+    "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07"
+    "\x04www1\xc0\x10"
+    /* www1 CNAME www2.  */
+    "\x04www1\xc0\x10"
+    "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07"
+    "\x04www2\xc0\x10"
+    /* www2 A 192.0.2.1.  */
+    "\x04www2\xc0\x10"
+    "\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04"
+    "\xc0\x00\x02\x01";
+  check_packet (packet, sizeof (packet) - 1, __func__,
+                "name: www.example\n"
+                "name: www1.example\n"
+                "name: www2.example\n"
+                "address: 192.0.2.1\n");
+}
+
+static int
+do_test (void)
+{
+  test_aaaa_length ();
+  test_multiple_cnames ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/tst-support_record_failure-2.sh b/support/tst-support_record_failure-2.sh
index 1751377..2c9372c 100644
--- a/support/tst-support_record_failure-2.sh
+++ b/support/tst-support_record_failure-2.sh
@@ -37,7 +37,7 @@ run_test () {
     set -e
     echo "  exit status: $status"
     if test "$output" != "$expected_output" ; then
-	echo "error: unexpected ouput: $output"
+	echo "error: unexpected output: $output"
 	exit 1
     fi
     if test "$status" -ne "$expected_status" ; then
@@ -52,9 +52,9 @@ different_status () {
     run_test 1 "error: 1 test failures" $direct --status=1
     run_test 2 "error: 1 test failures" $direct --status=2
     run_test 1 "error: 1 test failures" $direct --status=77
-    run_test 2 "error: tst-support_record_failure.c:108: not true: false
+    run_test 2 "error: tst-support_record_failure.c:109: not true: false
 error: 1 test failures" $direct --test-verify
-    run_test 2 "error: tst-support_record_failure.c:108: not true: false
+    run_test 2 "error: tst-support_record_failure.c:109: not true: false
 info: execution passed failed TEST_VERIFY
 error: 1 test failures" $direct --test-verify --verbose
 }
@@ -62,8 +62,8 @@ error: 1 test failures" $direct --test-verify --verbose
 different_status
 different_status --direct
 
-run_test 1 "error: tst-support_record_failure.c:115: not true: false
+run_test 1 "error: tst-support_record_failure.c:116: not true: false
 error: 1 test failures" --test-verify-exit
 # --direct does not print the summary error message if exit is called.
-run_test 1 "error: tst-support_record_failure.c:115: not true: false" \
+run_test 1 "error: tst-support_record_failure.c:116: not true: false" \
 	 --direct --test-verify-exit
diff --git a/support/tst-support_record_failure.c b/support/tst-support_record_failure.c
index 62d8e1f..e739e73 100644
--- a/support/tst-support_record_failure.c
+++ b/support/tst-support_record_failure.c
@@ -25,6 +25,7 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 static int exit_status_with_failure = -1;
 static bool test_verify;
diff --git a/support/support_test_verify_impl.c b/support/xaccept4.c
similarity index 68%
copy from support/support_test_verify_impl.c
copy to support/xaccept4.c
index 5bae38f..67dd95e 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xaccept4.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* accept4 with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,17 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
+#include <support/xsocket.h>
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <support/check.h>
 
-void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+int
+xaccept4 (int fd, struct sockaddr *sa, socklen_t *salen, int flags)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  int clientfd = accept4 (fd, sa, salen, flags);
+  if (clientfd < 0)
+    FAIL_EXIT1 ("accept4 (%d, 0x%x): %m", fd, flags);
+  return clientfd;
 }
diff --git a/support/support_test_verify_impl.c b/support/xchroot.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xchroot.c
index 5bae38f..abcc299 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xchroot.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* chroot with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -17,17 +17,12 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xchroot (const char *path)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (chroot (path) != 0)
+    FAIL_EXIT1 ("chroot (\"%s\"): %m", path);
 }
diff --git a/support/support_test_verify_impl.c b/support/xclose.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xclose.c
index 5bae38f..c931e08 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xclose.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* close with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,13 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <support/xunistd.h>
 #include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <errno.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xclose (int fd)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (close (fd) < 0 && errno != EINTR)
+    FAIL_EXIT1 ("close of descriptor %d failed: %m", fd);
 }
diff --git a/support/support_test_verify_impl.c b/support/xdup2.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xdup2.c
index 5bae38f..dc08c94 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xdup2.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* dup2 with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,13 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
+#include <support/xunistd.h>
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/check.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xdup2 (int from, int to)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (dup2 (from, to) < 0)
+    FAIL_EXIT1 ("dup2 (%d, %d): %m", from, to);
 }
diff --git a/support/support_test_verify_impl.c b/support/xmkdir.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xmkdir.c
index 5bae38f..ea17d49 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xmkdir.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* mkdir with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -17,17 +17,12 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xmkdir (const char *path, mode_t mode)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (mkdir (path, mode) != 0)
+    FAIL_EXIT1 ("mkdir (\"%s\", 0%o): %m", path, mode);
 }
diff --git a/support/support_test_verify_impl.c b/support/xmprotect.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xmprotect.c
index 5bae38f..9410251 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xmprotect.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* mprotect with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -17,17 +17,12 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xmprotect (void *addr, size_t length, int prot)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (mprotect (addr, length, prot) != 0)
+    FAIL_EXIT1 ("mprotect (%p, %zu, 0x%x): %m", addr, length, prot);
 }
diff --git a/support/support_test_verify_impl.c b/support/xopen.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xopen.c
index 5bae38f..7f033a0 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xopen.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* open64 with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -17,17 +17,14 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <support/check.h>
+#include <support/xunistd.h>
+#include <fcntl.h>
 
-#include <stdio.h>
-#include <stdlib.h>
-
-void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+int
+xopen (const char *path, int flags, mode_t mode)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  int ret = open64 (path, flags, mode);
+  if (ret < 0)
+    FAIL_EXIT1 ("open64 (\"%s\", 0x%x, 0%o): %m", path, flags, mode);
+  return ret;
 }
diff --git a/support/support_test_verify_impl.c b/support/xpipe.c
similarity index 65%
copy from support/support_test_verify_impl.c
copy to support/xpipe.c
index 5bae38f..89a64a5 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpipe.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pipe with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,13 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
+#include <support/xunistd.h>
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/check.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpipe (int fds[2])
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  if (pipe (fds) < 0)
+    FAIL_EXIT1 ("pipe: %m");
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_attr_setguardsize.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_attr_setguardsize.c
index 5bae38f..35fed5d 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_attr_setguardsize.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_attr_setguardsize with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,11 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_attr_setguardsize (pthread_attr_t *attr, size_t guardsize)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_attr_setguardize",
+			 pthread_attr_setguardsize (attr, guardsize));
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_rwlock_init.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_rwlock_init.c
index 5bae38f..824288c 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_rwlock_init.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_rwlock_init with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,12 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_rwlock_init (pthread_rwlock_t *rwlock,
+		      const pthread_rwlockattr_t *attr)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_rwlock_init",
+                         pthread_rwlock_init (rwlock, attr));
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_rwlock_rdlock.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_rwlock_rdlock.c
index 5bae38f..96330a5 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_rwlock_rdlock.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_rwlock_rdlock with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,11 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_rwlock_rdlock",
+			 pthread_rwlock_rdlock (rwlock));
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_rwlock_unlock.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_rwlock_unlock.c
index 5bae38f..eaa136b 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_rwlock_unlock.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_rwlock_unlock with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,11 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_rwlock_unlock (pthread_rwlock_t *rwlock)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_rwlock_unlock",
+			 pthread_rwlock_unlock (rwlock));
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_rwlock_wrlock.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_rwlock_wrlock.c
index 5bae38f..8d25d5b 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_rwlock_wrlock.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_rwlock_wrlock with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,11 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_rwlock_wrlock",
+			 pthread_rwlock_wrlock (rwlock));
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_rwlockattr_init.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_rwlockattr_init.c
index 5bae38f..48baf24 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_rwlockattr_init.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_rwlockattr_init with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,11 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_rwlockattr_init (pthread_rwlockattr_t *attr)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_rwlockattr_init",
+                         pthread_rwlockattr_init (attr));
 }
diff --git a/support/support_test_verify_impl.c b/support/xpthread_rwlockattr_setkind_np.c
similarity index 63%
copy from support/support_test_verify_impl.c
copy to support/xpthread_rwlockattr_setkind_np.c
index 5bae38f..958aace 100644
--- a/support/support_test_verify_impl.c
+++ b/support/xpthread_rwlockattr_setkind_np.c
@@ -1,5 +1,5 @@
-/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+/* pthread_rwlockattr_setkind_np with error checking.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,18 +16,12 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <support/check.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <support/xthread.h>
 
 void
-support_test_verify_impl (int status, const char *file, int line,
-                          const char *expr)
+xpthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr,
+				int pref)
 {
-  support_record_failure ();
-  printf ("error: %s:%d: not true: %s\n", file, line, expr);
-  if (status >= 0)
-    exit (status);
-
+  xpthread_check_return ("pthread_rwlockattr_setkind_np",
+                         pthread_rwlockattr_setkind_np (attr, pref));
 }
diff --git a/support/xsocket.h b/support/xsocket.h
index 0dbf13a..d672494 100644
--- a/support/xsocket.h
+++ b/support/xsocket.h
@@ -30,6 +30,7 @@ void xconnect (int, const struct sockaddr *, socklen_t);
 void xbind (int, const struct sockaddr *, socklen_t);
 void xlisten (int, int);
 int xaccept (int, struct sockaddr *, socklen_t *);
+int xaccept4 (int, struct sockaddr *, socklen_t *, int);
 void xsendto (int, const void *, size_t, int,
               const struct sockaddr *, socklen_t);
 size_t xrecvfrom (int, void *, size_t, int, struct sockaddr *, socklen_t *);
diff --git a/support/xthread.h b/support/xthread.h
index 6dd7e70..472763e 100644
--- a/support/xthread.h
+++ b/support/xthread.h
@@ -67,11 +67,21 @@ void xpthread_attr_setdetachstate (pthread_attr_t *attr,
 				   int detachstate);
 void xpthread_attr_setstacksize (pthread_attr_t *attr,
 				 size_t stacksize);
+void xpthread_attr_setguardsize (pthread_attr_t *attr,
+				 size_t guardsize);
 
 /* This function returns non-zero if pthread_barrier_wait returned
    PTHREAD_BARRIER_SERIAL_THREAD.  */
 int xpthread_barrier_wait (pthread_barrier_t *barrier);
 
+void xpthread_rwlock_init (pthread_rwlock_t *rwlock,
+			  const pthread_rwlockattr_t *attr);
+void xpthread_rwlockattr_init (pthread_rwlockattr_t *attr);
+void xpthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref);
+void xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock);
+void xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock);
+void xpthread_rwlock_unlock (pthread_rwlock_t *rwlock);
+
 __END_DECLS
 
 #endif /* SUPPORT_THREAD_H */
diff --git a/support/xunistd.h b/support/xunistd.h
index a83b1f4..c947bfd 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -22,20 +22,33 @@
 #ifndef SUPPORT_XUNISTD_H
 #define SUPPORT_XUNISTD_H
 
-#include <unistd.h>
 #include <sys/cdefs.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 __BEGIN_DECLS
 
+struct stat64;
+
 pid_t xfork (void);
 pid_t xwaitpid (pid_t, int *status, int flags);
+void xpipe (int[2]);
+void xdup2 (int, int);
+int xopen (const char *path, int flags, mode_t);
+void xstat (const char *path, struct stat64 *);
+void xmkdir (const char *path, mode_t);
+void xchroot (const char *path);
+
+/* Close the file descriptor.  Ignore EINTR errors, but terminate the
+   process on other errors.  */
+void xclose (int);
 
 /* Write the buffer.  Retry on short writes.  */
 void xwrite (int, const void *, size_t);
 
 /* Invoke mmap with a zero file offset.  */
 void *xmmap (void *addr, size_t length, int prot, int flags, int fd);
-
+void xmprotect (void *addr, size_t length, int prot);
 void xmunmap (void *addr, size_t length);
 
 __END_DECLS

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                  |   23 ++
 NEWS                                       |    2 +
 nptl/Makefile                              |    3 +-
 nptl/pthread_mutex_lock.c                  |   13 +-
 nptl/pthread_mutex_timedlock.c             |   13 +-
 nptl/pthread_rwlock_common.c               |  478 ++++++++++++++--------------
 nptl/tst-mutex7.c                          |   45 ++-
 nptl/tst-mutex7robust.c                    |    7 +
 nptl/tst-rwlock20.c                        |  116 +++++++
 scripts/backport-support.sh                |  110 +++++++
 support/Makefile                           |   27 ++
 support/capture_subprocess.h               |   61 ++++
 support/check.h                            |   10 +-
 support/namespace.h                        |   44 +++
 support/resolv_test.c                      |   61 +++-
 support/resolv_test.h                      |   10 +
 support/support-xstat.c                    |   30 ++
 support/support.h                          |   15 +
 support/support_can_chroot.c               |   65 ++++
 support/support_capture_subprocess.c       |  108 +++++++
 support/support_capture_subprocess_check.c |   67 ++++
 support/support_chroot.c                   |   71 ++++
 support/support_enter_network_namespace.c  |    5 +-
 support/support_format_addrinfo.c          |   69 +++-
 support/support_format_dns_packet.c        |   21 +-
 support/support_isolate_in_subprocess.c    |   38 +++
 support/support_run_diff.c                 |    4 +-
 support/support_shared_allocate.c          |   57 ++++
 support/support_test_main.c                |    3 +-
 support/support_test_verify_impl.c         |   12 +-
 support/support_write_file_string.c        |   39 +++
 support/temp_file.c                        |   31 ++-
 support/test-driver.c                      |    9 +
 support/test-driver.h                      |    1 +
 support/tst-support-namespace.c            |   84 +++++-
 support/tst-support_capture_subprocess.c   |  188 +++++++++++
 support/tst-support_format_dns_packet.c    |  101 ++++++
 support/tst-support_record_failure-2.sh    |   10 +-
 support/tst-support_record_failure.c       |    1 +
 support/xaccept4.c                         |   32 ++
 support/xchroot.c                          |   28 ++
 support/xclose.c                           |   28 ++
 support/xdup2.c                            |   28 ++
 support/xmkdir.c                           |   28 ++
 support/xmprotect.c                        |   28 ++
 support/xopen.c                            |   30 ++
 support/xpipe.c                            |   28 ++
 support/xpthread_attr_setguardsize.c       |   26 ++
 support/xpthread_rwlock_init.c             |   27 ++
 support/xpthread_rwlock_rdlock.c           |   26 ++
 support/xpthread_rwlock_unlock.c           |   26 ++
 support/xpthread_rwlock_wrlock.c           |   26 ++
 support/xpthread_rwlockattr_init.c         |   26 ++
 support/xpthread_rwlockattr_setkind_np.c   |   27 ++
 support/xsocket.h                          |    1 +
 support/xthread.h                          |   10 +
 support/xunistd.h                          |   17 +-
 57 files changed, 2166 insertions(+), 328 deletions(-)
 create mode 100644 nptl/tst-mutex7robust.c
 create mode 100644 nptl/tst-rwlock20.c
 create mode 100644 scripts/backport-support.sh
 create mode 100644 support/capture_subprocess.h
 create mode 100644 support/support-xstat.c
 create mode 100644 support/support_can_chroot.c
 create mode 100644 support/support_capture_subprocess.c
 create mode 100644 support/support_capture_subprocess_check.c
 create mode 100644 support/support_chroot.c
 create mode 100644 support/support_isolate_in_subprocess.c
 create mode 100644 support/support_shared_allocate.c
 create mode 100644 support/support_write_file_string.c
 create mode 100644 support/tst-support_capture_subprocess.c
 create mode 100644 support/tst-support_format_dns_packet.c
 create mode 100644 support/xaccept4.c
 create mode 100644 support/xchroot.c
 create mode 100644 support/xclose.c
 create mode 100644 support/xdup2.c
 create mode 100644 support/xmkdir.c
 create mode 100644 support/xmprotect.c
 create mode 100644 support/xopen.c
 create mode 100644 support/xpipe.c
 create mode 100644 support/xpthread_attr_setguardsize.c
 create mode 100644 support/xpthread_rwlock_init.c
 create mode 100644 support/xpthread_rwlock_rdlock.c
 create mode 100644 support/xpthread_rwlock_unlock.c
 create mode 100644 support/xpthread_rwlock_wrlock.c
 create mode 100644 support/xpthread_rwlockattr_init.c
 create mode 100644 support/xpthread_rwlockattr_setkind_np.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]