From patchwork Fri Jun 14 15:23:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Desnoyers X-Patchwork-Id: 1116070 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-102731-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="WkA9lYr3"; dkim=pass (2048-bit key; unprotected) header.d=efficios.com header.i=@efficios.com header.b="ObCQN549"; 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 45QPX11FW3z9sDX for ; Sat, 15 Jun 2019 01:23:44 +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=ZwO2DBXPEmKmIRXahlXhAbcFdaqnohx EGg3kH7UpEHUJZFWOjrehhq0dj8ly+fg1dG/m/lfZ/i/4xezRI+STbmxFRs/kU2J +6v07AjwIbPfIwBTdCFYZx97LP2Q72/BVh1oHBTy05Daje4rqfcXyAFtO48Eiryq nzp4+9c9leFU= 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=70EXUVamNfNf9d+aren+CrHhk+o=; b=WkA9l Yr3cSo89yIdUvjbUUvXJFaZWgfWLketHkqB5bV6A3EJ/Jp6TCleGMwLTLV6DbZ7y jwFsOaAkvaqFt2e3BYUGPWfxNvsE+mv0+pF1YKAlJP3ykJu6qqdtQ+U/9B84bZXZ 3Mx6X21CnqiURwuW9OIMRmibiBZJq4AX6lpQWQ= Received: (qmail 102908 invoked by alias); 14 Jun 2019 15:23:21 -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 102783 invoked by uid 89); 14 Jun 2019 15:23:20 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.0 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=extensive, chances X-HELO: mail.efficios.com DKIM-Filter: OpenDKIM Filter v2.10.3 mail.efficios.com 12EA5251754 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=efficios.com; s=default; t=1560525791; bh=fzQvmjCkWxo8xOJT1wAmg1PlGXSw85sZ/RDHgPXkkjw=; h=From:To:Date:Message-Id; b=ObCQN549mKTyRegX/PFbZpZ7JAsKcX+40ORj/p/ILMjo1dloRDT9rTuim/CNM7ZxU AAS52woNJ+CsAaKL/0qGjD12c1NH+UXr/lwaFP/5Bh0IELFHTQE1zm3/MKp9x3u1jm gIIqFEZsS7gpox+Pq2jtcp2lCse8cBbq0KSsAH1pxHNRvOCJ7QH/xfT3Ve/lUTvl3k gFJhOBPxgHQqxH85SFez2s7i7/vstX0XefCgoa3dzcwxjcdYLZngmK5TitYAGj8E2K thz5ITHvA3taV31FTaaMZGWS53eYbqm2QO1Xu7V1PQOul3mNjAK0xy2gTxjjvhAt+7 1NK8JtR2O2etw== 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 5/5] rseq registration tests (v5) Date: Fri, 14 Jun 2019 11:23:04 -0400 Message-Id: <20190614152304.12321-6-mathieu.desnoyers@efficios.com> In-Reply-To: <20190614152304.12321-1-mathieu.desnoyers@efficios.com> References: <20190614152304.12321-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. --- ChangeLog | 7 + sysdeps/unix/sysv/linux/Makefile | 4 +- sysdeps/unix/sysv/linux/tst-rseq-nptl.c | 368 ++++++++++++++++++++++++ sysdeps/unix/sysv/linux/tst-rseq.c | 122 ++++++++ 4 files changed, 499 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 9ac9d922ca..f753b67e8a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,11 @@ 2019-04-23 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-04-23 Mathieu Desnoyers + * support/Makefile: Add xpthread_key_create and xpthread_key_delete. * support/xthread.h: Add prototype for xpthread_key_create and xpthread_key_delete. diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 5b541469ec..7e7d00e8b6 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -54,7 +54,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \ tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \ test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \ tst-rlimit-infinity tst-ofdlocks -tests-internal += tst-ofdlocks-compat +tests-internal += tst-ofdlocks-compat tst-rseq # Generate the list of SYS_* macros for the system calls (__NR_* @@ -230,5 +230,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..e695d87da3 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-rseq-nptl.c @@ -0,0 +1,368 @@ +/* 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; + pthread_attr_t at; + + if (pthread_attr_init (&at) != 0) + { + FAIL_EXIT1 ("attr_init 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_EXIT1 ("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 (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