@@ -60,12 +60,6 @@ unsigned int rseq_size = -1U;
6060/* Flags used during rseq registration. */
6161unsigned int rseq_flags ;
6262
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-
6963static int rseq_ownership ;
7064static int rseq_reg_success ; /* At least one rseq registration has succeded. */
7165
@@ -111,6 +105,43 @@ int rseq_available(void)
111105 }
112106}
113107
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+
114145int rseq_register_current_thread (void )
115146{
116147 int rc ;
@@ -119,7 +150,7 @@ int rseq_register_current_thread(void)
119150 /* Treat libc's ownership as a successful registration. */
120151 return 0 ;
121152 }
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 );
123154 if (rc ) {
124155 if (RSEQ_READ_ONCE (rseq_reg_success )) {
125156 /* Incoherent success/failure within process. */
@@ -140,28 +171,12 @@ int rseq_unregister_current_thread(void)
140171 /* Treat libc's ownership as a successful unregistration. */
141172 return 0 ;
142173 }
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 );
144175 if (rc )
145176 return -1 ;
146177 return 0 ;
147178}
148179
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-
165180static __attribute__((constructor ))
166181void rseq_init (void )
167182{
@@ -178,28 +193,54 @@ void rseq_init(void)
178193 }
179194 if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
180195 * libc_rseq_size_p != 0 ) {
196+ unsigned int libc_rseq_size ;
197+
181198 /* rseq registration owned by glibc */
182199 rseq_offset = * libc_rseq_offset_p ;
183- rseq_size = * libc_rseq_size_p ;
200+ libc_rseq_size = * libc_rseq_size_p ;
184201 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+ }
188235 return ;
189236 }
190237 rseq_ownership = 1 ;
191238 if (!rseq_available ()) {
192239 rseq_size = 0 ;
193- rseq_feature_size = 0 ;
194240 return ;
195241 }
196242 rseq_offset = (void * )& __rseq_abi - rseq_thread_pointer ();
197243 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 ;
203244}
204245
205246static __attribute__((destructor ))
@@ -209,7 +250,6 @@ void rseq_exit(void)
209250 return ;
210251 rseq_offset = 0 ;
211252 rseq_size = -1U ;
212- rseq_feature_size = -1U ;
213253 rseq_ownership = 0 ;
214254}
215255
0 commit comments