rseq: set refcount back to 1 on unregister failure
[librseq.git] / src / rseq.c
1 // SPDX-License-Identifier: LGPL-2.1-only
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 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
20 #include <errno.h>
21 #include <sched.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <syscall.h>
27 #include <assert.h>
28 #include <signal.h>
29 #include <limits.h>
30
31 #include <rseq/rseq.h>
32
33 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
34
35 __thread volatile struct rseq __rseq_abi = {
36 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
37 };
38
39 /*
40 * Shared with other libraries. This library may take rseq ownership if it is
41 * still 0 when executing the library constructor. Set to 1 by library
42 * constructor when handling rseq. Set to 0 in destructor if handling rseq.
43 */
44 int __rseq_handled;
45
46 /* Whether this library have ownership of rseq registration. */
47 static int rseq_ownership;
48
49 static __thread volatile uint32_t __rseq_refcount;
50
51 static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len,
52 int flags, uint32_t sig)
53 {
54 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
55 }
56
57 int rseq_available(void)
58 {
59 int rc;
60
61 rc = sys_rseq(NULL, 0, 0, 0);
62 if (rc != -1)
63 abort();
64 switch (errno) {
65 case ENOSYS:
66 return 0;
67 case EINVAL:
68 return 1;
69 default:
70 abort();
71 }
72 }
73
74 static void signal_off_save(sigset_t *oldset)
75 {
76 sigset_t set;
77 int ret;
78
79 sigfillset(&set);
80 ret = pthread_sigmask(SIG_BLOCK, &set, oldset);
81 if (ret)
82 abort();
83 }
84
85 static void signal_restore(sigset_t oldset)
86 {
87 int ret;
88
89 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
90 if (ret)
91 abort();
92 }
93
94 int rseq_register_current_thread(void)
95 {
96 int rc, ret = 0;
97 sigset_t oldset;
98
99 if (!rseq_ownership)
100 return 0;
101 signal_off_save(&oldset);
102 if (__rseq_refcount == UINT_MAX) {
103 ret = -1;
104 goto end;
105 }
106 if (__rseq_refcount++)
107 goto end;
108 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
109 if (!rc) {
110 assert(rseq_current_cpu_raw() >= 0);
111 goto end;
112 }
113 if (errno != EBUSY)
114 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
115 ret = -1;
116 __rseq_refcount--;
117 end:
118 signal_restore(oldset);
119 return ret;
120 }
121
122 int rseq_unregister_current_thread(void)
123 {
124 int rc, ret = 0;
125 sigset_t oldset;
126
127 if (!rseq_ownership)
128 return 0;
129 signal_off_save(&oldset);
130 if (!__rseq_refcount) {
131 ret = -1;
132 goto end;
133 }
134 if (--__rseq_refcount)
135 goto end;
136 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
137 RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
138 if (!rc)
139 goto end;
140 __rseq_refcount = 1;
141 ret = -1;
142 end:
143 signal_restore(oldset);
144 return ret;
145 }
146
147 int32_t rseq_fallback_current_cpu(void)
148 {
149 int32_t cpu;
150
151 cpu = sched_getcpu();
152 if (cpu < 0) {
153 perror("sched_getcpu()");
154 abort();
155 }
156 return cpu;
157 }
158
159 void __attribute__((constructor)) rseq_init(void)
160 {
161 /* Check whether rseq is handled by another library. */
162 if (__rseq_handled)
163 return;
164 __rseq_handled = 1;
165 rseq_ownership = 1;
166 }
167
168 void __attribute__((destructor)) rseq_fini(void)
169 {
170 if (!rseq_ownership)
171 return;
172 __rseq_handled = 0;
173 rseq_ownership = 0;
174 }
This page took 0.041456 seconds and 4 git commands to generate.