[v3,36/37] nptl: Move setxid broadcast implementation into libc

Message ID 67a2fc1add8e478af071fc21b37c175e671e8f03.1615914632.git.fweimer@redhat.com
State Superseded
Delegated to: Adhemerval Zanella Netto
Headers
Series libpthread removal: NPTL forwarders are gone |

Commit Message

Florian Weimer March 16, 2021, 5:31 p.m. UTC
  The signal handler is exported as __nptl_setxid_sighandler, so
that the libpthread initialization code can install it.  This
is sufficient for now because it is guarantueed to happen before
the first pthread_create call.
---
 nptl/Makefile                              |   1 +
 nptl/Versions                              |   1 +
 nptl/allocatestack.c                       | 209 ---------------
 nptl/nptl-init.c                           |  49 +---
 nptl/nptl_setxid.c                         | 279 +++++++++++++++++++++
 nptl/pthreadP.h                            |   2 +
 sysdeps/nptl/pthread-functions.h           |   1 -
 sysdeps/nptl/setxid.h                      |  26 +-
 sysdeps/unix/sysv/linux/internal-signals.h |   4 -
 9 files changed, 287 insertions(+), 285 deletions(-)
 create mode 100644 nptl/nptl_setxid.c
  

Comments

Adhemerval Zanella Netto March 26, 2021, 6:15 p.m. UTC | #1
On 16/03/2021 14:31, Florian Weimer via Libc-alpha wrote:
> The signal handler is exported as __nptl_setxid_sighandler, so
> that the libpthread initialization code can install it.  This
> is sufficient for now because it is guarantueed to happen before
> the first pthread_create call.

LGTM with some small changes below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  nptl/Makefile                              |   1 +
>  nptl/Versions                              |   1 +
>  nptl/allocatestack.c                       | 209 ---------------
>  nptl/nptl-init.c                           |  49 +---
>  nptl/nptl_setxid.c                         | 279 +++++++++++++++++++++
>  nptl/pthreadP.h                            |   2 +
>  sysdeps/nptl/pthread-functions.h           |   1 -
>  sysdeps/nptl/setxid.h                      |  26 +-
>  sysdeps/unix/sysv/linux/internal-signals.h |   4 -
>  9 files changed, 287 insertions(+), 285 deletions(-)
>  create mode 100644 nptl/nptl_setxid.c
> 
> diff --git a/nptl/Makefile b/nptl/Makefile
> index 1cf72fd5df..1b5038485d 100644
> --- a/nptl/Makefile
> +++ b/nptl/Makefile
> @@ -47,6 +47,7 @@ routines = \
>    lowlevellock \
>    nptl_deallocate_tsd \
>    nptl_nthreads \
> +  nptl_setxid \
>    old_pthread_cond_broadcast \
>    old_pthread_cond_destroy \
>    old_pthread_cond_init \

Ok.

