From patchwork Wed Nov 21 18:39:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Desnoyers X-Patchwork-Id: 30254 Received: (qmail 32956 invoked by alias); 21 Nov 2018 18:48:34 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 32413 invoked by uid 89); 21 Nov 2018 18:48:07 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy= X-HELO: mail.efficios.com DKIM-Filter: OpenDKIM Filter v2.10.3 mail.efficios.com 2A949133EA0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=efficios.com; s=default; t=1542825589; bh=TQaRfxIQM8wUzYhCFL0xk2kqtwMIKOaBI9OjcHkYeB0=; h=From:To:Date:Message-Id; b=bepg8n2T0QlkpSmfZoyi/TgUc3kzsMPtfdcQDdEIWasHHnLP0XIH6qc4BwZcFfgrN SQdb0p6pzBnpMuBL2IKl1OH031szvJ0zNiUiSIIf6UtWfJbeu0Ku1afp3Shgr2pMRm CPFmNZMKkI6mg1Xrt7S7xDHMZx+OfXDzra9iaR1xj8YVqDK30VT7RvpF84LzqqWhCZ Eoi0T1PMdW3C3H530ilH/GrGioqFbA70nPt640M+8TFhqWqDN7UCz8uimy2YuWdE5n 0Cnp+HIRp5uY4VKBvdW6r6YH6Av6LNqfUhb72jhDNXJHwvc6v79SGt7T6vCV75gosz Nn2dsskTXL6sA== From: Mathieu Desnoyers To: Carlos O'Donell Cc: Florian Weimer , Joseph Myers , Szabolcs Nagy , libc-alpha@sourceware.org, Mathieu Desnoyers , Thomas Gleixner , Ben Maurer , Peter Zijlstra , "Paul E. McKenney" , Boqun Feng , Will Deacon , Dave Watson , Paul Turner Subject: [RFC PATCH v4 5/5] rseq registration tests Date: Wed, 21 Nov 2018 13:39:36 -0500 Message-Id: <20181121183936.8176-5-mathieu.desnoyers@efficios.com> In-Reply-To: <20181121183936.8176-1-mathieu.desnoyers@efficios.com> References: <20181121183936.8176-1-mathieu.desnoyers@efficios.com> These tests validate that rseq is registered from various execution contexts (main thread, constructor, destructor, other threads, other threads created from constructor and destructor, forked process (without exec), pthread_atfork handlers, pthread setspecific destructors, C++ thread and process destructors, and signal handlers). See the Linux kernel selftests for extensive rseq stress-tests. Signed-off-by: Mathieu Desnoyers CC: Carlos O'Donell CC: Florian Weimer CC: Joseph Myers CC: Szabolcs Nagy CC: Thomas Gleixner CC: Ben Maurer CC: Peter Zijlstra CC: "Paul E. McKenney" CC: Boqun Feng CC: Will Deacon CC: Dave Watson CC: Paul Turner CC: libc-alpha@sourceware.org --- nptl/Makefile | 2 +- nptl/tst-rseq.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 nptl/tst-rseq.c diff --git a/nptl/Makefile b/nptl/Makefile index 3a5dc80c65..797309f7a8 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -323,7 +323,7 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \ tests-internal := tst-rwlock19 tst-rwlock20 \ tst-sem11 tst-sem12 tst-sem13 \ tst-barrier5 tst-signal7 tst-mutex8 tst-mutex8-static \ - tst-mutexpi8 tst-mutexpi8-static + tst-mutexpi8 tst-mutexpi8-static tst-rseq xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 diff --git a/nptl/tst-rseq.c b/nptl/tst-rseq.c new file mode 100644 index 0000000000..c55345e44d --- /dev/null +++ b/nptl/tst-rseq.c @@ -0,0 +1,346 @@ +/* Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Mathieu Desnoyers , 2018. + + 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 + . */ + +/* These tests validate that rseq is registered from various execution + contexts (main thread, constructor, destructor, other threads, other + threads created from constructor and destructor, forked process + (without exec), pthread_atfork handlers, pthread setspecific + destructors, C++ thread and process destructors, and signal handlers). + + See the Linux kernel selftests for extensive rseq stress-tests. */ + +#include +#include +#include +#include + +#if defined (__linux__) && defined (__NR_rseq) +#define HAS_RSEQ +#endif + +#ifdef HAS_RSEQ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pthread_key_t rseq_test_key; + +extern __thread volatile struct rseq __rseq_abi +__attribute__ ((tls_model ("initial-exec"))); + +static int +rseq_thread_registered (void) +{ + return __rseq_abi.cpu_id >= 0; +} + +static int +do_rseq_main_test (void) +{ + if (raise (SIGUSR1)) + FAIL_EXIT1 ("error raising signal"); + return rseq_thread_registered () ? 0 : 1; +} + +static void +cancel_routine (void *arg) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in cancel routine"); +} + +static int cancel_thread_ready; + +static void +test_cancel_thread (void) +{ + pthread_cleanup_push (cancel_routine, NULL); + atomic_store_release (&cancel_thread_ready, 1); + for (;;) + usleep (100); + pthread_cleanup_pop (0); +} + +static void * +thread_function (void * arg) +{ + int i = (int) (intptr_t) arg; + + if (raise (SIGUSR1)) + FAIL_EXIT1 ("error raising signal"); + if (i == 0) + test_cancel_thread (); + if (pthread_setspecific (rseq_test_key, (void *) 1l)) + FAIL_EXIT1 ("error in pthread_setspecific"); + return rseq_thread_registered () ? NULL : (void *) 1l; +} + +static void +sighandler (int sig) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in signal handler"); +} + +static int +setup_signals (void) +{ + struct sigaction sa; + + sigemptyset (&sa.sa_mask); + sigaddset (&sa.sa_mask, SIGUSR1); + sa.sa_flags = 0; + sa.sa_handler = sighandler; + if (sigaction (SIGUSR1, &sa, NULL) != 0) + { + FAIL_RET ("sigaction failure: %s", strerror (errno)); + } + return 0; +} + +#define N 7 +static const int t[N] = { 1, 2, 6, 5, 4, 3, 50 }; + +static int +do_rseq_threads_test (int nr_threads) +{ + pthread_t th[nr_threads]; + int i; + int result = 0; + pthread_attr_t at; + + if (pthread_attr_init (&at) != 0) + { + FAIL_RET ("attr_init failed"); + } + + if (pthread_attr_setstacksize (&at, 1 * 1024 * 1024) != 0) + { + FAIL_RET ("attr_setstacksize failed"); + } + + cancel_thread_ready = 0; + for (i = 0; i < nr_threads; ++i) + if (pthread_create (&th[i], NULL, thread_function, + (void *) (intptr_t) i) != 0) + { + FAIL_EXIT1 ("creation of thread %d failed", i); + } + + if (pthread_attr_destroy (&at) != 0) + { + FAIL_RET ("attr_destroy failed"); + } + + while (!atomic_load_acquire (&cancel_thread_ready)) + usleep (100); + + if (pthread_cancel (th[0])) + FAIL_EXIT1 ("error in pthread_cancel"); + + for (i = 0; i < nr_threads; ++i) + { + void *v; + if (pthread_join (th[i], &v) != 0) + { + printf ("join of thread %d failed\n", i); + result = 1; + } + else if (i != 0 && v != NULL) + { + printf ("join %d successful, but child failed\n", i); + result = 1; + } + else if (i == 0 && v == NULL) + { + printf ("join %d successful, child did not fail as expected\n", i); + result = 1; + } + } + return result; +} + +static int +sys_rseq (volatile struct rseq *rseq_abi, uint32_t rseq_len, + int flags, uint32_t sig) +{ + return syscall (__NR_rseq, rseq_abi, rseq_len, flags, sig); +} + +static int +rseq_available (void) +{ + int rc; + + rc = sys_rseq (NULL, 0, 0, 0); + if (rc != -1) + FAIL_EXIT1 ("Unexpected rseq return value %d", rc); + switch (errno) + { + case ENOSYS: + return 0; + case EINVAL: + return 1; + default: + FAIL_EXIT1 ("Unexpected rseq error %s", strerror (errno)); + } +} + +static int +do_rseq_fork_test (void) +{ + int status; + pid_t pid, retpid; + + pid = fork (); + switch (pid) + { + case 0: + if (do_rseq_main_test ()) + FAIL_EXIT1 ("rseq not registered in child"); + exit (0); + case -1: + FAIL_EXIT1 ("Unexpected fork error %s", strerror (errno)); + } + retpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); + if (retpid != pid) + { + FAIL_EXIT1 ("waitpid returned %ld, expected %ld", + (long int) retpid, (long int) pid); + } + return WEXITSTATUS (status); +} + +static int +do_rseq_test (void) +{ + int i, result = 0; + + if (!rseq_available ()) + { + FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); + } + if (setup_signals ()) + FAIL_EXIT1 ("error setting up signal handler"); + if (raise (SIGUSR1)) + FAIL_EXIT1 ("error raising signal"); + if (do_rseq_main_test ()) + result = 1; + for (i = 0; i < N; i++) + { + if (do_rseq_threads_test (t[i])) + result = 1; + } + if (do_rseq_fork_test ()) + result = 1; + return result; +} + +static void +atfork_prepare (void) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in pthread atfork prepare"); +} + +static void +atfork_parent (void) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in pthread atfork parent"); +} + +static void +atfork_child (void) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in pthread atfork child"); +} + +static void +rseq_key_destructor (void *arg) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in pthread key destructor"); +} + +static void +do_rseq_create_key (void) +{ + if (pthread_key_create (&rseq_test_key, rseq_key_destructor)) + FAIL_EXIT1 ("error in pthread_key_create"); +} + +static void +do_rseq_delete_key (void) +{ + if (pthread_key_delete (rseq_test_key)) + FAIL_EXIT1 ("error in pthread_key_delete"); +} + +static void __attribute__ ((constructor)) +do_rseq_constructor_test (void) +{ + support_record_failure_init (); + do_rseq_create_key (); + if (pthread_atfork (atfork_prepare, atfork_parent, atfork_child)) + FAIL_EXIT1 ("error calling pthread_atfork"); + if (do_rseq_test ()) + FAIL_EXIT1 ("rseq not registered within constructor"); +} + +static void __attribute__ ((destructor)) +do_rseq_destructor_test (void) +{ + if (do_rseq_test ()) + FAIL_EXIT1 ("rseq not registered within destructor"); + do_rseq_delete_key (); +} + +/* Test C++ destructor called at thread and process exit. */ +void +__call_tls_dtors (void) +{ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in C++ thread/process exit destructor"); +} +#else +static int +do_rseq_test (void) +{ + FAIL_UNSUPPORTED ("kernel headers do not support rseq, skipping test"); + return 0; +} +#endif + +static int +do_test (void) +{ + return do_rseq_test (); +} + +#include