[PATCH v2] nptl: Avoid setxid deadlock with blocked signals in thread exit [BZ #28361]

Carlos O'Donell carlos@redhat.com
Wed Sep 22 19:59:51 GMT 2021


On 9/21/21 12:42, Florian Weimer via Libc-alpha wrote:
> As part of the fix for bug 12889, signals are blocked during
> thread exit, so that application code cannot run on the thread that
> is about to exit.  This would cause problems if the application
> expected signals to be delivered after the signal handler revealed
> the thread to still exist, despite pthread_kill can no longer be used
> to send signals to it.  However, glibc internally uses the SIGSETXID
> signal in a way that is incompatible with signal blocking, due to the
> way the setxid handshake delays thread exit until the setxid operation
> has completed.  With a blocked SIGSETXID, the handshake can never
> complete, causing a deadlock.
> 
> As a band-aid, restore the previous handshake protocol by not blocking
> SIGSETXID during thread exit.

This took a while for me to review because I started from first principles to make
sure that adding back the SIGSETXID signal handling was going to work.

INLINE_SETXID_SYSCALL
 -> __nptl_setxid
   -> setxid_mark_thread
     -> pd->cancelhandling and futex manipulated and waiting on signal deliver.

pthread_create
 -> We are exiting...
   -> We release the stack via advise_stack_range() and sit at a minimal stack.
     -> We do a final handshake for pd->cancelhandling and wait for signal delivery and handling.

If we don't do the last step then we deadlock having looked at pd->cancelhandling
and get stuck waiting for the futex forever.

> The new test sysdeps/pthread/tst-pthread-setuid-loop.c is based on
> a downstream test by Martin Osvald.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
 
> ---
> v2: Use __sigfillset instead of memset.
> 
>  nptl/pthread_create.c                     | 12 +++++-
>  sysdeps/pthread/Makefile                  |  1 +
>  sysdeps/pthread/tst-pthread-setuid-loop.c | 61 +++++++++++++++++++++++++++++++
>  3 files changed, 72 insertions(+), 2 deletions(-)
> 
> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
> index a559f86cc2..d6ea43a754 100644
> --- a/nptl/pthread_create.c
> +++ b/nptl/pthread_create.c
> @@ -487,8 +487,16 @@ start_thread (void *arg)
>  
>    /* This prevents sending a signal from this thread to itself during
>       its final stages.  This must come after the exit call above
> -     because atexit handlers must not run with signals blocked.  */
> -  __libc_signal_block_all (NULL);
> +     because atexit handlers must not run with signals blocked.
> +
> +     Do not block SIGSETXID.  The setxid handshake below expects the

OK.

> +     signal to be delivered.  (SIGSETXID cannot run application code,
> +     nor does it use pthread_kill.)  Reuse the pd->sigmask space for
> +     computing the signal mask, to save stack space.  */
> +  __sigfillset (&pd->sigmask);
> +  __sigdelset (&pd->sigmask, SIGSETXID);
> +  INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_BLOCK, &pd->sigmask, NULL,
> +			 __NSIG_BYTES);

OK.

>  
>    /* Tell __pthread_kill_internal that this thread is about to exit.
>       If there is a __pthread_kill_internal in progress, this delays
> diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
> index 48dba717a1..d4bd2d4e3e 100644
> --- a/sysdeps/pthread/Makefile
> +++ b/sysdeps/pthread/Makefile
> @@ -118,6 +118,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
>  	 tst-unload \
>  	 tst-unwind-thread \
>  	 tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \
> +	 tst-pthread-setuid-loop \

OK.

>  	 tst-pthread_cancel-exited \
>  	 tst-pthread_cancel-select-loop \
>  	 tst-pthread_kill-exited \
> diff --git a/sysdeps/pthread/tst-pthread-setuid-loop.c b/sysdeps/pthread/tst-pthread-setuid-loop.c
> new file mode 100644
> index 0000000000..fda2a49b7f
> --- /dev/null
> +++ b/sysdeps/pthread/tst-pthread-setuid-loop.c
> @@ -0,0 +1,61 @@
> +/* Test that setuid, pthread_create, thread exit do not deadlock (bug 28361).

OK.

> +   Copyright (C) 2021 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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <support/check.h>
> +#include <support/xthread.h>
> +#include <unistd.h>
> +
> +/* How many threads to launch during each iteration.  */
> +enum { threads = 4 };
> +
> +/* How many iterations to perform.  This value seems to reproduce
> +   bug 28361 in a bout one in three runs.  */
> +enum { iterations = 5000 };
> +
> +/* Cache of the real user ID used by setuid_thread.  */
> +static uid_t uid;
> +
> +/* Start routine for the threads.  */
> +static void *
> +setuid_thread (void *closure)
> +{
> +  TEST_COMPARE (setuid (uid), 0);

OK.

> +  return NULL;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  /* The setxid machinery is still invoked even if the UID is
> +     unchanged.  (The kernel might reset other credentials as part of
> +     the system call.)  */
> +  uid = getuid ();
> +
> +  for (int i = 0; i < iterations; ++i)
> +    {
> +      pthread_t thread_ids[threads];
> +      for (int j = 0; j < threads; ++j)
> +        thread_ids[j] = xpthread_create (NULL, setuid_thread, NULL);
> +      for (int j = 0; j < threads; ++j)
> +        xpthread_join (thread_ids[j]);

OK.

> +    }
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> 


-- 
Cheers,
Carlos.



More information about the Libc-alpha mailing list