> diff --git a/nptl/Versions b/nptl/Versions
> index a6c8746449..0039594cdd 100644
> --- a/nptl/Versions
> +++ b/nptl/Versions
> @@ -151,6 +151,7 @@ libc {
>      __mutex_aconf;
>      __nptl_deallocate_tsd;
>      __nptl_nthreads;
> +    __nptl_setxid_sighandler;
>      __pthread_attr_copy;
>      __pthread_attr_destroy;
>      __pthread_attr_init;

Ok.

> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
> index 149b999603..6e0808f1a0 100644
> --- a/nptl/allocatestack.c
> +++ b/nptl/allocatestack.c
> @@ -963,215 +963,6 @@ __reclaim_stacks (void)
>  }
>  
>  
> -static void
> -setxid_mark_thread (struct xid_command *cmdp, struct pthread *t)
> -{
> -  int ch;
> -
> -  /* Wait until this thread is cloned.  */
> -  if (t->setxid_futex == -1
> -      && ! atomic_compare_and_exchange_bool_acq (&t->setxid_futex, -2, -1))
> -    do
> -      futex_wait_simple (&t->setxid_futex, -2, FUTEX_PRIVATE);
> -    while (t->setxid_futex == -2);
> -
> -  /* Don't let the thread exit before the setxid handler runs.  */
> -  t->setxid_futex = 0;
> -
> -  do
> -    {
> -      ch = t->cancelhandling;
> -
> -      /* If the thread is exiting right now, ignore it.  */
> -      if ((ch & EXITING_BITMASK) != 0)
> -	{
> -	  /* Release the futex if there is no other setxid in
> -	     progress.  */
> -	  if ((ch & SETXID_BITMASK) == 0)
> -	    {
> -	      t->setxid_futex = 1;
> -	      futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
> -	    }
> -	  return;
> -	}
> -    }
> -  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
> -					       ch | SETXID_BITMASK, ch));
> -}
> -
> -
> -static void
> -setxid_unmark_thread (struct xid_command *cmdp, struct pthread *t)
> -{
> -  int ch;
> -
> -  do
> -    {
> -      ch = t->cancelhandling;
> -      if ((ch & SETXID_BITMASK) == 0)
> -	return;
> -    }
> -  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
> -					       ch & ~SETXID_BITMASK, ch));
> -
> -  /* Release the futex just in case.  */
> -  t->setxid_futex = 1;
> -  futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
> -}
> -
> -
> -static int
> -setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
> -{
> -  if ((t->cancelhandling & SETXID_BITMASK) == 0)
> -    return 0;
> -
> -  int val;
> -  pid_t pid = __getpid ();
> -  val = INTERNAL_SYSCALL_CALL (tgkill, pid, t->tid, SIGSETXID);
> -
> -  /* If this failed, it must have had not started yet or else exited.  */
> -  if (!INTERNAL_SYSCALL_ERROR_P (val))
> -    {
> -      atomic_increment (&cmdp->cntr);
> -      return 1;
> -    }
> -  else
> -    return 0;
> -}
> -
> -/* Check for consistency across set*id system call results.  The abort
> -   should not happen as long as all privileges changes happen through
> -   the glibc wrappers.  ERROR must be 0 (no error) or an errno
> -   code.  */
> -void
> -attribute_hidden
> -__nptl_setxid_error (struct xid_command *cmdp, int error)
> -{
> -  do
> -    {
> -      int olderror = cmdp->error;
> -      if (olderror == error)
> -	break;
> -      if (olderror != -1)
> -	{
> -	  /* Mismatch between current and previous results.  Save the
> -	     error value to memory so that is not clobbered by the
> -	     abort function and preserved in coredumps.  */
> -	  volatile int xid_err __attribute__((unused)) = error;
> -	  abort ();
> -	}
> -    }
> -  while (atomic_compare_and_exchange_bool_acq (&cmdp->error, error, -1));
> -}
> -
> -int
> -attribute_hidden
> -__nptl_setxid (struct xid_command *cmdp)
> -{
> -  int signalled;
> -  int result;
> -  lll_lock (GL (dl_stack_cache_lock), LLL_PRIVATE);
> -
> -  __xidcmd = cmdp;
> -  cmdp->cntr = 0;
> -  cmdp->error = -1;
> -
> -  struct pthread *self = THREAD_SELF;
> -
> -  /* Iterate over the list with system-allocated threads first.  */
> -  list_t *runp;
> -  list_for_each (runp, &GL (dl_stack_used))
> -    {
> -      struct pthread *t = list_entry (runp, struct pthread, list);
> -      if (t == self)
> -	continue;
> -
> -      setxid_mark_thread (cmdp, t);
> -    }
> -
> -  /* Now the list with threads using user-allocated stacks.  */
> -  list_for_each (runp, &GL (dl_stack_user))
> -    {
> -      struct pthread *t = list_entry (runp, struct pthread, list);
> -      if (t == self)
> -	continue;
> -
> -      setxid_mark_thread (cmdp, t);
> -    }
> -
> -  /* Iterate until we don't succeed in signalling anyone.  That means
> -     we have gotten all running threads, and their children will be
> -     automatically correct once started.  */
> -  do
> -    {
> -      signalled = 0;
> -
> -      list_for_each (runp, &GL (dl_stack_used))
> -	{
> -	  struct pthread *t = list_entry (runp, struct pthread, list);
> -	  if (t == self)
> -	    continue;
> -
> -	  signalled += setxid_signal_thread (cmdp, t);
> -	}
> -
> -      list_for_each (runp, &GL (dl_stack_user))
> -	{
> -	  struct pthread *t = list_entry (runp, struct pthread, list);
> -	  if (t == self)
> -	    continue;
> -
> -	  signalled += setxid_signal_thread (cmdp, t);
> -	}
> -
> -      int cur = cmdp->cntr;
> -      while (cur != 0)
> -	{
> -	  futex_wait_simple ((unsigned int *) &cmdp->cntr, cur,
> -			     FUTEX_PRIVATE);
> -	  cur = cmdp->cntr;
> -	}
> -    }
> -  while (signalled != 0);
> -
> -  /* Clean up flags, so that no thread blocks during exit waiting
> -     for a signal which will never come.  */
> -  list_for_each (runp, &GL (dl_stack_used))
> -    {
> -      struct pthread *t = list_entry (runp, struct pthread, list);
> -      if (t == self)
> -	continue;
> -
> -      setxid_unmark_thread (cmdp, t);
> -    }
> -
> -  list_for_each (runp, &GL (dl_stack_user))
> -    {
> -      struct pthread *t = list_entry (runp, struct pthread, list);
> -      if (t == self)
> -	continue;
> -
> -      setxid_unmark_thread (cmdp, t);
> -    }
> -
> -  /* This must be last, otherwise the current thread might not have
> -     permissions to send SIGSETXID syscall to the other threads.  */
> -  result = INTERNAL_SYSCALL_NCS (cmdp->syscall_no, 3,
> -				 cmdp->id[0], cmdp->id[1], cmdp->id[2]);
> -  int error = 0;
> -  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
> -    {
> -      error = INTERNAL_SYSCALL_ERRNO (result);
> -      __set_errno (error);
> -      result = -1;
> -    }
> -  __nptl_setxid_error (cmdp, error);
> -
> -  lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
> -  return result;
> -}
> -
>  static inline void __attribute__((always_inline))
>  init_one_static_tls (struct pthread *curp, struct link_map *map)
>  {

Ok.

> diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
> index 50bc19c4e3..f4a603b32d 100644
> --- a/nptl/nptl-init.c
> +++ b/nptl/nptl-init.c
> @@ -53,7 +53,6 @@ static const char nptl_version[] __attribute_used__ = VERSION;
>  #ifdef SHARED
>  static const struct pthread_functions pthread_functions =
>    {
> -    .ptr__nptl_setxid = __nptl_setxid,
>    };
>  # define ptr_pthread_functions &pthread_functions
>  #else
> @@ -107,52 +106,6 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx)
>  }
>  
>  
> -struct xid_command *__xidcmd attribute_hidden;
> -
> -/* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
> -   tell each thread to call the respective setxid syscall on itself.  This is
> -   the handler.  */
> -static void
> -sighandler_setxid (int sig, siginfo_t *si, void *ctx)
> -{
> -  int result;
> -
> -  /* Safety check.  It would be possible to call this function for
> -     other signals and send a signal from another process.  This is not
> -     correct and might even be a security problem.  Try to catch as
> -     many incorrect invocations as possible.  */
> -  if (sig != SIGSETXID
> -      || si->si_pid != __getpid ()
> -      || si->si_code != SI_TKILL)
> -    return;
> -
> -  result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, 3, __xidcmd->id[0],
> -				 __xidcmd->id[1], __xidcmd->id[2]);
> -  int error = 0;
> -  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
> -    error = INTERNAL_SYSCALL_ERRNO (result);
> -  __nptl_setxid_error (__xidcmd, error);
> -
> -  /* Reset the SETXID flag.  */
> -  struct pthread *self = THREAD_SELF;
> -  int flags, newval;
> -  do
> -    {
> -      flags = THREAD_GETMEM (self, cancelhandling);
> -      newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
> -					  flags & ~SETXID_BITMASK, flags);
> -    }
> -  while (flags != newval);
> -
> -  /* And release the futex.  */
> -  self->setxid_futex = 1;
> -  futex_wake (&self->setxid_futex, 1, FUTEX_PRIVATE);
> -
> -  if (atomic_decrement_val (&__xidcmd->cntr) == 0)
> -    futex_wake ((unsigned int *) &__xidcmd->cntr, 1, FUTEX_PRIVATE);
> -}
> -
> -
>  /* When using __thread for this, we do it in libc so as not
>     to give libpthread its own TLS segment just for this.  */
>  extern void **__libc_dl_error_tsd (void) __attribute__ ((const));

Ok,.

