From patchwork Wed Aug 7 14:27:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Desnoyers X-Patchwork-Id: 1143546 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=sourceware.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=libc-alpha-return-104213-incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=efficios.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b="ECdGFWzI"; dkim=pass (2048-bit key; unprotected) header.d=efficios.com header.i=@efficios.com header.b="ZrzFEAw0"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 463YvR4YF1z9s3Z for ; Thu, 8 Aug 2019 00:35:31 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; q=dns; s=default; b=f0PKBtmNcO2xva+EyTtC7WyRXtyXwvl 23xXDMPXfN4Imjjdp4+heppYcpB/kk5Nlsy9c1kiMHkwujZaVaT0LShMvsde6l8u iSrYlxQSse4ar4jrbvv1Z77SP6wG+t7KN84ZXhVlQEX7P8R5E5EcOzrW0y9PkNI5 H3/H3G6UrlAw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; s=default; bh=So9K+ZtpwAg1eN/kCCHNBJCjKSY=; b=ECdGF WzIhzE5JlK9LWyUsIBMriTGzUryM44SQyYgNOi1E3NYYuhzebswRUwQ5UCH5h4rE 3Vmk+2smmUqulgCuv8ktxrTxPsUlJzeqa4BGgPrd0MjG0UKAMTcTTXLJkSRRX9GP MkyYv1IwUYGlfB6MhSaFz0498TR+Mq60JdBytU= Received: (qmail 94849 invoked by alias); 7 Aug 2019 14:35:17 -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 94787 invoked by uid 89); 7 Aug 2019 14:35:16 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.1 required=5.0 tests=AWL, 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.1 spammy= X-HELO: mail.efficios.com DKIM-Filter: OpenDKIM Filter v2.10.3 mail.efficios.com 974F22B7468 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=efficios.com; s=default; t=1565188059; bh=trPyjS+insu/Zm9bdhQJ+wgbdqsWvcVFsYZre6arLTc=; h=From:To:Date:Message-Id; b=ZrzFEAw0HgnMBHqICSHLZZuN5YfCjzNXLWnQFBFwmmnb7rv3X2tnT8R+cufZbJQCR qrsG5qaaL0qggAfdbhA7LL0l0Qb8OAach6k4V3iZMFVcqmMIQDavz1euLugPeNPyaf nu7/c+igi/clF2m3a+Vbo8Alg4SIblLlKKECb9YtIJZNPWKBFB7SYyTx62XlTAYJ3R zsGW6bBEkw6pC+MBA1EHpaQn1Zn6HtyK+lFoPzABLb50P48LCjB/BXoL9k/FYiupZE 0Vox8N6i8AjWWk1c1f03OXRVS/BHHjjlTNoRhUl2QEJF1xJIuEsuvBweRUZbsCMUmb G9IzbObKfLfJw== 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: [PATCH glibc 2.31 5/5] rseq registration tests (v6) Date: Wed, 7 Aug 2019 10:27:26 -0400 Message-Id: <20190807142726.2579-6-mathieu.desnoyers@efficios.com> In-Reply-To: <20190807142726.2579-1-mathieu.desnoyers@efficios.com> References: <20190807142726.2579-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, 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. 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 --- 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. --- ChangeLog | 6 + sysdeps/unix/sysv/linux/Makefile | 4 +- sysdeps/unix/sysv/linux/tst-rseq-nptl.c | 357 ++++++++++++++++++++++++ sysdeps/unix/sysv/linux/tst-rseq.c | 122 ++++++++ 4 files changed, 487 insertions(+), 2 deletions(-) create mode 100644 sysdeps/unix/sysv/linux/tst-rseq-nptl.c create mode 100644 sysdeps/unix/sysv/linux/tst-rseq.c diff --git a/ChangeLog b/ChangeLog index 15fec9d4c0..3ab29a8026 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2019-08-06 Mathieu Desnoyers + + * sysdeps/unix/sysv/linux/Makefile: Add tst-rseq and tst-rseq-nptl. + * sysdeps/unix/sysv/linux/tst-rseq-nptl.c: New file. + * sysdeps/unix/sysv/linux/tst-rseq.c: Likewise. + 2019-08-06 Mathieu Desnoyers * support/Makefile: Add xpthread_key_create and xpthread_key_delete. diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index f2bbe25c02..9b89e5442d 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -55,7 +55,7 @@ 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 +tests-internal += tst-ofdlocks-compat tst-rseq # Generate the list of SYS_* macros for the system calls (__NR_* # macros). The file syscall-names.list contains all possible system @@ -245,5 +245,5 @@ ifeq ($(subdir),nptl) tests += tst-align-clone tst-getpid1 \ tst-thread-affinity-pthread tst-thread-affinity-pthread2 \ tst-thread-affinity-sched -tests-internal += tst-setgetname +tests-internal += tst-setgetname 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..9e535b9105 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-rseq-nptl.c @@ -0,0 +1,357 @@ +/* Restartable Sequences NPTL test. + + Copyright (C) 2019 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, 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, signal handlers, + atexit handlers). + + See the Linux kernel selftests for extensive rseq stress-tests. */ + +#include +#include +#include +#include +#include + +#ifdef __NR_rseq +#include +#endif + +#if defined __NR_rseq && defined RSEQ_SIG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pthread_key_t rseq_test_key; + +static int +rseq_thread_registered (void) +{ + return (int32_t) atomic_load_relaxed (&__rseq_abi.cpu_id) >= 0; +} + +static int +do_rseq_main_test (void) +{ + if (raise (SIGUSR1)) + FAIL_EXIT1 ("error raising signal"); + if (pthread_setspecific (rseq_test_key, (void *) 1l)) + FAIL_EXIT1 ("error in pthread_setspecific"); + if (!rseq_thread_registered ()) + { + FAIL_RET ("rseq not registered in main thread"); + } + return 0; +} + +static void +cancel_routine (void *arg) +{ + if (!rseq_thread_registered ()) + { + printf ("rseq not registered in cancel routine\n"); + support_record_failure (); + } +} + +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 ()) + { + printf ("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; + if (sigaction (SIGUSR1, &sa, NULL) != 0) + { + FAIL_EXIT1 ("sigaction failure: %s", strerror (errno)); + } +} + +#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; + + 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); + } + + 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 (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: + exit (do_rseq_main_test ()); + 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); + } + if (WEXITSTATUS (status)) + { + printf ("rseq not registered in child\n"); + return 1; + } + return 0; +} + +static int +do_rseq_test (void) +{ + int i, result = 0; + + if (!rseq_available ()) + { + FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); + } + setup_signals (); + 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 ()) + { + printf ("rseq not registered in pthread atfork prepare\n"); + support_record_failure (); + } +} + +static void +atfork_parent (void) +{ + if (!rseq_thread_registered ()) + { + printf ("rseq not registered in pthread atfork parent\n"); + support_record_failure (); + } +} + +static void +atfork_child (void) +{ + if (!rseq_thread_registered ()) + { + printf ("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 __attribute__ ((constructor)) +do_rseq_constructor_test (void) +{ + support_record_failure_init (); + if (!__rseq_handled) + FAIL_EXIT1 ("rseq registration ownership is not published by libc"); + if (atexit (atexit_handler)) + FAIL_EXIT1 ("error calling atexit"); + xpthread_key_create (&rseq_test_key, rseq_key_destructor); + 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) +{ + /* 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); +} + +/* Test C++ destructor called at thread and process exit. */ +void +__call_tls_dtors (void) +{ + /* Cannot use deferred failure reporting after main () returns. */ + if (!rseq_thread_registered ()) + FAIL_EXIT1 ("rseq not registered in C++ thread/process exit destructor"); +} +#else +static int +do_rseq_test (void) +{ +#ifndef __NR_rseq + FAIL_UNSUPPORTED ("kernel headers do not support rseq, skipping test"); +#endif +#ifndef RSEQ_SIG + FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test"); +#endif + return 0; +} +#endif + +static int +do_test (void) +{ + if (!__rseq_handled) + FAIL_EXIT1 ("rseq registration ownership is not published by libc"); + 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..53e70a7c88 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-rseq.c @@ -0,0 +1,122 @@ +/* Restartable Sequences single-threaded tests. + + Copyright (C) 2019 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 __NR_rseq +#include +#endif + +#if defined __NR_rseq && defined RSEQ_SIG +#include +#include +#include +#include +#include +#include +#include + +static int +rseq_thread_registered (void) +{ + return (int32_t) atomic_load_relaxed (&__rseq_abi.cpu_id) >= 0; +} + +static int +do_rseq_main_test (void) +{ + if (!rseq_thread_registered ()) + { + FAIL_RET ("rseq not registered in main thread"); + } + return 0; +} + +static 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 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_test (void) +{ + int result = 0; + + if (!rseq_available ()) + { + FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); + } + if (do_rseq_main_test ()) + result = 1; + return result; +} +#else +static int +do_rseq_test (void) +{ +#ifndef __NR_rseq + FAIL_UNSUPPORTED ("kernel headers do not support rseq, skipping test"); +#endif +#ifndef RSEQ_SIG + FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test"); +#endif + return 0; +} +#endif + +/* Test whether glibc correctly sets __rseq_handled ownership by + providing an instance of the symbol within the executable. + The dynamic linker ensures all libraries and the executable + agree on choosing a single copy of this global symbol. */ +int __rseq_handled; + +static int +do_test (void) +{ + if (!__rseq_handled) + FAIL_RET ("rseq registration ownership is not published by libc"); + return do_rseq_test (); +} + +#include