1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
|
From ff1e1631665651ceb8b4b226ec725140c7420e8c Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Thu, 9 Dec 2021 09:49:32 +0100
Subject: [PATCH 6/9] nptl: Add glibc.pthread.rseq tunable to control rseq
registration
This tunable allows applications to register the rseq area instead
of glibc.
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
-----
change:
Since rseq is a big feature and have changed code behavior quite a lot,
disable rseq by default to reduce potential conflicts.
sysdeps/nptl/dl-tunables.list:
default: 1 -> default: 0
---
manual/tunables.texi | 10 +++
nptl/pthread_create.c | 10 ++-
sysdeps/nptl/dl-tls_init_tp.c | 11 ++-
sysdeps/nptl/dl-tunables.list | 6 ++
sysdeps/nptl/internaltypes.h | 1 +
sysdeps/unix/sysv/linux/Makefile | 8 ++
sysdeps/unix/sysv/linux/rseq-internal.h | 19 +++--
sysdeps/unix/sysv/linux/tst-rseq-disable.c | 89 ++++++++++++++++++++++
8 files changed, 145 insertions(+), 9 deletions(-)
create mode 100644 sysdeps/unix/sysv/linux/tst-rseq-disable.c
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 658547c6..1f5c4102 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -413,6 +413,16 @@ The value is measured in bytes. The default is @samp{41943040}
(fourty mibibytes).
@end deftp
+@deftp Tunable glibc.pthread.rseq
+The @code{glibc.pthread.rseq} tunable can be set to @samp{0}, to disable
+restartable sequences support in @theglibc{}. This enables applications
+to perform direct restartable sequence registration with the kernel.
+The default is @samp{1}, which means that @theglibc{} performs
+registration on behalf of the application.
+
+Restartable sequences are a Linux-specific extension.
+@end deftp
+
@node Hardware Capability Tunables
@section Hardware Capability Tunables
@cindex hardware capability tunables
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index d2b40924..f405fa35 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -369,7 +369,10 @@ start_thread (void *arg)
__ctype_init ();
/* Register rseq TLS to the kernel. */
- rseq_register_current_thread (pd);
+ {
+ bool do_rseq = THREAD_GETMEM (pd, flags) & ATTR_FLAG_DO_RSEQ;
+ rseq_register_current_thread (pd, do_rseq);
+ }
#ifndef __ASSUME_SET_ROBUST_LIST
if (__nptl_set_robust_list_avail)
@@ -678,6 +681,11 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
| (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
+ /* Inherit rseq registration state. Without seccomp filters, rseq
+ registration will either always fail or always succeed. */
+ if ((int) THREAD_GETMEM_VOLATILE (self, rseq_area.cpu_id) >= 0)
+ pd->flags |= ATTR_FLAG_DO_RSEQ;
+
/* Initialize the field for the ID of the thread which is waiting
for us. This is a self-reference in case the thread is created
detached. */
diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c
index fedb876f..b39dfbff 100644
--- a/sysdeps/nptl/dl-tls_init_tp.c
+++ b/sysdeps/nptl/dl-tls_init_tp.c
@@ -23,6 +23,9 @@
#include <tls.h>
#include <rseq-internal.h>
+#define TUNABLE_NAMESPACE pthread
+#include <dl-tunables.h>
+
#ifndef __ASSUME_SET_ROBUST_LIST
bool __nptl_set_robust_list_avail;
rtld_hidden_data_def (__nptl_set_robust_list_avail)
@@ -92,7 +95,13 @@ __tls_init_tp (void)
}
}
- rseq_register_current_thread (pd);
+ {
+ bool do_rseq = true;
+#if HAVE_TUNABLES
+ do_rseq = TUNABLE_GET (rseq, int, NULL);
+#endif
+ rseq_register_current_thread (pd, do_rseq);
+ }
/* Set initial thread's stack block from 0 up to __libc_stack_end.
It will be bigger than it actually is, but for unwind.c/pt-longjmp.c
diff --git a/sysdeps/nptl/dl-tunables.list b/sysdeps/nptl/dl-tunables.list
index ac5d0532..d24f4be0 100644
--- a/sysdeps/nptl/dl-tunables.list
+++ b/sysdeps/nptl/dl-tunables.list
@@ -27,5 +27,11 @@ glibc {
type: SIZE_T
default: 41943040
}
+ rseq {
+ type: INT_32
+ minval: 0
+ maxval: 1
+ default: 0
+ }
}
}
diff --git a/sysdeps/nptl/internaltypes.h b/sysdeps/nptl/internaltypes.h
index 50a2ad19..8205c6d1 100644
--- a/sysdeps/nptl/internaltypes.h
+++ b/sysdeps/nptl/internaltypes.h
@@ -49,6 +49,7 @@ struct pthread_attr
#define ATTR_FLAG_OLDATTR 0x0010
#define ATTR_FLAG_SCHED_SET 0x0020
#define ATTR_FLAG_POLICY_SET 0x0040
+#define ATTR_FLAG_DO_RSEQ 0x0080
/* Used to allocate a pthread_attr_t object which is also accessed
internally. */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index f103a964..0657f400 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -135,6 +135,12 @@ tests-internal += \
tst-sigcontext-get_pc \
# tests-internal
+ifneq (no,$(have-tunables))
+tests-internal += \
+ tst-rseq-disable \
+ # tests-internal $(have-tunables)
+endif
+
tests-time64 += \
tst-adjtimex-time64 \
tst-clock_adjtime-time64 \
@@ -226,6 +232,8 @@ $(objpfx)tst-mman-consts.out: ../sysdeps/unix/sysv/linux/tst-mman-consts.py
< /dev/null > $@ 2>&1; $(evaluate-test)
$(objpfx)tst-mman-consts.out: $(sysdeps-linux-python-deps)
+tst-rseq-disable-ENV = GLIBC_TUNABLES=glibc.pthread.rseq=0
+
endif # $(subdir) == misc
ifeq ($(subdir),time)
diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
index 909f5478..15bc7ffd 100644
--- a/sysdeps/unix/sysv/linux/rseq-internal.h
+++ b/sysdeps/unix/sysv/linux/rseq-internal.h
@@ -21,22 +21,27 @@
#include <sysdep.h>
#include <errno.h>
#include <kernel-features.h>
+#include <stdbool.h>
#include <stdio.h>
#include <sys/rseq.h>
#ifdef RSEQ_SIG
static inline void
-rseq_register_current_thread (struct pthread *self)
+rseq_register_current_thread (struct pthread *self, bool do_rseq)
{
- int ret = INTERNAL_SYSCALL_CALL (rseq,
- &self->rseq_area, sizeof (self->rseq_area),
- 0, RSEQ_SIG);
- if (INTERNAL_SYSCALL_ERROR_P (ret))
- THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
+ if (do_rseq)
+ {
+ int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area,
+ sizeof (self->rseq_area),
+ 0, RSEQ_SIG);
+ if (!INTERNAL_SYSCALL_ERROR_P (ret))
+ return;
+ }
+ THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
}
#else /* RSEQ_SIG */
static inline void
-rseq_register_current_thread (struct pthread *self)
+rseq_register_current_thread (struct pthread *self, bool do_rseq)
{
THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
}
diff --git a/sysdeps/unix/sysv/linux/tst-rseq-disable.c b/sysdeps/unix/sysv/linux/tst-rseq-disable.c
new file mode 100644
index 00000000..000e3518
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-rseq-disable.c
@@ -0,0 +1,89 @@
+/* Test disabling of rseq registration via tunable.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xthread.h>
+#include <sysdep.h>
+#include <unistd.h>
+
+#ifdef RSEQ_SIG
+
+/* Check that rseq can be registered and has not been taken by glibc. */
+static void
+check_rseq_disabled (void)
+{
+ struct pthread *pd = THREAD_SELF;
+ TEST_COMPARE ((int) pd->rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
+
+ int ret = syscall (__NR_rseq, &pd->rseq_area, sizeof (pd->rseq_area),
+ 0, RSEQ_SIG);
+ if (ret == 0)
+ {
+ ret = syscall (__NR_rseq, &pd->rseq_area, sizeof (pd->rseq_area),
+ RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
+ TEST_COMPARE (ret, 0);
+ pd->rseq_area.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
+ }
+ else
+ {
+ TEST_VERIFY (errno != -EINVAL);
+ TEST_VERIFY (errno != -EBUSY);
+ }
+}
+
+static void *
+thread_func (void *ignored)
+{
+ check_rseq_disabled ();
+ return NULL;
+}
+
+static void
+proc_func (void *ignored)
+{
+ check_rseq_disabled ();
+}
+
+static int
+do_test (void)
+{
+ puts ("info: checking main thread");
+ check_rseq_disabled ();
+
+ puts ("info: checking main thread (2)");
+ check_rseq_disabled ();
+
+ puts ("info: checking new thread");
+ xpthread_join (xpthread_create (NULL, thread_func, NULL));
+
+ puts ("info: checking subprocess");
+ support_isolate_in_subprocess (proc_func, NULL);
+
+ return 0;
+}
+#else /* !RSEQ_SIG */
+static int
+do_test (void)
+{
+ FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test");
+}
+#endif
+
+#include <support/test-driver.c>
--
2.23.0
|