> @@ -184,7 +137,7 @@ __pthread_initialize_minimal_internal (void)
>    (void) __libc_sigaction (SIGCANCEL, &sa, NULL);
>  
>    /* Install the handle to change the threads' uid/gid.  */
> -  sa.sa_sigaction = sighandler_setxid;
> +  sa.sa_sigaction = __nptl_setxid_sighandler;
>    sa.sa_flags = SA_SIGINFO | SA_RESTART;
>    (void) __libc_sigaction (SIGSETXID, &sa, NULL);
>  
> diff --git a/nptl/nptl_setxid.c b/nptl/nptl_setxid.c
> new file mode 100644
> index 0000000000..607c8f426e
> --- /dev/null
> +++ b/nptl/nptl_setxid.c
> @@ -0,0 +1,279 @@

Missing one line description.

> +/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.

Remove the 'Contributed by' Line.

> +
> +   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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <futex-internal.h>
> +#include <ldsodefs.h>
> +#include <list.h>
> +#include <lowlevellock.h>
> +#include <pthreadP.h>
> +#include <unistd.h>
> +
> +static struct xid_command *__xidcmd;

No need double underscore for static variables.

> +
> +/* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
> +   tell each thread to call the respective setxid syscall on itself.  This is
> +   the handler.  */
> +void
> +__nptl_setxid_sighandler (int sig, siginfo_t *si, void *ctx)
> +{
> +  int result;
> +
> +  /* Safety check.  It would be possible to call this function for
> +     other signals and send a signal from another process.  This is not
> +     correct and might even be a security problem.  Try to catch as
> +     many incorrect invocations as possible.  */
> +  if (sig != SIGSETXID
> +      || si->si_pid != __getpid ()
> +      || si->si_code != SI_TKILL)
> +    return;
> +
> +  result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, 3, __xidcmd->id[0],
> +				 __xidcmd->id[1], __xidcmd->id[2]);
> +  int error = 0;
> +  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
> +    error = INTERNAL_SYSCALL_ERRNO (result);
> +  __nptl_setxid_error (__xidcmd, error);
> +
> +  /* Reset the SETXID flag.  */
> +  struct pthread *self = THREAD_SELF;
> +  int flags, newval;
> +  do
> +    {
> +      flags = THREAD_GETMEM (self, cancelhandling);
> +      newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
> +					  flags & ~SETXID_BITMASK, flags);
> +    }
> +  while (flags != newval);
> +
> +  /* And release the futex.  */
> +  self->setxid_futex = 1;
> +  futex_wake (&self->setxid_futex, 1, FUTEX_PRIVATE);
> +
> +  if (atomic_decrement_val (&__xidcmd->cntr) == 0)
> +    futex_wake ((unsigned int *) &__xidcmd->cntr, 1, FUTEX_PRIVATE);
> +}
> +libc_hidden_def (__nptl_setxid_sighandler)

Ok.

> +
> +static void
> +setxid_mark_thread (struct xid_command *cmdp, struct pthread *t)
> +{
> +  int ch;
> +
> +  /* Wait until this thread is cloned.  */
> +  if (t->setxid_futex == -1
> +      && ! atomic_compare_and_exchange_bool_acq (&t->setxid_futex, -2, -1))
> +    do
> +      futex_wait_simple (&t->setxid_futex, -2, FUTEX_PRIVATE);
> +    while (t->setxid_futex == -2);
> +
> +  /* Don't let the thread exit before the setxid handler runs.  */
> +  t->setxid_futex = 0;
> +
> +  do
> +    {
> +      ch = t->cancelhandling;
> +
> +      /* If the thread is exiting right now, ignore it.  */
> +      if ((ch & EXITING_BITMASK) != 0)
> +        {
> +          /* Release the futex if there is no other setxid in
> +             progress.  */
> +          if ((ch & SETXID_BITMASK) == 0)
> +            {
> +              t->setxid_futex = 1;
> +              futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
> +            }
> +          return;
> +        }
> +    }
> +  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
> +                                               ch | SETXID_BITMASK, ch));
> +}
> +
> +
> +static void
> +setxid_unmark_thread (struct xid_command *cmdp, struct pthread *t)
> +{
> +  int ch;
> +
> +  do
> +    {
> +      ch = t->cancelhandling;
> +      if ((ch & SETXID_BITMASK) == 0)
> +        return;
> +    }
> +  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
> +                                               ch & ~SETXID_BITMASK, ch));
> +
> +  /* Release the futex just in case.  */
> +  t->setxid_futex = 1;
> +  futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
> +}
> +
> +
> +static int
> +setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
> +{
> +  if ((t->cancelhandling & SETXID_BITMASK) == 0)
> +    return 0;
> +
> +  int val;
> +  pid_t pid = __getpid ();
> +  val = INTERNAL_SYSCALL_CALL (tgkill, pid, t->tid, SIGSETXID);
> +
> +  /* If this failed, it must have had not started yet or else exited.  */
> +  if (!INTERNAL_SYSCALL_ERROR_P (val))
> +    {
> +      atomic_increment (&cmdp->cntr);
> +      return 1;
> +    }
> +  else
> +    return 0;
> +}
> +
> +/* Check for consistency across set*id system call results.  The abort
> +   should not happen as long as all privileges changes happen through
> +   the glibc wrappers.  ERROR must be 0 (no error) or an errno
> +   code.  */
> +void
> +attribute_hidden
> +__nptl_setxid_error (struct xid_command *cmdp, int error)
> +{
> +  do
> +    {
> +      int olderror = cmdp->error;
> +      if (olderror == error)
> +        break;
> +      if (olderror != -1)
> +        {
> +          /* Mismatch between current and previous results.  Save the
> +             error value to memory so that is not clobbered by the
> +             abort function and preserved in coredumps.  */
> +          volatile int xid_err __attribute__((unused)) = error;
> +          abort ();
> +        }
> +    }
> +  while (atomic_compare_and_exchange_bool_acq (&cmdp->error, error, -1));
> +}
> +
> +int
> +attribute_hidden
> +__nptl_setxid (struct xid_command *cmdp)
> +{
> +  int signalled;
> +  int result;
> +  lll_lock (GL (dl_stack_cache_lock), LLL_PRIVATE);
> +
> +  __xidcmd = cmdp;
> +  cmdp->cntr = 0;
> +  cmdp->error = -1;
> +
> +  struct pthread *self = THREAD_SELF;
> +
> +  /* Iterate over the list with system-allocated threads first.  */
> +  list_t *runp;
> +  list_for_each (runp, &GL (dl_stack_used))
> +    {
> +      struct pthread *t = list_entry (runp, struct pthread, list);
> +      if (t == self)
> +        continue;
> +
> +      setxid_mark_thread (cmdp, t);
> +    }
> +
> +  /* Now the list with threads using user-allocated stacks.  */
> +  list_for_each (runp, &GL (dl_stack_user))
> +    {
> +      struct pthread *t = list_entry (runp, struct pthread, list);
> +      if (t == self)
> +        continue;
> +
> +      setxid_mark_thread (cmdp, t);
> +    }
> +
> +  /* Iterate until we don't succeed in signalling anyone.  That means
> +     we have gotten all running threads, and their children will be
> +     automatically correct once started.  */
> +  do
> +    {
> +      signalled = 0;
> +
> +      list_for_each (runp, &GL (dl_stack_used))
> +        {
> +          struct pthread *t = list_entry (runp, struct pthread, list);
> +          if (t == self)
> +            continue;
> +
> +          signalled += setxid_signal_thread (cmdp, t);
> +        }
> +
> +      list_for_each (runp, &GL (dl_stack_user))
> +        {
> +          struct pthread *t = list_entry (runp, struct pthread, list);
> +          if (t == self)
> +            continue;
> +
> +          signalled += setxid_signal_thread (cmdp, t);
> +        }
> +
> +      int cur = cmdp->cntr;
> +      while (cur != 0)
> +        {
> +          futex_wait_simple ((unsigned int *) &cmdp->cntr, cur,
> +                             FUTEX_PRIVATE);
> +          cur = cmdp->cntr;
> +        }
> +    }
> +  while (signalled != 0);
> +
> +  /* Clean up flags, so that no thread blocks during exit waiting
> +     for a signal which will never come.  */
> +  list_for_each (runp, &GL (dl_stack_used))
> +    {
> +      struct pthread *t = list_entry (runp, struct pthread, list);
> +      if (t == self)
> +        continue;
> +
> +      setxid_unmark_thread (cmdp, t);
> +    }
> +
> +  list_for_each (runp, &GL (dl_stack_user))
> +    {
> +      struct pthread *t = list_entry (runp, struct pthread, list);
> +      if (t == self)
> +        continue;
> +
> +      setxid_unmark_thread (cmdp, t);
> +    }
> +
> +  /* This must be last, otherwise the current thread might not have
> +     permissions to send SIGSETXID syscall to the other threads.  */
> +  result = INTERNAL_SYSCALL_NCS (cmdp->syscall_no, 3,
> +                                 cmdp->id[0], cmdp->id[1], cmdp->id[2]);
> +  int error = 0;
> +  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
> +    {
> +      error = INTERNAL_SYSCALL_ERRNO (result);
> +      __set_errno (error);
> +      result = -1;
> +    }
> +  __nptl_setxid_error (cmdp, error);
> +
> +  lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
> +  return result;
> +}

