1/* x86-64 CET initializers function.
2 Copyright (C) 2018-2024 Free Software Foundation, Inc.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18#include <unistd.h>
19#include <errno.h>
20#include <libintl.h>
21#include <ldsodefs.h>
22#include <dl-cet.h>
23#include <sys/single_threaded.h>
24
25/* GNU_PROPERTY_X86_FEATURE_1_IBT and GNU_PROPERTY_X86_FEATURE_1_SHSTK
26 are defined in <elf.h>, which are only available for C sources.
27 X86_FEATURE_1_IBT and X86_FEATURE_1_SHSTK are defined in <sysdep.h>
28 which are available for both C and asm sources. They must match. */
29#if GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT
30# error GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT
31#endif
32#if GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
33# error GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
34#endif
35
36struct dl_cet_info
37{
38 const char *program;
39
40 /* Check how IBT and SHSTK should be enabled. */
41 enum dl_x86_cet_control enable_ibt_type;
42 enum dl_x86_cet_control enable_shstk_type;
43
44 /* If IBT and SHSTK were previously enabled. */
45 unsigned int feature_1_enabled;
46
47 /* If IBT and SHSTK should be enabled. */
48 unsigned int enable_feature_1;
49
50 /* If there are any legacy shared object. */
51 unsigned int feature_1_legacy;
52
53 /* Which shared object is the first legacy shared object. */
54 unsigned int feature_1_legacy_ibt;
55 unsigned int feature_1_legacy_shstk;
56};
57
58/* Check if the object M and its dependencies are legacy object. */
59
60static void
61dl_check_legacy_object (struct link_map *m,
62 struct dl_cet_info *info)
63{
64 unsigned int i;
65 struct link_map *l = NULL;
66
67 i = m->l_searchlist.r_nlist;
68 while (i-- > 0)
69 {
70 /* Check each shared object to see if IBT and SHSTK are enabled. */
71 l = m->l_initfini[i];
72
73 if (l->l_init_called)
74 continue;
75
76#ifdef SHARED
77 /* Skip check for ld.so since it has the features enabled. The
78 features will be disabled later if they are not enabled in
79 executable. */
80 if (l == &GL(dl_rtld_map)
81 || l->l_real == &GL(dl_rtld_map)
82 || (info->program != NULL && l == m))
83 continue;
84#endif
85
86 /* IBT and SHSTK set only if enabled in executable and all DSOs.
87 NB: cet_always_on is handled outside of the loop. */
88 info->enable_feature_1 &= ((l->l_x86_feature_1_and
89 & (GNU_PROPERTY_X86_FEATURE_1_IBT
90 | GNU_PROPERTY_X86_FEATURE_1_SHSTK))
91 | ~(GNU_PROPERTY_X86_FEATURE_1_IBT
92 | GNU_PROPERTY_X86_FEATURE_1_SHSTK));
93 if ((info->feature_1_legacy
94 & GNU_PROPERTY_X86_FEATURE_1_IBT) == 0
95 && ((info->enable_feature_1
96 & GNU_PROPERTY_X86_FEATURE_1_IBT)
97 != (info->feature_1_enabled
98 & GNU_PROPERTY_X86_FEATURE_1_IBT)))
99 {
100 info->feature_1_legacy_ibt = i;
101 info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_IBT;
102 }
103
104 if ((info->feature_1_legacy
105 & GNU_PROPERTY_X86_FEATURE_1_SHSTK) == 0
106 && ((info->enable_feature_1
107 & GNU_PROPERTY_X86_FEATURE_1_SHSTK)
108 != (info->feature_1_enabled
109 & GNU_PROPERTY_X86_FEATURE_1_SHSTK)))
110 {
111 info->feature_1_legacy_shstk = i;
112 info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
113 }
114 }
115
116 /* Handle cet_always_on. */
117 if ((info->feature_1_enabled
118 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0
119 && info->enable_ibt_type == cet_always_on)
120 {
121 info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
122 info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
123 }
124
125 if ((info->feature_1_enabled
126 & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0
127 && info->enable_shstk_type == cet_always_on)
128 {
129 info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
130 info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
131 }
132}
133
134#ifdef SHARED
135/* Enable IBT and SHSTK only if they are enabled in executable. Set
136 feature bits properly at the start of the program. */
137
138static void
139dl_cet_check_startup (struct link_map *m, struct dl_cet_info *info)
140{
141 /* NB: IBT and SHSTK may be disabled by environment variable:
142
143 GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK.
144 */
145 if (CPU_FEATURE_USABLE (IBT))
146 {
147 if (info->enable_ibt_type == cet_always_on)
148 info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
149 else
150 info->enable_feature_1 &= ((m->l_x86_feature_1_and
151 & GNU_PROPERTY_X86_FEATURE_1_IBT)
152 | ~GNU_PROPERTY_X86_FEATURE_1_IBT);
153 }
154 else
155 info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
156
157 if (CPU_FEATURE_USABLE (SHSTK))
158 {
159 if (info->enable_shstk_type == cet_always_on)
160 info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
161 else
162 info->enable_feature_1 &= ((m->l_x86_feature_1_and
163 & GNU_PROPERTY_X86_FEATURE_1_SHSTK)
164 | ~GNU_PROPERTY_X86_FEATURE_1_SHSTK);
165 }
166 else
167 info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
168
169 if (info->enable_feature_1 != 0)
170 dl_check_legacy_object (m, info);
171
172 unsigned int disable_feature_1
173 = info->enable_feature_1 ^ info->feature_1_enabled;
174 if (disable_feature_1 != 0)
175 {
176 /* Clear the disabled bits. Sync dl_x86_feature_1 and
177 info->feature_1_enabled with info->enable_feature_1. */
178 info->feature_1_enabled = info->enable_feature_1;
179 GL(dl_x86_feature_1) = info->enable_feature_1;
180 }
181}
182#endif
183
184/* Check feature bits when dlopening the shared object M. */
185
186static void
187dl_cet_check_dlopen (struct link_map *m, struct dl_cet_info *info)
188{
189 /* Check if there are any legacy objects loaded. */
190 if (info->enable_feature_1 != 0)
191 {
192 dl_check_legacy_object (m, info);
193
194 /* Skip if there are no legacy shared objects loaded. */
195 if (info->feature_1_legacy == 0)
196 return;
197 }
198
199 unsigned int disable_feature_1 = 0;
200 unsigned int legacy_obj = 0;
201 const char *msg = NULL;
202
203 if ((info->feature_1_enabled
204 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0
205 && (info->feature_1_legacy
206 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0)
207 {
208 /* Don't disable IBT if not single threaded since IBT may be still
209 enabled in other threads. */
210 if (info->enable_ibt_type != cet_permissive
211 || !SINGLE_THREAD_P)
212 {
213 legacy_obj = info->feature_1_legacy_ibt;
214 msg = N_("rebuild shared object with IBT support enabled");
215 }
216 else
217 disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
218 }
219
220 /* Check the next feature only if there is no error. */
221 if (msg == NULL
222 && (info->feature_1_enabled
223 & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0
224 && (info->feature_1_legacy
225 & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0)
226 {
227 /* Don't disable SHSTK if not single threaded since SHSTK may be
228 still enabled in other threads. */
229 if (info->enable_shstk_type != cet_permissive
230 || !SINGLE_THREAD_P)
231 {
232 legacy_obj = info->feature_1_legacy_shstk;
233 msg = N_("rebuild shared object with SHSTK support enabled");
234 }
235 else
236 disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
237 }
238
239 /* If there is an error, long jump back to the caller. */
240 if (msg != NULL)
241 _dl_signal_error (errcode: 0, object: m->l_initfini[legacy_obj]->l_name, occasion: "dlopen",
242 errstring: msg);
243
244 if (disable_feature_1 != 0)
245 {
246 int res = dl_cet_disable_cet (cet_feature: disable_feature_1);
247 if (res)
248 {
249 if ((disable_feature_1
250 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0)
251 msg = N_("can't disable IBT");
252 else
253 msg = N_("can't disable SHSTK");
254 /* Long jump back to the caller on error. */
255 _dl_signal_error (errcode: -res, object: m->l_initfini[legacy_obj]->l_name,
256 occasion: "dlopen", errstring: msg);
257 }
258
259 /* Clear the disabled bits in dl_x86_feature_1. */
260 GL(dl_x86_feature_1) &= ~disable_feature_1;
261
262 THREAD_SETMEM (THREAD_SELF, header.feature_1,
263 GL(dl_x86_feature_1));
264 }
265}
266
267static void
268dl_cet_check (struct link_map *m, const char *program)
269{
270 struct dl_cet_info info;
271
272 /* CET is enabled only if RTLD_START_ENABLE_X86_FEATURES is defined. */
273#if defined SHARED && defined RTLD_START_ENABLE_X86_FEATURES
274 /* Set dl_x86_feature_1 to features enabled in the executable. */
275 if (program != NULL)
276 GL(dl_x86_feature_1) = (m->l_x86_feature_1_and
277 & (X86_FEATURE_1_IBT
278 | X86_FEATURE_1_SHSTK));
279#endif
280
281 /* Check how IBT and SHSTK should be enabled. */
282 info.enable_ibt_type = GL(dl_x86_feature_control).ibt;
283 info.enable_shstk_type = GL(dl_x86_feature_control).shstk;
284
285 info.feature_1_enabled = GL(dl_x86_feature_1);
286
287 /* No legacy object check if IBT and SHSTK are always on. */
288 if (info.enable_ibt_type == cet_always_on
289 && info.enable_shstk_type == cet_always_on)
290 return;
291
292 /* Check if IBT and SHSTK were enabled. */
293 if (info.feature_1_enabled == 0)
294 return;
295
296 info.program = program;
297
298 /* Check which features should be enabled. */
299 info.enable_feature_1 = 0;
300 if (info.enable_ibt_type != cet_always_off)
301 info.enable_feature_1 |= (info.feature_1_enabled
302 & GNU_PROPERTY_X86_FEATURE_1_IBT);
303 if (info.enable_shstk_type != cet_always_off)
304 info.enable_feature_1 |= (info.feature_1_enabled
305 & GNU_PROPERTY_X86_FEATURE_1_SHSTK);
306
307 /* Start with no legacy objects. */
308 info.feature_1_legacy = 0;
309 info.feature_1_legacy_ibt = 0;
310 info.feature_1_legacy_shstk = 0;
311
312#ifdef SHARED
313 if (program)
314 dl_cet_check_startup (m, &info);
315 else
316#endif
317 dl_cet_check_dlopen (m, info: &info);
318}
319
320void
321_dl_cet_open_check (struct link_map *l)
322{
323 dl_cet_check (m: l, NULL);
324}
325
326/* Set GL(dl_x86_feature_1) to the enabled features and clear the
327 active bits of the disabled features. */
328
329attribute_hidden void
330_dl_cet_setup_features (unsigned int cet_feature)
331{
332 /* NB: cet_feature == GL(dl_x86_feature_1) which is set to features
333 enabled from executable, not necessarily supported by kernel. */
334 if (cet_feature != 0)
335 {
336 cet_feature = dl_cet_get_cet_status ();
337 if (cet_feature != 0)
338 {
339 THREAD_SETMEM (THREAD_SELF, header.feature_1, cet_feature);
340
341 /* Lock CET if IBT or SHSTK is enabled in executable. Don't
342 lock CET if IBT or SHSTK is enabled permissively. */
343 if (GL(dl_x86_feature_control).ibt != cet_permissive
344 && (GL(dl_x86_feature_control).shstk != cet_permissive))
345 dl_cet_lock_cet (cet_feature);
346 }
347 /* Sync GL(dl_x86_feature_1) with kernel. */
348 GL(dl_x86_feature_1) = cet_feature;
349 }
350}
351
352#ifdef SHARED
353
354# ifndef LINKAGE
355# define LINKAGE
356# endif
357
358LINKAGE
359void
360_dl_cet_check (struct link_map *main_map, const char *program)
361{
362 dl_cet_check (main_map, program);
363}
364#endif /* SHARED */
365

source code of glibc/sysdeps/x86_64/dl-cet.c