1 // SPDX-License-Identifier: LGPL-2.1 2 /* 3 * rseq.c 4 * 5 * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; only 10 * version 2.1 of the License. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 */ 17 18 #define _GNU_SOURCE 19 #include <errno.h> 20 #include <sched.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <syscall.h> 26 #include <assert.h> 27 #include <signal.h> 28 #include <limits.h> 29 30 #include "../kselftest.h" 31 #include "rseq.h" 32 33 __thread volatile struct rseq __rseq_abi = { 34 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, 35 }; 36 37 /* 38 * Shared with other libraries. This library may take rseq ownership if it is 39 * still 0 when executing the library constructor. Set to 1 by library 40 * constructor when handling rseq. Set to 0 in destructor if handling rseq. 41 */ 42 int __rseq_handled; 43 44 /* Whether this library have ownership of rseq registration. */ 45 static int rseq_ownership; 46 47 static __thread volatile uint32_t __rseq_refcount; 48 49 static void signal_off_save(sigset_t *oldset) 50 { 51 sigset_t set; 52 int ret; 53 54 sigfillset(&set); 55 ret = pthread_sigmask(SIG_BLOCK, &set, oldset); 56 if (ret) 57 abort(); 58 } 59 60 static void signal_restore(sigset_t oldset) 61 { 62 int ret; 63 64 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL); 65 if (ret) 66 abort(); 67 } 68 69 static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, 70 int flags, uint32_t sig) 71 { 72 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 73 } 74 75 int rseq_register_current_thread(void) 76 { 77 int rc, ret = 0; 78 sigset_t oldset; 79 80 if (!rseq_ownership) 81 return 0; 82 signal_off_save(&oldset); 83 if (__rseq_refcount == UINT_MAX) { 84 ret = -1; 85 goto end; 86 } 87 if (__rseq_refcount++) 88 goto end; 89 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG); 90 if (!rc) { 91 assert(rseq_current_cpu_raw() >= 0); 92 goto end; 93 } 94 if (errno != EBUSY) 95 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED; 96 ret = -1; 97 __rseq_refcount--; 98 end: 99 signal_restore(oldset); 100 return ret; 101 } 102 103 int rseq_unregister_current_thread(void) 104 { 105 int rc, ret = 0; 106 sigset_t oldset; 107 108 if (!rseq_ownership) 109 return 0; 110 signal_off_save(&oldset); 111 if (!__rseq_refcount) { 112 ret = -1; 113 goto end; 114 } 115 if (--__rseq_refcount) 116 goto end; 117 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 118 RSEQ_FLAG_UNREGISTER, RSEQ_SIG); 119 if (!rc) 120 goto end; 121 __rseq_refcount = 1; 122 ret = -1; 123 end: 124 signal_restore(oldset); 125 return ret; 126 } 127 128 int32_t rseq_fallback_current_cpu(void) 129 { 130 int32_t cpu; 131 132 cpu = sched_getcpu(); 133 if (cpu < 0) { 134 perror("sched_getcpu()"); 135 abort(); 136 } 137 return cpu; 138 } 139 140 void __attribute__((constructor)) rseq_init(void) 141 { 142 /* Check whether rseq is handled by another library. */ 143 if (__rseq_handled) 144 return; 145 __rseq_handled = 1; 146 rseq_ownership = 1; 147 } 148 149 void __attribute__((destructor)) rseq_fini(void) 150 { 151 if (!rseq_ownership) 152 return; 153 __rseq_handled = 0; 154 rseq_ownership = 0; 155 } 156