Ok, this is basically moving the code from nptl/allocatestack.c

> diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
> index 786c566e81..9852906b3a 100644
> --- a/nptl/pthreadP.h
> +++ b/nptl/pthreadP.h
> @@ -695,6 +695,8 @@ extern void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer
>  extern void __nptl_deallocate_tsd (void);
>  libc_hidden_proto (__nptl_deallocate_tsd)
>  
> +void __nptl_setxid_sighandler (int sig, siginfo_t *si, void *ctx);
> +libc_hidden_proto (__nptl_setxid_sighandler)
>  extern void __nptl_setxid_error (struct xid_command *cmdp, int error)
>    attribute_hidden;

Ok.

>  extern int __nptl_setxid (struct xid_command *cmdp) attribute_hidden;
> diff --git a/sysdeps/nptl/pthread-functions.h b/sysdeps/nptl/pthread-functions.h
> index 56c746a01c..d76b2cdecf 100644
> --- a/sysdeps/nptl/pthread-functions.h
> +++ b/sysdeps/nptl/pthread-functions.h
> @@ -30,7 +30,6 @@ struct xid_command;
>     the thread functions.  */
>  struct pthread_functions
>  {
> -  int (*ptr__nptl_setxid) (struct xid_command *);
>  };
>  
>  /* Variable in libc.so.  */

Ok.

> diff --git a/sysdeps/nptl/setxid.h b/sysdeps/nptl/setxid.h
> index 57b665cb5b..8b49d11550 100644
> --- a/sysdeps/nptl/setxid.h
> +++ b/sysdeps/nptl/setxid.h
> @@ -16,6 +16,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <nptl/pthreadP.h>
> +#include <sys/single_threaded.h>
>  #include <sysdep.h>
>  
>  #define __SETXID_1(cmd, arg1) \
> @@ -25,30 +26,10 @@
>  #define __SETXID_3(cmd, arg1, arg2, arg3) \
>    __SETXID_2 (cmd, arg1, arg2); cmd.id[2] = (long int) arg3
>  
> -#ifdef SINGLE_THREAD
> -# define INLINE_SETXID_SYSCALL(name, nr, args...) \
> -  INLINE_SYSCALL (name, nr, args)
> -#elif defined SHARED
> -# define INLINE_SETXID_SYSCALL(name, nr, args...) \
> +#define INLINE_SETXID_SYSCALL(name, nr, args...) \
>    ({									\
>      int __result;							\
> -    if (__builtin_expect (__libc_pthread_functions_init, 0))		\
> -      {									\
> -	struct xid_command __cmd;					\
> -	__cmd.syscall_no = __NR_##name;					\
> -	__SETXID_##nr (__cmd, args);					\
> -	__result = PTHFCT_CALL (ptr__nptl_setxid, (&__cmd));		\
> -	}								\
> -    else								\
> -      __result = INLINE_SYSCALL (name, nr, args);			\
> -    __result;								\
> -   })
> -#else
> -# define INLINE_SETXID_SYSCALL(name, nr, args...) \
> -  ({									\
> -    extern __typeof (__nptl_setxid) __nptl_setxid __attribute__((weak));\
> -    int __result;							\
> -    if (__glibc_unlikely (__nptl_setxid	!= NULL))			      \
> +    if (!__libc_single_threaded)					\
>        {									\
>  	struct xid_command __cmd;					\
>  	__cmd.syscall_no = __NR_##name;					\
> @@ -59,4 +40,3 @@
>        __result = INLINE_SYSCALL (name, nr, args);			\
>      __result;								\
>     })
> -#endif

Ok.

> diff --git a/sysdeps/unix/sysv/linux/internal-signals.h b/sysdeps/unix/sysv/linux/internal-signals.h
> index 1535f5e530..1357b8f5f8 100644
> --- a/sysdeps/unix/sysv/linux/internal-signals.h
> +++ b/sysdeps/unix/sysv/linux/internal-signals.h
> @@ -105,8 +105,4 @@ __libc_signal_restore_set (const sigset_t *set)
>    INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_SETMASK, set, NULL,
>  			 __NSIG_BYTES);
>  }
> -
> -/* Used to communicate with signal handler.  */
> -extern struct xid_command *__xidcmd attribute_hidden;
> -
>  #endif
> 

