af431dd8b449a6293575143e372ec705a8ebce72
[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 struct rseq __rseq_abi = {
36 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
37 };
38
39 static __thread uint32_t __rseq_refcount;
40
41 static int sys_rseq(struct rseq *rseq_abi, uint32_t rseq_len,
42 int flags, uint32_t sig)
43 {
44 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
45 }
46
47 int rseq_available(void)
48 {
49 int rc;
50
51 rc = sys_rseq(NULL, 0, 0, 0);
52 if (rc != -1)
53 abort();
54 switch (errno) {
55 case ENOSYS:
56 return 0;
57 case EINVAL:
58 return 1;
59 default:
60 abort();
61 }
62 }
63
64 static void signal_off_save(sigset_t *oldset)
65 {
66 sigset_t set;
67 int ret;
68
69 sigfillset(&set);
70 ret = pthread_sigmask(SIG_BLOCK, &set, oldset);
71 if (ret)
72 abort();
73 }
74
75 static void signal_restore(sigset_t oldset)
76 {
77 int ret;
78
79 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
80 if (ret)
81 abort();
82 }
83
84 int rseq_register_current_thread(void)
85 {
86 int rc, ret = 0, cpu_id;
87 sigset_t oldset;
88
89 signal_off_save(&oldset);
90 cpu_id = rseq_current_cpu_raw();
91 if (cpu_id == RSEQ_CPU_ID_REGISTRATION_FAILED) {
92 errno = EPERM;
93 ret = -1;
94 goto end;
95 }
96 /*
97 * If cpu_id >= 0, rseq is already successfully registered either by
98 * libc (__rseq_refcount == 0) or by another user library
99 * (__rseq_refcount > 0) for this thread.
100 */
101 if (cpu_id >= 0) {
102 /* Treat libc's ownership as a successful registration. */
103 if (__rseq_refcount == 0)
104 goto end;
105 if (__rseq_refcount == UINT_MAX) {
106 errno = EOVERFLOW;
107 ret = -1;
108 goto end;
109 }
110 } else {
111 assert(__rseq_refcount == 0);
112 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
113 if (rc) {
114 assert(errno != EBUSY);
115 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
116 ret = -1;
117 goto end;
118 }
119 assert(rseq_current_cpu_raw() >= 0);
120 }
121 __rseq_refcount++;
122 end:
123 signal_restore(oldset);
124 return ret;
125 }
126
127 int rseq_unregister_current_thread(void)
128 {
129 int rc, ret = 0, cpu_id;
130 sigset_t oldset;
131
132 signal_off_save(&oldset);
133 cpu_id = rseq_current_cpu_raw();
134 /* cpu_id < 0 means rseq is either uninitialized or registration failed. */
135 if (cpu_id < 0) {
136 errno = EPERM;
137 ret = -1;
138 goto end;
139 }
140 /*
141 * If cpu_id >= 0, rseq is currently successfully registered either by
142 * libc (__rseq_refcount == 0) or by another user library
143 * (__rseq_refcount > 0) for this thread.
144 *
145 * Treat libc's ownership as a successful unregistration.
146 */
147 if (__rseq_refcount == 0) {
148 goto end;
149 }
150 if (__rseq_refcount == 1) {
151 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
152 RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
153 if (rc) {
154 ret = -1;
155 goto end;
156 }
157 }
158 __rseq_refcount--;
159 end:
160 signal_restore(oldset);
161 return ret;
162 }
163
164 int32_t rseq_fallback_current_cpu(void)
165 {
166 int32_t cpu;
167
168 cpu = sched_getcpu();
169 if (cpu < 0) {
170 perror("sched_getcpu()");
171 abort();
172 }
173 return cpu;
174 }
This page took 0.033567 seconds and 3 git commands to generate.