From patchwork Mon Jun 22 18:08:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Desnoyers X-Patchwork-Id: 39769 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DB04B388E838; Mon, 22 Jun 2020 18:08:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DB04B388E838 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1592849297; bh=1WMAqGroTjgy6a1t3IWADleDhIK4sflg1ElASpj/lns=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Phd/+2MamY21eUtAsUnVCluzLj+2j77bbWOwhl6qAVLB9RzEYYDY+PNtYzL1ZbYJb GYURwoaJFf5neN5CpoxhmJqFwSeL2n5KnBofLnMgeK+c5cUfwCEIW32NtxdNEja53m wWCnOW3vokpLfrHw0L9eyabwnLAe03CneGzonnkw= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail.efficios.com (mail.efficios.com [167.114.26.124]) by sourceware.org (Postfix) with ESMTPS id 571A2388A83C for ; Mon, 22 Jun 2020 18:08:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 571A2388A83C Received: from localhost (localhost [127.0.0.1]) by mail.efficios.com (Postfix) with ESMTP id 1E4302B6BAD; Mon, 22 Jun 2020 14:08:11 -0400 (EDT) Received: from mail.efficios.com ([127.0.0.1]) by localhost (mail03.efficios.com [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id NM71iAcsPm0E; Mon, 22 Jun 2020 14:08:10 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by mail.efficios.com (Postfix) with ESMTP id 4C2F42B6F32; Mon, 22 Jun 2020 14:08:10 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.efficios.com 4C2F42B6F32 X-Virus-Scanned: amavisd-new at efficios.com Received: from mail.efficios.com ([127.0.0.1]) by localhost (mail03.efficios.com [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id f9jIC-DhQFHd; Mon, 22 Jun 2020 14:08:10 -0400 (EDT) Received: from localhost.localdomain (192-222-181-218.qc.cable.ebox.net [192.222.181.218]) by mail.efficios.com (Postfix) with ESMTPSA id EDC562B6F2A; Mon, 22 Jun 2020 14:08:09 -0400 (EDT) To: Florian Weimer Subject: [PATCH 3/3] rseq registration tests (v11) Date: Mon, 22 Jun 2020 14:08:03 -0400 Message-Id: <20200622180803.1449-4-mathieu.desnoyers@efficios.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200622180803.1449-1-mathieu.desnoyers@efficios.com> References: <20200622180803.1449-1-mathieu.desnoyers@efficios.com> X-Spam-Status: No, score=-16.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Mathieu Desnoyers via Libc-alpha From: Mathieu Desnoyers Reply-To: Mathieu Desnoyers Cc: libc-alpha@sourceware.org, Boqun Feng , Will Deacon , Peter Zijlstra , Ben Maurer , Mathieu Desnoyers , Thomas Gleixner , "Paul E. McKenney" , Paul Turner , Joseph Myers Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" These tests validate that rseq is registered from various execution contexts (main thread, destructor, other threads, other threads created from destructor, forked process (without exec), pthread_atfork handlers, pthread setspecific destructors, signal handlers, atexit handlers). tst-rseq.c only links against libc.so, testing registration of rseq in a non-multithreaded environment. tst-rseq-nptl.c also links against libpthread.so, testing registration of rseq in a multithreaded environment. See the Linux kernel selftests for extensive rseq stress-tests. 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: Paul Turner CC: libc-alpha@sourceware.org --- Changes since v1: - Rename tst-rseq.c to tst-rseq-nptl.c. - Introduce tst-rseq.c testing rseq registration in a non-multithreaded environment. Chances since v2: - Update file headers. - use xpthread key create/delete. - remove set stacksize. - Tests depend on both __NR_rseq and RSEQ_SIG being defined. Changes since v3: - Update ChangeLog. Changes since v4: - Remove volatile from sys_rseq() rseq_abi parameter. - Use atomic_load_relaxed to load __rseq_abi.cpu_id, consequence of the fact that __rseq_abi is not volatile anymore. - Include atomic.h from tst-rseq.c for use of atomic_load_relaxed. Move tst-rseq.c to internal tests within Makefile due to its use of atomic.h. - Test __rseq_handled initialization by glibc. Changes since v5: - Rebase on glibc 2.30. Changes since v6: - Remove __rseq_handled. Changes since v7: - Update copyright range to include 2020. - Use __ASSUME_RSEQ to detect rseq availability. Changes since v8: - Remove use of __ASSUME_RSEQ. Changes since v9: - Adapt to new prototype for xpthread_key_create. - Update copyright year to 2020. - Remove constructor test (moved to later patch due to test harness modification dependency). - Change http:// for https://. Changes since v10: - Introduce rseq-tst.h for common helpers. - Take care of comments from Florian. --- sysdeps/unix/sysv/linux/Makefile | 10 +- sysdeps/unix/sysv/linux/tst-rseq-nptl.c | 256 ++++++++++++++++++++++++ sysdeps/unix/sysv/linux/tst-rseq.c | 64 ++++++ sysdeps/unix/sysv/linux/tst-rseq.h | 59 ++++++ 4 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 sysdeps/unix/sysv/linux/tst-rseq-nptl.c create mode 100644 sysdeps/unix/sysv/linux/tst-rseq.c create mode 100644 sysdeps/unix/sysv/linux/tst-rseq.h diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index e855db2cb9..2ee83e88d7 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -100,7 +100,11 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \ test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \ tst-rlimit-infinity tst-ofdlocks tst-gettid tst-gettid-kill \ tst-tgkill -tests-internal += tst-ofdlocks-compat tst-sigcontext-get_pc + +# tst-rseq is an internal test because it requires a definition of __NR_rseq +# from the internal system call list. +tests-internal += tst-ofdlocks-compat tst-sigcontext-get_pc \ + tst-rseq CFLAGS-tst-sigcontext-get_pc.c = -fasynchronous-unwind-tables @@ -301,4 +305,8 @@ endif ifeq ($(subdir),nptl) tests += tst-align-clone tst-getpid1 + +# tst-rseq-nptl is an internal test because it requires a definition of +# __NR_rseq from the internal system call list. +tests-internal += tst-rseq-nptl endif diff --git a/sysdeps/unix/sysv/linux/tst-rseq-nptl.c b/sysdeps/unix/sysv/linux/tst-rseq-nptl.c new file mode 100644 index 0000000000..5e788dcfa9 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-rseq-nptl.c @@ -0,0 +1,256 @@ +/* Restartable Sequences NPTL test. + Copyright (C) 2020 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; if not, see + . */ + +/* These tests validate that rseq is registered from various execution + contexts (main thread, destructor, other threads, other threads created + from destructor, forked process (without exec), pthread_atfork handlers, + pthread setspecific destructors, signal handlers, atexit handlers). + + See the Linux kernel selftests for extensive rseq stress-tests. */ + +#include +#include +#include +#include +#include + +#ifdef RSEQ_SIG +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include "tst-rseq.h" + +static pthread_key_t rseq_test_key; + +static void +atfork_prepare (void) +{ + if (!rseq_thread_registered ()) + { + printf ("error: rseq not registered in pthread atfork prepare\n"); + support_record_failure (); + } +} + +static void +atfork_parent (void) +{ + if (!rseq_thread_registered ()) + { + printf ("error: rseq not registered in pthread atfork parent\n"); + support_record_failure (); + } +} + +static void +atfork_child (void) +{ + if (!rseq_thread_registered ()) + { + printf ("error: rseq not registered in pthread atfork child\n"); + support_record_failure (); + } +} + +static void +rseq_key_destructor (void *arg) +{ + /* Cannot use deferred failure reporting after main returns. */ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in pthread key destructor"); +} + +static void +atexit_handler (void) +{ + /* Cannot use deferred failure reporting after main returns. */ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in atexit handler"); +} + +static void +do_rseq_main_test (void) +{ + TEST_COMPARE (atexit (atexit_handler), 0); + rseq_test_key = xpthread_key_create (rseq_key_destructor); + TEST_COMPARE (pthread_atfork (atfork_prepare, atfork_parent, atfork_child), 0); + xraise (SIGUSR1); + TEST_COMPARE (pthread_setspecific (rseq_test_key, (void *) 1l), 0); + TEST_VERIFY_EXIT (rseq_thread_registered ()); +} + +static void +cancel_routine (void *arg) +{ + if (!rseq_thread_registered ()) + { + printf ("error: rseq not registered in cancel routine\n"); + support_record_failure (); + } +} + +static pthread_barrier_t cancel_thread_barrier; +static pthread_cond_t cancel_thread_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t cancel_thread_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void +test_cancel_thread (void) +{ + pthread_cleanup_push (cancel_routine, NULL); + (void) xpthread_barrier_wait (&cancel_thread_barrier); + /* Wait forever until cancellation. */ + xpthread_cond_wait (&cancel_thread_cond, &cancel_thread_mutex); + pthread_cleanup_pop (0); +} + +static void * +thread_function (void * arg) +{ + int i = (int) (intptr_t) arg; + + xraise (SIGUSR1); + if (i == 0) + test_cancel_thread (); + TEST_COMPARE (pthread_setspecific (rseq_test_key, (void *) 1l), 0); + return rseq_thread_registered () ? NULL : (void *) 1l; +} + +static void +sighandler (int sig) +{ + if (!rseq_thread_registered ()) + { + printf ("error: rseq not registered in signal handler\n"); + support_record_failure (); + } +} + +static void +setup_signals (void) +{ + struct sigaction sa; + + sigemptyset (&sa.sa_mask); + sigaddset (&sa.sa_mask, SIGUSR1); + sa.sa_flags = 0; + sa.sa_handler = sighandler; + xsigaction (SIGUSR1, &sa, NULL); +} + +static int +do_rseq_threads_test (int nr_threads) +{ + pthread_t th[nr_threads]; + int i; + int result = 0; + + xpthread_barrier_init (&cancel_thread_barrier, NULL, 2); + + for (i = 0; i < nr_threads; ++i) + th[i] = xpthread_create (NULL, thread_function, + (void *) (intptr_t) i); + + (void) xpthread_barrier_wait (&cancel_thread_barrier); + + xpthread_cancel (th[0]); + + for (i = 0; i < nr_threads; ++i) + { + void *v; + + v = xpthread_join (th[i]); + if (i != 0 && v != NULL) + { + printf ("error: join %d successful, but child failed\n", i); + result = 1; + } + else if (i == 0 && v == NULL) + { + printf ("error: join %d successful, child did not fail as expected\n", i); + result = 1; + } + } + + xpthread_barrier_destroy (&cancel_thread_barrier); + + return result; +} + +static void +subprocess_callback (void *closure) +{ + do_rseq_main_test (); +} + +static void +do_rseq_fork_test (void) +{ + support_isolate_in_subprocess (subprocess_callback, NULL); +} + +static int +do_rseq_test (void) +{ + int t[] = { 1, 2, 6, 5, 4, 3, 50 }; + int i, result = 0; + + if (!rseq_available ()) + FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); + setup_signals (); + xraise (SIGUSR1); + do_rseq_main_test (); + for (i = 0; i < array_length (t); i++) + if (do_rseq_threads_test (t[i])) + result = 1; + do_rseq_fork_test (); + return result; +} + +static void __attribute__ ((destructor)) +do_rseq_destructor_test (void) +{ + /* Cannot use deferred failure reporting after main returns. */ + if (do_rseq_test ()) + FAIL_EXIT1 ("rseq not registered within destructor"); + xpthread_key_delete (rseq_test_key); +} + +#else /* RSEQ_SIG */ +static int +do_rseq_test (void) +{ + FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test"); + return 0; +} +#endif /* RSEQ_SIG */ + +static int +do_test (void) +{ + return do_rseq_test (); +} + +#include diff --git a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c new file mode 100644 index 0000000000..aa902fb26a --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-rseq.c @@ -0,0 +1,64 @@ +/* Restartable Sequences single-threaded tests. + Copyright (C) 2020 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; if not, see + . */ + +/* These tests validate that rseq is registered from main in an executable + not linked against libpthread. */ + +#include +#include +#include +#include + +#ifdef RSEQ_SIG +# include +# include +# include +# include +# include +# include "tst-rseq.h" + +static void +do_rseq_main_test (void) +{ + TEST_VERIFY_EXIT (rseq_thread_registered ()); +} + +static void +do_rseq_test (void) +{ + if (!rseq_available ()) + { + FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); + } + do_rseq_main_test (); +} +#else /* RSEQ_SIG */ +static void +do_rseq_test (void) +{ + FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test"); +} +#endif /* RSEQ_SIG */ + +static int +do_test (void) +{ + do_rseq_test (); + return 0; +} + +#include diff --git a/sysdeps/unix/sysv/linux/tst-rseq.h b/sysdeps/unix/sysv/linux/tst-rseq.h new file mode 100644 index 0000000000..c2cb211f56 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-rseq.h @@ -0,0 +1,59 @@ +/* Restartable Sequences tests header. + Copyright (C) 2020 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; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include + +static inline bool +rseq_thread_registered (void) +{ + int32_t v; + + __atomic_load (&__rseq_abi.cpu_id, &v, __ATOMIC_RELAXED); + return v >= 0; +} + +static inline int +sys_rseq (struct rseq *rseq_abi, uint32_t rseq_len, int flags, uint32_t sig) +{ + return syscall (__NR_rseq, rseq_abi, rseq_len, flags, sig); +} + +static inline bool +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 false; + case EINVAL: + /* rseq is implemented, but detected an invalid rseq_len parameter. */ + return true; + default: + FAIL_EXIT1 ("Unexpected rseq error %s", strerror (errno)); + } +}