]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/x86/dl-cet.c
x86: Set header.feature_1 in TCB for always-on CET [BZ #27177]
[thirdparty/glibc.git] / sysdeps / x86 / dl-cet.c
CommitLineData
f753fa7d 1/* x86 CET initializers function.
04277e02 2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
f753fa7d
L
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 <http://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 <cet-tunables.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
36static int
37dl_cet_mark_legacy_region (struct link_map *l)
38{
39 /* Mark PT_LOAD segments with PF_X in legacy code page bitmap. */
40 size_t i, phnum = l->l_phnum;
41 const ElfW(Phdr) *phdr = l->l_phdr;
42#ifdef __x86_64__
43 typedef unsigned long long word_t;
44#else
45 typedef unsigned long word_t;
46#endif
47 unsigned int bits_to_set;
48 word_t mask_to_set;
49#define BITS_PER_WORD (sizeof (word_t) * 8)
50#define BITMAP_FIRST_WORD_MASK(start) \
51 (~((word_t) 0) << ((start) & (BITS_PER_WORD - 1)))
52#define BITMAP_LAST_WORD_MASK(nbits) \
53 (~((word_t) 0) >> (-(nbits) & (BITS_PER_WORD - 1)))
54
55 word_t *bitmap = (word_t *) GL(dl_x86_legacy_bitmap)[0];
56 word_t bitmap_size = GL(dl_x86_legacy_bitmap)[1];
57 word_t *p;
58 size_t page_size = GLRO(dl_pagesize);
59
60 for (i = 0; i < phnum; i++)
61 if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X))
62 {
63 /* One bit in legacy bitmap represents a page. */
64 ElfW(Addr) start = (phdr[i].p_vaddr + l->l_addr) / page_size;
65 ElfW(Addr) len = (phdr[i].p_memsz + page_size - 1) / page_size;
66 ElfW(Addr) end = start + len;
67
68 if ((end / 8) > bitmap_size)
69 return -EINVAL;
70
71 p = bitmap + (start / BITS_PER_WORD);
72 bits_to_set = BITS_PER_WORD - (start % BITS_PER_WORD);
73 mask_to_set = BITMAP_FIRST_WORD_MASK (start);
74
75 while (len >= bits_to_set)
76 {
77 *p |= mask_to_set;
78 len -= bits_to_set;
79 bits_to_set = BITS_PER_WORD;
80 mask_to_set = ~((word_t) 0);
81 p++;
82 }
83 if (len)
84 {
85 mask_to_set &= BITMAP_LAST_WORD_MASK (end);
86 *p |= mask_to_set;
87 }
88 }
89
90 return 0;
91}
92
93/* Check if object M is compatible with CET. */
94
95static void
96dl_cet_check (struct link_map *m, const char *program)
97{
98 /* Check how IBT should be enabled. */
99 unsigned int enable_ibt_type
100 = GL(dl_x86_feature_1)[1] & ((1 << CET_MAX) - 1);
101 /* Check how SHSTK should be enabled. */
102 unsigned int enable_shstk_type
103 = ((GL(dl_x86_feature_1)[1] >> CET_MAX) & ((1 << CET_MAX) - 1));
104
105 /* No legacy object check if both IBT and SHSTK are always on. */
106 if (enable_ibt_type == CET_ALWAYS_ON
107 && enable_shstk_type == CET_ALWAYS_ON)
420ade1f
L
108 {
109 THREAD_SETMEM (THREAD_SELF, header.feature_1,
110 GL(dl_x86_feature_1)[0]);
111 return;
112 }
f753fa7d
L
113
114 /* Check if IBT is enabled by kernel. */
115 bool ibt_enabled
116 = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0;
117 /* Check if SHSTK is enabled by kernel. */
118 bool shstk_enabled
119 = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0;
120
121 if (ibt_enabled || shstk_enabled)
122 {
123 struct link_map *l = NULL;
124
125 /* Check if IBT and SHSTK are enabled in object. */
126 bool enable_ibt = (ibt_enabled
127 && enable_ibt_type != CET_ALWAYS_OFF);
128 bool enable_shstk = (shstk_enabled
129 && enable_shstk_type != CET_ALWAYS_OFF);
130 if (program)
131 {
132 /* Enable IBT and SHSTK only if they are enabled in executable.
133 NB: IBT and SHSTK may be disabled by environment variable:
134
dce452dc 135 GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK
f753fa7d
L
136 */
137 enable_ibt &= (HAS_CPU_FEATURE (IBT)
138 && (enable_ibt_type == CET_ALWAYS_ON
139 || (m->l_cet & lc_ibt) != 0));
140 enable_shstk &= (HAS_CPU_FEATURE (SHSTK)
141 && (enable_shstk_type == CET_ALWAYS_ON
142 || (m->l_cet & lc_shstk) != 0));
143 }
144
145 /* ld.so is CET-enabled by kernel. But shared objects may not
146 support IBT nor SHSTK. */
147 if (enable_ibt || enable_shstk)
148 {
149 int res;
150 unsigned int i;
151 unsigned int first_legacy, last_legacy;
152 bool need_legacy_bitmap = false;
153
154 i = m->l_searchlist.r_nlist;
155 while (i-- > 0)
156 {
157 /* Check each shared object to see if IBT and SHSTK are
158 enabled. */
159 l = m->l_initfini[i];
160
161 if (l->l_init_called)
162 continue;
163
164#ifdef SHARED
165 /* Skip CET check for ld.so since ld.so is CET-enabled.
166 CET will be disabled later if CET isn't enabled in
167 executable. */
168 if (l == &GL(dl_rtld_map)
169 || l->l_real == &GL(dl_rtld_map)
170 || (program && l == m))
171 continue;
172#endif
173
174 if (enable_ibt
175 && enable_ibt_type != CET_ALWAYS_ON
176 && !(l->l_cet & lc_ibt))
177 {
178 /* Remember the first and last legacy objects. */
179 if (!need_legacy_bitmap)
180 last_legacy = i;
181 first_legacy = i;
182 need_legacy_bitmap = true;
183 }
184
185 /* SHSTK is enabled only if it is enabled in executable as
186 well as all shared objects. */
187 enable_shstk &= (enable_shstk_type == CET_ALWAYS_ON
188 || (l->l_cet & lc_shstk) != 0);
189 }
190
191 if (need_legacy_bitmap)
192 {
193 if (GL(dl_x86_legacy_bitmap)[0])
194 {
195 /* Change legacy bitmap to writable. */
196 if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0],
197 GL(dl_x86_legacy_bitmap)[1],
198 PROT_READ | PROT_WRITE) < 0)
199 {
200mprotect_failure:
201 if (program)
202 _dl_fatal_printf ("%s: mprotect legacy bitmap failed\n",
203 l->l_name);
204 else
205 _dl_signal_error (EINVAL, l->l_name, "dlopen",
206 N_("mprotect legacy bitmap failed"));
207 }
208 }
209 else
210 {
211 /* Allocate legacy bitmap. */
212 int res = dl_cet_allocate_legacy_bitmap
213 (GL(dl_x86_legacy_bitmap));
214 if (res != 0)
215 {
216 if (program)
217 _dl_fatal_printf ("%s: legacy bitmap isn't available\n",
218 l->l_name);
219 else
220 _dl_signal_error (EINVAL, l->l_name, "dlopen",
221 N_("legacy bitmap isn't available"));
222 }
223 }
224
225 /* Put legacy shared objects in legacy bitmap. */
226 for (i = first_legacy; i <= last_legacy; i++)
227 {
228 l = m->l_initfini[i];
229
230 if (l->l_init_called || (l->l_cet & lc_ibt))
231 continue;
232
233#ifdef SHARED
234 if (l == &GL(dl_rtld_map)
235 || l->l_real == &GL(dl_rtld_map)
236 || (program && l == m))
237 continue;
238#endif
239
240 /* If IBT is enabled in executable and IBT isn't enabled
241 in this shard object, mark PT_LOAD segments with PF_X
242 in legacy code page bitmap. */
243 res = dl_cet_mark_legacy_region (l);
244 if (res != 0)
245 {
246 if (program)
247 _dl_fatal_printf ("%s: failed to mark legacy code region\n",
248 l->l_name);
249 else
250 _dl_signal_error (-res, l->l_name, "dlopen",
251 N_("failed to mark legacy code region"));
252 }
253 }
254
255 /* Change legacy bitmap to read-only. */
256 if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0],
257 GL(dl_x86_legacy_bitmap)[1], PROT_READ) < 0)
258 goto mprotect_failure;
259 }
260 }
261
262 bool cet_feature_changed = false;
263
264 if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled)
265 {
266 if (!program
267 && enable_shstk_type != CET_PERMISSIVE)
268 {
269 /* When SHSTK is enabled, we can't dlopening a shared
270 object without SHSTK. */
271 if (enable_shstk != shstk_enabled)
272 _dl_signal_error (EINVAL, l->l_name, "dlopen",
273 N_("shadow stack isn't enabled"));
274 return;
275 }
276
277 /* Disable IBT and/or SHSTK if they are enabled by kernel, but
278 disabled in executable or shared objects. */
279 unsigned int cet_feature = 0;
280
281 /* Disable IBT only during program startup. */
282 if (program && !enable_ibt)
283 cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT;
284 if (!enable_shstk)
285 cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
286
287 int res = dl_cet_disable_cet (cet_feature);
288 if (res != 0)
289 {
290 if (program)
291 _dl_fatal_printf ("%s: can't disable CET\n", program);
292 else
293 _dl_signal_error (-res, l->l_name, "dlopen",
294 N_("can't disable CET"));
295 }
296
297 /* Clear the disabled bits in dl_x86_feature_1. */
298 GL(dl_x86_feature_1)[0] &= ~cet_feature;
299
300 cet_feature_changed = true;
301 }
302
303#ifdef SHARED
304 if (program
305 && (!shstk_enabled
306 || enable_shstk_type != CET_PERMISSIVE)
307 && (ibt_enabled || shstk_enabled))
308 {
309 /* Lock CET if IBT or SHSTK is enabled in executable. Don't
310 lock CET if SHSTK is enabled permissively. */
311 int res = dl_cet_lock_cet ();
312 if (res != 0)
313 _dl_fatal_printf ("%s: can't lock CET\n", program);
314
315 cet_feature_changed = true;
316 }
317#endif
318
319 if (cet_feature_changed)
320 {
321 unsigned int feature_1 = 0;
322 if (enable_ibt)
323 feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
324 if (enable_shstk)
325 feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
326 struct pthread *self = THREAD_SELF;
327 THREAD_SETMEM (self, header.feature_1, feature_1);
328 }
329 }
330}
331
332void
333_dl_cet_open_check (struct link_map *l)
334{
335 dl_cet_check (l, NULL);
336}
337
338#ifdef SHARED
339
340# ifndef LINKAGE
341# define LINKAGE
342# endif
343
344LINKAGE
345void
346_dl_cet_check (struct link_map *main_map, const char *program)
347{
348 dl_cet_check (main_map, program);
349}
350#endif /* SHARED */