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 "rseq.h" 31 32 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 33 34 __thread volatile struct rseq __rseq_abi = { 35 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, 36 }; 37 38 /* 39 * Shared with other libraries. This library may take rseq ownership if it is 40 * still 0 when executing the library constructor. Set to 1 by library 41 * constructor when handling rseq. Set to 0 in destructor if handling rseq. 42 */ 43 int __rseq_handled; 44 45 /* Whether this library have ownership of rseq registration. */ 46 static int rseq_ownership; 47 48 static __thread volatile uint32_t __rseq_refcount; 49 50 static void signal_off_save(sigset_t *oldset) 51 { 52 sigset_t set; 53 int ret; 54 55 sigfillset(&set); 56 ret = pthread_sigmask(SIG_BLOCK, &set, oldset); 57 if (ret) 58 abort(); 59 } 60 61 static void signal_restore(sigset_t oldset) 62 { 63 int ret; 64 65 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL); 66 if (ret) 67 abort(); 68 } 69 70 static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, 71 int flags, uint32_t sig) 72 { 73 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 74 } 75 76 int rseq_register_current_thread(void) 77 { 78 int rc, ret = 0; 79 sigset_t oldset; 80 81 if (!rseq_ownership) 82 return 0; 83 signal_off_save(&oldset); 84 if (__rseq_refcount == UINT_MAX) { 85 ret = -1; 86 goto end; 87 } 88 if (__rseq_refcount++) 89 goto end; 90 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG); 91 if (!rc) { 92 assert(rseq_current_cpu_raw() >= 0); 93 goto end; 94 } 95 if (errno != EBUSY) 96 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED; 97 ret = -1; 98 __rseq_refcount--; 99 end: 100 signal_restore(oldset); 101 return ret; 102 } 103 104 int rseq_unregister_current_thread(void) 105 { 106 int rc, ret = 0; 107 sigset_t oldset; 108 109 if (!rseq_ownership) 110 return 0; 111 signal_off_save(&oldset); 112 if (!__rseq_refcount) { 113 ret = -1; 114 goto end; 115 } 116 if (--__rseq_refcount) 117 goto end; 118 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 119 RSEQ_FLAG_UNREGISTER, RSEQ_SIG); 120 if (!rc) 121 goto end; 122 __rseq_refcount = 1; 123 ret = -1; 124 end: 125 signal_restore(oldset); 126 return ret; 127 } 128 129 int32_t rseq_fallback_current_cpu(void) 130 { 131 int32_t cpu; 132 133 cpu = sched_getcpu(); 134 if (cpu < 0) { 135 perror("sched_getcpu()"); 136 abort(); 137 } 138 return cpu; 139 } 140 141 void __attribute__((constructor)) rseq_init(void) 142 { 143 /* Check whether rseq is handled by another library. */ 144 if (__rseq_handled) 145 return; 146 __rseq_handled = 1; 147 rseq_ownership = 1; 148 } 149 150 void __attribute__((destructor)) rseq_fini(void) 151 { 152 if (!rseq_ownership) 153 return; 154 __rseq_handled = 0; 155 rseq_ownership = 0; 156 } 157