Ok.
  
Florian Weimer April 6, 2021, 6:41 p.m. UTC | #2
* Adhemerval Zanella:

>> +/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
>
> Remove the 'Contributed by' Line.

I prefer not to.  It's a copy of an existing file.  And in Germany,
authors have an inalienable right to be named.

>> +static struct xid_command *__xidcmd;
>
> No need double underscore for static variables.

Okay, I've also made some of the functions static.

Thanks,
Florian
  
Adhemerval Zanella Netto April 6, 2021, 6:54 p.m. UTC | #3
On 06/04/2021 15:41, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>>> +/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
>>> +   This file is part of the GNU C Library.
>>> +   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
>>
>> Remove the 'Contributed by' Line.
> 
> I prefer not to.  It's a copy of an existing file.  And in Germany,
> authors have an inalienable right to be named.

I am far from Copyright expert here, but my understanding is since
we do not add such 'Contributed by' anymore and Copyright assignment
is what matter, it should be fine to remove even this is a copy.

> 
>>> +static struct xid_command *__xidcmd;
>>
>> No need double underscore for static variables.
> 
> Okay, I've also made some of the functions static.
> 
> Thanks,
> Florian
>
  
Florian Weimer April 6, 2021, 7:23 p.m. UTC | #4
* Adhemerval Zanella:

> On 06/04/2021 15:41, Florian Weimer wrote:
>> * Adhemerval Zanella:
>> 
>>>> +/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
>>>> +   This file is part of the GNU C Library.
>>>> +   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
>>>
>>> Remove the 'Contributed by' Line.
>> 
>> I prefer not to.  It's a copy of an existing file.  And in Germany,
>> authors have an inalienable right to be named.
>
> I am far from Copyright expert here, but my understanding is since
> we do not add such 'Contributed by' anymore and Copyright assignment
> is what matter, it should be fine to remove even this is a copy.

No, authorship and copyright ownership are different legal concepts.
You can sign away your copyright (or the equivalent, exploitation
rights), but you cannot sign away your authorship rights (or moral
rights, as they are sometimes called).

If someone else wants to do a bulk removal of “Contributed by”s, I won't
object to it (beyond what I have written above).  But I won't do it
myself.

Thanks,
Florian
  
Adhemerval Zanella Netto April 6, 2021, 7:40 p.m. UTC | #5
On 06/04/2021 16:23, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>> On 06/04/2021 15:41, Florian Weimer wrote:
>>> * Adhemerval Zanella:
>>>
>>>>> +/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
>>>>> +   This file is part of the GNU C Library.
>>>>> +   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
>>>>
>>>> Remove the 'Contributed by' Line.
>>>
>>> I prefer not to.  It's a copy of an existing file.  And in Germany,
>>> authors have an inalienable right to be named.
>>
>> I am far from Copyright expert here, but my understanding is since
>> we do not add such 'Contributed by' anymore and Copyright assignment
>> is what matter, it should be fine to remove even this is a copy.
> 
> No, authorship and copyright ownership are different legal concepts.
> You can sign away your copyright (or the equivalent, exploitation
> rights), but you cannot sign away your authorship rights (or moral
> rights, as they are sometimes called).
> 
> If someone else wants to do a bulk removal of “Contributed by”s, I won't
> object to it (beyond what I have written above).  But I won't do it
> myself.

Fair enough.
  

Patch

diff --git a/nptl/Makefile b/nptl/Makefile
index 1cf72fd5df..1b5038485d 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -47,6 +47,7 @@  routines = \
   lowlevellock \
   nptl_deallocate_tsd \
   nptl_nthreads \
+  nptl_setxid \
   old_pthread_cond_broadcast \
   old_pthread_cond_destroy \
   old_pthread_cond_init \
diff --git a/nptl/Versions b/nptl/Versions
index a6c8746449..0039594cdd 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -151,6 +151,7 @@  libc {
     __mutex_aconf;
     __nptl_deallocate_tsd;
     __nptl_nthreads;
+    __nptl_setxid_sighandler;
     __pthread_attr_copy;
     __pthread_attr_destroy;
     __pthread_attr_init;
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index 149b999603..6e0808f1a0 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -963,215 +963,6 @@  __reclaim_stacks (void)
 }
 
 
