From 706e6749de9667f3f3763743a294d28f895f4fa9 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Wed, 14 Sep 2016 14:07:20 -0300 Subject: [PATCH] posix: Correctly enable/disable cancellation on Linux posix_spawn This patch correctly enable and disable asynchronous cancellation on Linux posix_spawn. Current code invert the logic by enabling and disabling instead. It also adds a new test to check if posix_spawn is not a cancellation entrypoint. Checked on x86_64, i686, powerpc64le, and aarch64. * nptl/Makefile (tests): Add tst-exec5. * nptl/tst-exec5.c: New file. * sysdeps/unix/sysv/linux/spawni.c (__spawni): Correctly enable and disable asynchronous cancellation. --- ChangeLog | 7 ++ nptl/Makefile | 2 +- nptl/tst-exec5.c | 176 +++++++++++++++++++++++++++++++ sysdeps/unix/sysv/linux/spawni.c | 6 +- 4 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 nptl/tst-exec5.c diff --git a/ChangeLog b/ChangeLog index 7f960669a7..76d508ffae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2016-09-20 Adhemerval Zanella + + * nptl/Makefile (tests): Add tst-exec5. + * nptl/tst-exec5.c: New file. + * sysdeps/unix/sysv/linux/spawni.c (__spawni): Correctly enable and disable + asynchronous cancellation. + 2016-09-20 Samuel Thibault * hurd/exc2signal.c: #include diff --git a/nptl/Makefile b/nptl/Makefile index e9485dfdb3..91303db54d 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -273,7 +273,7 @@ tests = tst-typesizes \ tst-flock1 tst-flock2 \ tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \ tst-signal6 tst-signal7 \ - tst-exec1 tst-exec2 tst-exec3 tst-exec4 \ + tst-exec1 tst-exec2 tst-exec3 tst-exec4 tst-exec5 \ tst-exit1 tst-exit2 tst-exit3 \ tst-stdio1 tst-stdio2 \ tst-stack1 tst-stack2 tst-stack3 tst-stack4 tst-pthread-getattr \ diff --git a/nptl/tst-exec5.c b/nptl/tst-exec5.c new file mode 100644 index 0000000000..a2e7e947e8 --- /dev/null +++ b/nptl/tst-exec5.c @@ -0,0 +1,176 @@ +/* Check if posix_spawn does not act as a cancellation entrypoint. + Copyright (C) 2016 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int do_test (void); +#define TEST_FUNCTION do_test () +#include + +static pthread_barrier_t b; + +static pid_t pid; +static int pipefd[2]; + +static void * +tf (void *arg) +{ + xpthread_barrier_wait (&b); + + posix_spawn_file_actions_t a; + if (posix_spawn_file_actions_init (&a) != 0) + { + puts ("error: spawn_file_actions_init failed"); + exit (1); + } + + if (posix_spawn_file_actions_adddup2 (&a, pipefd[1], STDOUT_FILENO) != 0) + { + puts ("error: spawn_file_actions_adddup2 failed"); + exit (1); + } + + if (posix_spawn_file_actions_addclose (&a, pipefd[0]) != 0) + { + puts ("error: spawn_file_actions_addclose"); + exit (1); + } + + char *argv[] = { (char *) _PATH_BSHELL, (char *) "-c", (char *) "echo $$", + NULL }; + if (posix_spawn (&pid, _PATH_BSHELL, &a, NULL, argv, NULL) != 0) + { + puts ("error: spawn failed"); + exit (1); + } + + return NULL; +} + + +static int +do_test (void) +{ + /* The test basically pipe a 'echo $$' created by a thread with a + cancellation pending. It then checks if the thread is not cancelled, + the process is created and if the output is the expected one. */ + + if (pipe (pipefd) != 0) + { + puts ("error: pipe failed"); + exit (1); + } + + /* Not interested in knowing when the pipe is closed. */ + if (sigignore (SIGPIPE) != 0) + { + puts ("error: sigignore failed"); + exit (1); + } + + /* To synchronize with the thread. */ + if (pthread_barrier_init (&b, NULL, 2) != 0) + { + puts ("error: pthread_barrier_init failed"); + exit (1); + } + + pthread_t th = xpthread_create (NULL, &tf, NULL); + + if (pthread_cancel (th) != 0) + { + puts ("error: pthread_cancel failed"); + return 1; + } + + xpthread_barrier_wait (&b); + + if (xpthread_join (th) == PTHREAD_CANCELED) + { + puts ("error: thread cancelled"); + exit (1); + } + + close (pipefd[1]); + + /* The global 'pid' should be set by thread posix_spawn calling. Check + below if it was executed correctly and with expected output. */ + + char buf[64]; + ssize_t n; + bool seen_pid = false; + while (TEMP_FAILURE_RETRY ((n = read (pipefd[0], buf, sizeof (buf)))) > 0) + { + /* We only expect to read the PID. */ + char *endp; + long int rpid = strtol (buf, &endp, 10); + + if (*endp != '\n') + { + printf ("error: didn't parse whole line: \"%s\"\n", buf); + exit (1); + } + if (endp == buf) + { + puts ("error: read empty line"); + exit (1); + } + + if (rpid != pid) + { + printf ("error: found \"%s\", expected PID %ld\n", buf, + (long int) pid); + exit (1); + } + + if (seen_pid) + { + puts ("error: found more than one PID line"); + exit (1); + } + + seen_pid = true; + } + + close (pipefd[0]); + + int status; + int err = waitpid (pid, &status, 0); + if (err != pid) + { + puts ("errnor: waitpid failed"); + exit (1); + } + + if (!seen_pid) + { + puts ("error: didn't get PID"); + exit (1); + } + + return 0; +} diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c index bb3eecfde1..5c5fcad5f4 100644 --- a/sysdeps/unix/sysv/linux/spawni.c +++ b/sysdeps/unix/sysv/linux/spawni.c @@ -340,7 +340,9 @@ __spawnix (pid_t * pid, const char *file, } /* Disable asynchronous cancellation. */ - int cs = LIBC_CANCEL_ASYNC (); + int state; + __libc_ptf_call (__pthread_setcancelstate, + (PTHREAD_CANCEL_DISABLE, &state), 0); args.file = file; args.exec = exec; @@ -386,7 +388,7 @@ __spawnix (pid_t * pid, const char *file, __sigprocmask (SIG_SETMASK, &args.oldmask, 0); - LIBC_CANCEL_RESET (cs); + __libc_ptf_call (__pthread_setcancelstate, (state, NULL), 0); return ec; }