rseq: error out on refcount overflow/underflow
[librseq.git] / src / rseq.c
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/rseq.h>
31
32 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
33
34 /*
35 * linux/rseq.h defines struct rseq as aligned on 32 bytes. The kernel ABI
36 * size is 20 bytes. For support of multiple rseq users within a process,
37 * user-space defines an extra 4 bytes field as a reference count, for a
38 * total of 24 bytes.
39 */
40 struct libc_rseq {
41 /* kernel-userspace ABI. */
42 __u32 cpu_id_start;
43 __u32 cpu_id;
44 __u64 rseq_cs;
45 __u32 flags;
46 /* user-space ABI. */
47 __u32 refcount;
48 } __attribute__((aligned(4 * sizeof(__u64))));
49
50 __attribute__((visibility("hidden"))) __thread
51 volatile struct libc_rseq __lib_rseq_abi = {
52 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
53 };
54
55 extern __attribute__((weak, alias("__lib_rseq_abi"))) __thread
56 volatile struct rseq __rseq_abi;
57
58 static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len,
59 int flags, uint32_t sig)
60 {
61 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
62 }
63
64 int rseq_available(void)
65 {
66 int rc;
67
68 rc = sys_rseq(NULL, 0, 0, 0);
69 if (rc != -1)
70 abort();
71 switch (errno) {
72 case ENOSYS:
73 return 0;
74 case EINVAL:
75 return 1;
76 default:
77 abort();
78 }
79 }
80
81 static void signal_off_save(sigset_t *oldset)
82 {
83 sigset_t set;
84 int ret;
85
86 sigfillset(&set);
87 ret = pthread_sigmask(SIG_BLOCK, &set, oldset);
88 if (ret)
89 abort();
90 }
91
92 static void signal_restore(sigset_t oldset)
93 {
94 int ret;
95
96 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
97 if (ret)
98 abort();
99 }
100
101 int rseq_register_current_thread(void)
102 {
103 int rc, ret = 0;
104 sigset_t oldset;
105
106 signal_off_save(&oldset);
107 if (__lib_rseq_abi.refcount == INT_MAX) {
108 ret = -1;
109 goto end;
110 }
111 if (__lib_rseq_abi.refcount++)
112 goto end;
113 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
114 if (!rc) {
115 assert(rseq_current_cpu_raw() >= 0);
116 goto end;
117 }
118 if (errno != EBUSY)
119 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
120 ret = -1;
121 __lib_rseq_abi.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;
130 sigset_t oldset;
131
132 signal_off_save(&oldset);
133 if (!__lib_rseq_abi.refcount) {
134 ret = -1;
135 goto end;
136 }
137 if (--__lib_rseq_abi.refcount)
138 goto end;
139 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
140 RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
141 if (!rc)
142 goto end;
143 ret = -1;
144 end:
145 signal_restore(oldset);
146 return ret;
147 }
148
149 int32_t rseq_fallback_current_cpu(void)
150 {
151 int32_t cpu;
152
153 cpu = sched_getcpu();
154 if (cpu < 0) {
155 perror("sched_getcpu()");
156 abort();
157 }
158 return cpu;
159 }
This page took 0.033592 seconds and 5 git commands to generate.