-static void
-setxid_mark_thread (struct xid_command *cmdp, struct pthread *t)
-{
-  int ch;
-
-  /* Wait until this thread is cloned.  */
-  if (t->setxid_futex == -1
-      && ! atomic_compare_and_exchange_bool_acq (&t->setxid_futex, -2, -1))
-    do
-      futex_wait_simple (&t->setxid_futex, -2, FUTEX_PRIVATE);
-    while (t->setxid_futex == -2);
-
-  /* Don't let the thread exit before the setxid handler runs.  */
-  t->setxid_futex = 0;
-
-  do
-    {
-      ch = t->cancelhandling;
-
-      /* If the thread is exiting right now, ignore it.  */
-      if ((ch & EXITING_BITMASK) != 0)
-	{
-	  /* Release the futex if there is no other setxid in
-	     progress.  */
-	  if ((ch & SETXID_BITMASK) == 0)
-	    {
-	      t->setxid_futex = 1;
-	      futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
-	    }
-	  return;
-	}
-    }
-  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
-					       ch | SETXID_BITMASK, ch));
-}
-
-
-static void
-setxid_unmark_thread (struct xid_command *cmdp, struct pthread *t)
-{
-  int ch;
-
-  do
-    {
-      ch = t->cancelhandling;
-      if ((ch & SETXID_BITMASK) == 0)
-	return;
-    }
-  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
-					       ch & ~SETXID_BITMASK, ch));
-
-  /* Release the futex just in case.  */
-  t->setxid_futex = 1;
-  futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
-}
-
-
-static int
-setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
-{
-  if ((t->cancelhandling & SETXID_BITMASK) == 0)
-    return 0;
-
-  int val;
-  pid_t pid = __getpid ();
-  val = INTERNAL_SYSCALL_CALL (tgkill, pid, t->tid, SIGSETXID);
-
-  /* If this failed, it must have had not started yet or else exited.  */
-  if (!INTERNAL_SYSCALL_ERROR_P (val))
-    {
-      atomic_increment (&cmdp->cntr);
-      return 1;
-    }
-  else
-    return 0;
-}
-
-/* Check for consistency across set*id system call results.  The abort
-   should not happen as long as all privileges changes happen through
-   the glibc wrappers.  ERROR must be 0 (no error) or an errno
-   code.  */
-void
-attribute_hidden
-__nptl_setxid_error (struct xid_command *cmdp, int error)
-{
-  do
-    {
-      int olderror = cmdp->error;
-      if (olderror == error)
-	break;
-      if (olderror != -1)
-	{
-	  /* Mismatch between current and previous results.  Save the
-	     error value to memory so that is not clobbered by the
-	     abort function and preserved in coredumps.  */
-	  volatile int xid_err __attribute__((unused)) = error;
-	  abort ();
-	}
-    }
-  while (atomic_compare_and_exchange_bool_acq (&cmdp->error, error, -1));
-}
-
-int
-attribute_hidden
-__nptl_setxid (struct xid_command *cmdp)
-{
-  int signalled;
-  int result;
-  lll_lock (GL (dl_stack_cache_lock), LLL_PRIVATE);
-
-  __xidcmd = cmdp;
-  cmdp->cntr = 0;
-  cmdp->error = -1;
-
-  struct pthread *self = THREAD_SELF;
-
-  /* Iterate over the list with system-allocated threads first.  */
-  list_t *runp;
-  list_for_each (runp, &GL (dl_stack_used))
-    {
-      struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
-	continue;
-
-      setxid_mark_thread (cmdp, t);
-    }
-
-  /* Now the list with threads using user-allocated stacks.  */
-  list_for_each (runp, &GL (dl_stack_user))
-    {
-      struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
-	continue;
-
-      setxid_mark_thread (cmdp, t);
-    }
-
-  /* Iterate until we don't succeed in signalling anyone.  That means
-     we have gotten all running threads, and their children will be
-     automatically correct once started.  */
-  do
-    {
-      signalled = 0;
-
-      list_for_each (runp, &GL (dl_stack_used))
-	{
-	  struct pthread *t = list_entry (runp, struct pthread, list);
-	  if (t == self)
-	    continue;
-
-	  signalled += setxid_signal_thread (cmdp, t);
-	}
-
-      list_for_each (runp, &GL (dl_stack_user))
-	{
-	  struct pthread *t = list_entry (runp, struct pthread, list);
-	  if (t == self)
-	    continue;
-
-	  signalled += setxid_signal_thread (cmdp, t);
-	}
-
-      int cur = cmdp->cntr;
-      while (cur != 0)
-	{
-	  futex_wait_simple ((unsigned int *) &cmdp->cntr, cur,
-			     FUTEX_PRIVATE);
-	  cur = cmdp->cntr;
-	}
-    }
-  while (signalled != 0);
-
-  /* Clean up flags, so that no thread blocks during exit waiting
-     for a signal which will never come.  */
-  list_for_each (runp, &GL (dl_stack_used))
-    {
-      struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
-	continue;
-
-      setxid_unmark_thread (cmdp, t);
-    }
-
-  list_for_each (runp, &GL (dl_stack_user))
-    {
-      struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
-	continue;
-
-      setxid_unmark_thread (cmdp, t);
-    }
-
-  /* This must be last, otherwise the current thread might not have
-     permissions to send SIGSETXID syscall to the other threads.  */
-  result = INTERNAL_SYSCALL_NCS (cmdp->syscall_no, 3,
-				 cmdp->id[0], cmdp->id[1], cmdp->id[2]);
-  int error = 0;
-  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
-    {
-      error = INTERNAL_SYSCALL_ERRNO (result);
-      __set_errno (error);
-      result = -1;
-    }
-  __nptl_setxid_error (cmdp, error);
-
-  lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
-  return result;
-}
-
 static inline void __attribute__((always_inline))
 init_one_static_tls (struct pthread *curp, struct link_map *map)
 {
diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
index 50bc19c4e3..f4a603b32d 100644
--- a/nptl/nptl-init.c
+++ b/nptl/nptl-init.c
@@ -53,7 +53,6 @@  static const char nptl_version[] __attribute_used__ = VERSION;
 #ifdef SHARED
 static const struct pthread_functions pthread_functions =
   {
-    .ptr__nptl_setxid = __nptl_setxid,
   };
 # define ptr_pthread_functions &pthread_functions
 #else
@@ -107,52 +106,6 @@  sigcancel_handler (int sig, siginfo_t *si, void *ctx)
 }
 
 
-struct xid_command *__xidcmd attribute_hidden;
-
-/* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
-   tell each thread to call the respective setxid syscall on itself.  This is
-   the handler.  */
-static void
-sighandler_setxid (int sig, siginfo_t *si, void *ctx)
-{
-  int result;
-
-  /* Safety check.  It would be possible to call this function for
-     other signals and send a signal from another process.  This is not
-     correct and might even be a security problem.  Try to catch as
-     many incorrect invocations as possible.  */
-  if (sig != SIGSETXID
-      || si->si_pid != __getpid ()
-      || si->si_code != SI_TKILL)
-    return;
-
-  result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, 3, __xidcmd->id[0],
-				 __xidcmd->id[1], __xidcmd->id[2]);
-  int error = 0;
-  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
-    error = INTERNAL_SYSCALL_ERRNO (result);
-  __nptl_setxid_error (__xidcmd, error);
-
-  /* Reset the SETXID flag.  */
-  struct pthread *self = THREAD_SELF;
-  int flags, newval;
-  do
-    {
-      flags = THREAD_GETMEM (self, cancelhandling);
-      newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
-					  flags & ~SETXID_BITMASK, flags);
-    }
-  while (flags != newval);
-
-  /* And release the futex.  */
-  self->setxid_futex = 1;
-  futex_wake (&self->setxid_futex, 1, FUTEX_PRIVATE);
-
-  if (atomic_decrement_val (&__xidcmd->cntr) == 0)
-    futex_wake ((unsigned int *) &__xidcmd->cntr, 1, FUTEX_PRIVATE);
-}
-
-
 /* When using __thread for this, we do it in libc so as not
    to give libpthread its own TLS segment just for this.  */
 extern void **__libc_dl_error_tsd (void) __attribute__ ((const));
