@@ -60,12 +60,6 @@ unsigned int rseq_size = -1U;
60
60
/* Flags used during rseq registration. */
61
61
unsigned int rseq_flags ;
62
62
63
- /*
64
- * rseq feature size supported by the kernel. 0 if the registration was
65
- * unsuccessful.
66
- */
67
- unsigned int rseq_feature_size = -1U ;
68
-
69
63
static int rseq_ownership ;
70
64
static int rseq_reg_success ; /* At least one rseq registration has succeded. */
71
65
@@ -111,6 +105,43 @@ int rseq_available(void)
111
105
}
112
106
}
113
107
108
+ /* The rseq areas need to be at least 32 bytes. */
109
+ static
110
+ unsigned int get_rseq_min_alloc_size (void )
111
+ {
112
+ unsigned int alloc_size = rseq_size ;
113
+
114
+ if (alloc_size < ORIG_RSEQ_ALLOC_SIZE )
115
+ alloc_size = ORIG_RSEQ_ALLOC_SIZE ;
116
+ return alloc_size ;
117
+ }
118
+
119
+ /*
120
+ * Return the feature size supported by the kernel.
121
+ *
122
+ * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE):
123
+ *
124
+ * 0: Return ORIG_RSEQ_FEATURE_SIZE (20)
125
+ * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE).
126
+ *
127
+ * It should never return a value below ORIG_RSEQ_FEATURE_SIZE.
128
+ */
129
+ static
130
+ unsigned int get_rseq_kernel_feature_size (void )
131
+ {
132
+ unsigned long auxv_rseq_feature_size , auxv_rseq_align ;
133
+
134
+ auxv_rseq_align = getauxval (AT_RSEQ_ALIGN );
135
+ assert (!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE );
136
+
137
+ auxv_rseq_feature_size = getauxval (AT_RSEQ_FEATURE_SIZE );
138
+ assert (!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE );
139
+ if (auxv_rseq_feature_size )
140
+ return auxv_rseq_feature_size ;
141
+ else
142
+ return ORIG_RSEQ_FEATURE_SIZE ;
143
+ }
144
+
114
145
int rseq_register_current_thread (void )
115
146
{
116
147
int rc ;
@@ -119,7 +150,7 @@ int rseq_register_current_thread(void)
119
150
/* Treat libc's ownership as a successful registration. */
120
151
return 0 ;
121
152
}
122
- rc = sys_rseq (& __rseq_abi , rseq_size , 0 , RSEQ_SIG );
153
+ rc = sys_rseq (& __rseq_abi , get_rseq_min_alloc_size () , 0 , RSEQ_SIG );
123
154
if (rc ) {
124
155
if (RSEQ_READ_ONCE (rseq_reg_success )) {
125
156
/* Incoherent success/failure within process. */
@@ -140,28 +171,12 @@ int rseq_unregister_current_thread(void)
140
171
/* Treat libc's ownership as a successful unregistration. */
141
172
return 0 ;
142
173
}
143
- rc = sys_rseq (& __rseq_abi , rseq_size , RSEQ_ABI_FLAG_UNREGISTER , RSEQ_SIG );
174
+ rc = sys_rseq (& __rseq_abi , get_rseq_min_alloc_size () , RSEQ_ABI_FLAG_UNREGISTER , RSEQ_SIG );
144
175
if (rc )
145
176
return -1 ;
146
177
return 0 ;
147
178
}
148
179
149
- static
150
- unsigned int get_rseq_feature_size (void )
151
- {
152
- unsigned long auxv_rseq_feature_size , auxv_rseq_align ;
153
-
154
- auxv_rseq_align = getauxval (AT_RSEQ_ALIGN );
155
- assert (!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE );
156
-
157
- auxv_rseq_feature_size = getauxval (AT_RSEQ_FEATURE_SIZE );
158
- assert (!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE );
159
- if (auxv_rseq_feature_size )
160
- return auxv_rseq_feature_size ;
161
- else
162
- return ORIG_RSEQ_FEATURE_SIZE ;
163
- }
164
-
165
180
static __attribute__((constructor ))
166
181
void rseq_init (void )
167
182
{
@@ -178,28 +193,54 @@ void rseq_init(void)
178
193
}
179
194
if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
180
195
* libc_rseq_size_p != 0 ) {
196
+ unsigned int libc_rseq_size ;
197
+
181
198
/* rseq registration owned by glibc */
182
199
rseq_offset = * libc_rseq_offset_p ;
183
- rseq_size = * libc_rseq_size_p ;
200
+ libc_rseq_size = * libc_rseq_size_p ;
184
201
rseq_flags = * libc_rseq_flags_p ;
185
- rseq_feature_size = get_rseq_feature_size ();
186
- if (rseq_feature_size > rseq_size )
187
- rseq_feature_size = rseq_size ;
202
+
203
+ /*
204
+ * Previous versions of glibc expose the value
205
+ * 32 even though the kernel only supported 20
206
+ * bytes initially. Therefore treat 32 as a
207
+ * special-case. glibc 2.40 exposes a 20 bytes
208
+ * __rseq_size without using getauxval(3) to
209
+ * query the supported size, while still allocating a 32
210
+ * bytes area. Also treat 20 as a special-case.
211
+ *
212
+ * Special-cases are handled by using the following
213
+ * value as active feature set size:
214
+ *
215
+ * rseq_size = min(32, get_rseq_kernel_feature_size())
216
+ */
217
+ switch (libc_rseq_size ) {
218
+ case ORIG_RSEQ_FEATURE_SIZE :
219
+ fallthrough ;
220
+ case ORIG_RSEQ_ALLOC_SIZE :
221
+ {
222
+ unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size ();
223
+
224
+ if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE )
225
+ rseq_size = rseq_kernel_feature_size ;
226
+ else
227
+ rseq_size = ORIG_RSEQ_ALLOC_SIZE ;
228
+ break ;
229
+ }
230
+ default :
231
+ /* Otherwise just use the __rseq_size from libc as rseq_size. */
232
+ rseq_size = libc_rseq_size ;
233
+ break ;
234
+ }
188
235
return ;
189
236
}
190
237
rseq_ownership = 1 ;
191
238
if (!rseq_available ()) {
192
239
rseq_size = 0 ;
193
- rseq_feature_size = 0 ;
194
240
return ;
195
241
}
196
242
rseq_offset = (void * )& __rseq_abi - rseq_thread_pointer ();
197
243
rseq_flags = 0 ;
198
- rseq_feature_size = get_rseq_feature_size ();
199
- if (rseq_feature_size == ORIG_RSEQ_FEATURE_SIZE )
200
- rseq_size = ORIG_RSEQ_ALLOC_SIZE ;
201
- else
202
- rseq_size = RSEQ_THREAD_AREA_ALLOC_SIZE ;
203
244
}
204
245
205
246
static __attribute__((destructor ))
@@ -209,7 +250,6 @@ void rseq_exit(void)
209
250
return ;
210
251
rseq_offset = 0 ;
211
252
rseq_size = -1U ;
212
- rseq_feature_size = -1U ;
213
253
rseq_ownership = 0 ;
214
254
}
215
255
0 commit comments