@@ -184,7 +137,7 @@  __pthread_initialize_minimal_internal (void)
   (void) __libc_sigaction (SIGCANCEL, &sa, NULL);
 
   /* Install the handle to change the threads' uid/gid.  */
-  sa.sa_sigaction = sighandler_setxid;
+  sa.sa_sigaction = __nptl_setxid_sighandler;
   sa.sa_flags = SA_SIGINFO | SA_RESTART;
   (void) __libc_sigaction (SIGSETXID, &sa, NULL);
 
diff --git a/nptl/nptl_setxid.c b/nptl/nptl_setxid.c
new file mode 100644
index 0000000000..607c8f426e
--- /dev/null
+++ b/nptl/nptl_setxid.c
@@ -0,0 +1,279 @@ 
+/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <futex-internal.h>
+#include <ldsodefs.h>
+#include <list.h>
+#include <lowlevellock.h>
+#include <pthreadP.h>
+#include <unistd.h>
+
+static struct xid_command *__xidcmd;
+
+/* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
+   tell each thread to call the respective setxid syscall on itself.  This is
+   the handler.  */
+void
+__nptl_setxid_sighandler (int sig, siginfo_t *si, void *ctx)
+{
+  int result;
+
+  /* Safety check.  It would be possible to call this function for
+     other signals and send a signal from another process.  This is not
+     correct and might even be a security problem.  Try to catch as
+     many incorrect invocations as possible.  */
+  if (sig != SIGSETXID
+      || si->si_pid != __getpid ()
+      || si->si_code != SI_TKILL)
+    return;
+
+  result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, 3, __xidcmd->id[0],
+				 __xidcmd->id[1], __xidcmd->id[2]);
+  int error = 0;
+  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
+    error = INTERNAL_SYSCALL_ERRNO (result);
+  __nptl_setxid_error (__xidcmd, error);
+
+  /* Reset the SETXID flag.  */
+  struct pthread *self = THREAD_SELF;
+  int flags, newval;
+  do
+    {
+      flags = THREAD_GETMEM (self, cancelhandling);
+      newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
+					  flags & ~SETXID_BITMASK, flags);
+    }
+  while (flags != newval);
+
+  /* And release the futex.  */
+  self->setxid_futex = 1;
+  futex_wake (&self->setxid_futex, 1, FUTEX_PRIVATE);
+
+  if (atomic_decrement_val (&__xidcmd->cntr) == 0)
+    futex_wake ((unsigned int *) &__xidcmd->cntr, 1, FUTEX_PRIVATE);
+}
+libc_hidden_def (__nptl_setxid_sighandler)
+
+static void
+setxid_mark_thread (struct xid_command *cmdp, struct pthread *t)
+{
+  int ch;
+
+  /* Wait until this thread is cloned.  */
+  if (t->setxid_futex == -1
+      && ! atomic_compare_and_exchange_bool_acq (&t->setxid_futex, -2, -1))
+    do
+      futex_wait_simple (&t->setxid_futex, -2, FUTEX_PRIVATE);
+    while (t->setxid_futex == -2);
+
+  /* Don't let the thread exit before the setxid handler runs.  */
+  t->setxid_futex = 0;
+
+  do
+    {
+      ch = t->cancelhandling;
+
+      /* If the thread is exiting right now, ignore it.  */
+      if ((ch & EXITING_BITMASK) != 0)
+        {
+          /* Release the futex if there is no other setxid in
+             progress.  */
+          if ((ch & SETXID_BITMASK) == 0)
+            {
+              t->setxid_futex = 1;
+              futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
+            }
+          return;
+        }
+    }
+  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
+                                               ch | SETXID_BITMASK, ch));
+}
+
+
+static void
+setxid_unmark_thread (struct xid_command *cmdp, struct pthread *t)
+{
+  int ch;
+
+  do
+    {
+      ch = t->cancelhandling;
+      if ((ch & SETXID_BITMASK) == 0)
+        return;
+    }
+  while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
+                                               ch & ~SETXID_BITMASK, ch));
+
+  /* Release the futex just in case.  */
+  t->setxid_futex = 1;
+  futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
+}
+
+
+static int
+setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
+{
+  if ((t->cancelhandling & SETXID_BITMASK) == 0)
+    return 0;
+
+  int val;
+  pid_t pid = __getpid ();
+  val = INTERNAL_SYSCALL_CALL (tgkill, pid, t->tid, SIGSETXID);
+
+  /* If this failed, it must have had not started yet or else exited.  */
+  if (!INTERNAL_SYSCALL_ERROR_P (val))
+    {
+      atomic_increment (&cmdp->cntr);
+      return 1;
+    }
+  else
+    return 0;
+}
+
+/* Check for consistency across set*id system call results.  The abort
+   should not happen as long as all privileges changes happen through
+   the glibc wrappers.  ERROR must be 0 (no error) or an errno
+   code.  */
+void
+attribute_hidden
+__nptl_setxid_error (struct xid_command *cmdp, int error)
+{
+  do
+    {
+      int olderror = cmdp->error;
+      if (olderror == error)
+        break;
+      if (olderror != -1)
+        {
+          /* Mismatch between current and previous results.  Save the
+             error value to memory so that is not clobbered by the
+             abort function and preserved in coredumps.  */
+          volatile int xid_err __attribute__((unused)) = error;
+          abort ();
+        }
+    }
+  while (atomic_compare_and_exchange_bool_acq (&cmdp->error, error, -1));
+}
+
+int
+attribute_hidden
+__nptl_setxid (struct xid_command *cmdp)
+{
+  int signalled;
+  int result;
+  lll_lock (GL (dl_stack_cache_lock), LLL_PRIVATE);
+
+  __xidcmd = cmdp;
+  cmdp->cntr = 0;
+  cmdp->error = -1;
+
+  struct pthread *self = THREAD_SELF;
+
+  /* Iterate over the list with system-allocated threads first.  */
+  list_t *runp;
+  list_for_each (runp, &GL (dl_stack_used))
+    {
+      struct pthread *t = list_entry (runp, struct pthread, list);
+      if (t == self)
+        continue;
+
+      setxid_mark_thread (cmdp, t);
+    }
+
+  /* Now the list with threads using user-allocated stacks.  */
+  list_for_each (runp, &GL (dl_stack_user))
+    {
+      struct pthread *t = list_entry (runp, struct pthread, list);
+      if (t == self)
+        continue;
+
+      setxid_mark_thread (cmdp, t);
+    }
+
+  /* Iterate until we don't succeed in signalling anyone.  That means
+     we have gotten all running threads, and their children will be
+     automatically correct once started.  */
+  do
+    {
+      signalled = 0;
+
+      list_for_each (runp, &GL (dl_stack_used))
+        {
+          struct pthread *t = list_entry (runp, struct pthread, list);
+          if (t == self)
+            continue;
+
+          signalled += setxid_signal_thread (cmdp, t);
+        }
+
+      list_for_each (runp, &GL (dl_stack_user))
+        {
+          struct pthread *t = list_entry (runp, struct pthread, list);
+          if (t == self)
+            continue;
+
+          signalled += setxid_signal_thread (cmdp, t);
+        }
+
+      int cur = cmdp->cntr;
+      while (cur != 0)
+        {
+          futex_wait_simple ((unsigned int *) &cmdp->cntr, cur,
+                             FUTEX_PRIVATE);
+          cur = cmdp->cntr;
+        }
+    }
+  while (signalled != 0);
+
+  /* Clean up flags, so that no thread blocks during exit waiting
+     for a signal which will never come.  */
+  list_for_each (runp, &GL (dl_stack_used))
+    {
+      struct pthread *t = list_entry (runp, struct pthread, list);
+      if (t == self)
+        continue;
+
+      setxid_unmark_thread (cmdp, t);
+    }
+
+  list_for_each (runp, &GL (dl_stack_user))
+    {
+      struct pthread *t = list_entry (runp, struct pthread, list);
+      if (t == self)
+        continue;
+
+      setxid_unmark_thread (cmdp, t);
+    }
+
+  /* This must be last, otherwise the current thread might not have
+     permissions to send SIGSETXID syscall to the other threads.  */
+  result = INTERNAL_SYSCALL_NCS (cmdp->syscall_no, 3,
+                                 cmdp->id[0], cmdp->id[1], cmdp->id[2]);
+  int error = 0;
+  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
+    {
+      error = INTERNAL_SYSCALL_ERRNO (result);
+      __set_errno (error);
+      result = -1;
+    }
+  __nptl_setxid_error (cmdp, error);
+
+  lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
+  return result;
+}
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index 786c566e81..9852906b3a 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -695,6 +695,8 @@  extern void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer
 extern void __nptl_deallocate_tsd (void);
 libc_hidden_proto (__nptl_deallocate_tsd)
 
+void __nptl_setxid_sighandler (int sig, siginfo_t *si, void *ctx);
+libc_hidden_proto (__nptl_setxid_sighandler)
 extern void __nptl_setxid_error (struct xid_command *cmdp, int error)
   attribute_hidden;
 extern int __nptl_setxid (struct xid_command *cmdp) attribute_hidden;
diff --git a/sysdeps/nptl/pthread-functions.h b/sysdeps/nptl/pthread-functions.h
index 56c746a01c..d76b2cdecf 100644
--- a/sysdeps/nptl/pthread-functions.h
+++ b/sysdeps/nptl/pthread-functions.h
@@ -30,7 +30,6 @@  struct xid_command;
    the thread functions.  */
 struct pthread_functions
 {
-  int (*ptr__nptl_setxid) (struct xid_command *);
 };
 
 /* Variable in libc.so.  */
diff --git a/sysdeps/nptl/setxid.h b/sysdeps/nptl/setxid.h
index 57b665cb5b..8b49d11550 100644
--- a/sysdeps/nptl/setxid.h
+++ b/sysdeps/nptl/setxid.h
@@ -16,6 +16,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <nptl/pthreadP.h>
+#include <sys/single_threaded.h>
 #include <sysdep.h>
 
 #define __SETXID_1(cmd, arg1) \
@@ -25,30 +26,10 @@ 
 #define __SETXID_3(cmd, arg1, arg2, arg3) \
   __SETXID_2 (cmd, arg1, arg2); cmd.id[2] = (long int) arg3
 
-#ifdef SINGLE_THREAD
-# define INLINE_SETXID_SYSCALL(name, nr, args...) \
-  INLINE_SYSCALL (name, nr, args)
-#elif defined SHARED
-# define INLINE_SETXID_SYSCALL(name, nr, args...) \
+#define INLINE_SETXID_SYSCALL(name, nr, args...) \
   ({									\
     int __result;							\
-    if (__builtin_expect (__libc_pthread_functions_init, 0))		\
-      {									\
-	struct xid_command __cmd;					\
-	__cmd.syscall_no = __NR_##name;					\
-	__SETXID_##nr (__cmd, args);					\
-	__result = PTHFCT_CALL (ptr__nptl_setxid, (&__cmd));		\
-	}								\
-    else								\
-      __result = INLINE_SYSCALL (name, nr, args);			\
-    __result;								\
-   })
-#else
-# define INLINE_SETXID_SYSCALL(name, nr, args...) \
-  ({									\
-    extern __typeof (__nptl_setxid) __nptl_setxid __attribute__((weak));\
-    int __result;							\
-    if (__glibc_unlikely (__nptl_setxid	!= NULL))			      \
+    if (!__libc_single_threaded)					\
       {									\
 	struct xid_command __cmd;					\
 	__cmd.syscall_no = __NR_##name;					\
@@ -59,4 +40,3 @@ 
       __result = INLINE_SYSCALL (name, nr, args);			\
     __result;								\
    })
-#endif
diff --git a/sysdeps/unix/sysv/linux/internal-signals.h b/sysdeps/unix/sysv/linux/internal-signals.h
index 1535f5e530..1357b8f5f8 100644
--- a/sysdeps/unix/sysv/linux/internal-signals.h
+++ b/sysdeps/unix/sysv/linux/internal-signals.h
@@ -105,8 +105,4 @@  __libc_signal_restore_set (const sigset_t *set)
   INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_SETMASK, set, NULL,
 			 __NSIG_BYTES);
 }
-
-/* Used to communicate with signal handler.  */
-extern struct xid_command *__xidcmd attribute_hidden;
-
 #endif