]>
Commit | Line | Data |
---|---|---|
f753fa7d | 1 | /* x86 CET initializers function. |
d614a753 | 2 | Copyright (C) 2018-2020 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 | |
5a82c748 | 16 | <https://www.gnu.org/licenses/>. */ |
f753fa7d L |
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 | ||
36 | static int | |
37 | dl_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 | ||
95 | static void | |
96 | dl_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) | |
108 | return; | |
109 | ||
110 | /* Check if IBT is enabled by kernel. */ | |
111 | bool ibt_enabled | |
112 | = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0; | |
113 | /* Check if SHSTK is enabled by kernel. */ | |
114 | bool shstk_enabled | |
115 | = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0; | |
116 | ||
117 | if (ibt_enabled || shstk_enabled) | |
118 | { | |
119 | struct link_map *l = NULL; | |
120 | ||
121 | /* Check if IBT and SHSTK are enabled in object. */ | |
122 | bool enable_ibt = (ibt_enabled | |
123 | && enable_ibt_type != CET_ALWAYS_OFF); | |
124 | bool enable_shstk = (shstk_enabled | |
125 | && enable_shstk_type != CET_ALWAYS_OFF); | |
126 | if (program) | |
127 | { | |
128 | /* Enable IBT and SHSTK only if they are enabled in executable. | |
129 | NB: IBT and SHSTK may be disabled by environment variable: | |
130 | ||
dce452dc | 131 | GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK |
f753fa7d L |
132 | */ |
133 | enable_ibt &= (HAS_CPU_FEATURE (IBT) | |
134 | && (enable_ibt_type == CET_ALWAYS_ON | |
135 | || (m->l_cet & lc_ibt) != 0)); | |
136 | enable_shstk &= (HAS_CPU_FEATURE (SHSTK) | |
137 | && (enable_shstk_type == CET_ALWAYS_ON | |
138 | || (m->l_cet & lc_shstk) != 0)); | |
139 | } | |
140 | ||
141 | /* ld.so is CET-enabled by kernel. But shared objects may not | |
142 | support IBT nor SHSTK. */ | |
143 | if (enable_ibt || enable_shstk) | |
144 | { | |
145 | int res; | |
146 | unsigned int i; | |
147 | unsigned int first_legacy, last_legacy; | |
148 | bool need_legacy_bitmap = false; | |
149 | ||
150 | i = m->l_searchlist.r_nlist; | |
151 | while (i-- > 0) | |
152 | { | |
153 | /* Check each shared object to see if IBT and SHSTK are | |
154 | enabled. */ | |
155 | l = m->l_initfini[i]; | |
156 | ||
157 | if (l->l_init_called) | |
158 | continue; | |
159 | ||
160 | #ifdef SHARED | |
161 | /* Skip CET check for ld.so since ld.so is CET-enabled. | |
162 | CET will be disabled later if CET isn't enabled in | |
163 | executable. */ | |
164 | if (l == &GL(dl_rtld_map) | |
165 | || l->l_real == &GL(dl_rtld_map) | |
166 | || (program && l == m)) | |
167 | continue; | |
168 | #endif | |
169 | ||
170 | if (enable_ibt | |
171 | && enable_ibt_type != CET_ALWAYS_ON | |
172 | && !(l->l_cet & lc_ibt)) | |
173 | { | |
174 | /* Remember the first and last legacy objects. */ | |
175 | if (!need_legacy_bitmap) | |
176 | last_legacy = i; | |
177 | first_legacy = i; | |
178 | need_legacy_bitmap = true; | |
179 | } | |
180 | ||
181 | /* SHSTK is enabled only if it is enabled in executable as | |
182 | well as all shared objects. */ | |
183 | enable_shstk &= (enable_shstk_type == CET_ALWAYS_ON | |
184 | || (l->l_cet & lc_shstk) != 0); | |
185 | } | |
186 | ||
187 | if (need_legacy_bitmap) | |
188 | { | |
189 | if (GL(dl_x86_legacy_bitmap)[0]) | |
190 | { | |
191 | /* Change legacy bitmap to writable. */ | |
192 | if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0], | |
193 | GL(dl_x86_legacy_bitmap)[1], | |
194 | PROT_READ | PROT_WRITE) < 0) | |
195 | { | |
196 | mprotect_failure: | |
197 | if (program) | |
198 | _dl_fatal_printf ("%s: mprotect legacy bitmap failed\n", | |
199 | l->l_name); | |
200 | else | |
201 | _dl_signal_error (EINVAL, l->l_name, "dlopen", | |
202 | N_("mprotect legacy bitmap failed")); | |
203 | } | |
204 | } | |
205 | else | |
206 | { | |
207 | /* Allocate legacy bitmap. */ | |
208 | int res = dl_cet_allocate_legacy_bitmap | |
209 | (GL(dl_x86_legacy_bitmap)); | |
210 | if (res != 0) | |
211 | { | |
212 | if (program) | |
213 | _dl_fatal_printf ("%s: legacy bitmap isn't available\n", | |
214 | l->l_name); | |
215 | else | |
216 | _dl_signal_error (EINVAL, l->l_name, "dlopen", | |
217 | N_("legacy bitmap isn't available")); | |
218 | } | |
219 | } | |
220 | ||
221 | /* Put legacy shared objects in legacy bitmap. */ | |
222 | for (i = first_legacy; i <= last_legacy; i++) | |
223 | { | |
224 | l = m->l_initfini[i]; | |
225 | ||
226 | if (l->l_init_called || (l->l_cet & lc_ibt)) | |
227 | continue; | |
228 | ||
229 | #ifdef SHARED | |
230 | if (l == &GL(dl_rtld_map) | |
231 | || l->l_real == &GL(dl_rtld_map) | |
232 | || (program && l == m)) | |
233 | continue; | |
234 | #endif | |
235 | ||
236 | /* If IBT is enabled in executable and IBT isn't enabled | |
237 | in this shard object, mark PT_LOAD segments with PF_X | |
238 | in legacy code page bitmap. */ | |
239 | res = dl_cet_mark_legacy_region (l); | |
240 | if (res != 0) | |
241 | { | |
242 | if (program) | |
243 | _dl_fatal_printf ("%s: failed to mark legacy code region\n", | |
244 | l->l_name); | |
245 | else | |
246 | _dl_signal_error (-res, l->l_name, "dlopen", | |
247 | N_("failed to mark legacy code region")); | |
248 | } | |
249 | } | |
250 | ||
251 | /* Change legacy bitmap to read-only. */ | |
252 | if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0], | |
253 | GL(dl_x86_legacy_bitmap)[1], PROT_READ) < 0) | |
254 | goto mprotect_failure; | |
255 | } | |
256 | } | |
257 | ||
258 | bool cet_feature_changed = false; | |
259 | ||
260 | if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled) | |
261 | { | |
262 | if (!program | |
263 | && enable_shstk_type != CET_PERMISSIVE) | |
264 | { | |
265 | /* When SHSTK is enabled, we can't dlopening a shared | |
266 | object without SHSTK. */ | |
267 | if (enable_shstk != shstk_enabled) | |
268 | _dl_signal_error (EINVAL, l->l_name, "dlopen", | |
269 | N_("shadow stack isn't enabled")); | |
270 | return; | |
271 | } | |
272 | ||
273 | /* Disable IBT and/or SHSTK if they are enabled by kernel, but | |
274 | disabled in executable or shared objects. */ | |
275 | unsigned int cet_feature = 0; | |
276 | ||
277 | /* Disable IBT only during program startup. */ | |
278 | if (program && !enable_ibt) | |
279 | cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT; | |
280 | if (!enable_shstk) | |
281 | cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; | |
282 | ||
283 | int res = dl_cet_disable_cet (cet_feature); | |
284 | if (res != 0) | |
285 | { | |
286 | if (program) | |
287 | _dl_fatal_printf ("%s: can't disable CET\n", program); | |
288 | else | |
289 | _dl_signal_error (-res, l->l_name, "dlopen", | |
290 | N_("can't disable CET")); | |
291 | } | |
292 | ||
293 | /* Clear the disabled bits in dl_x86_feature_1. */ | |
294 | GL(dl_x86_feature_1)[0] &= ~cet_feature; | |
295 | ||
296 | cet_feature_changed = true; | |
297 | } | |
298 | ||
299 | #ifdef SHARED | |
300 | if (program | |
301 | && (!shstk_enabled | |
302 | || enable_shstk_type != CET_PERMISSIVE) | |
303 | && (ibt_enabled || shstk_enabled)) | |
304 | { | |
305 | /* Lock CET if IBT or SHSTK is enabled in executable. Don't | |
306 | lock CET if SHSTK is enabled permissively. */ | |
307 | int res = dl_cet_lock_cet (); | |
308 | if (res != 0) | |
309 | _dl_fatal_printf ("%s: can't lock CET\n", program); | |
310 | ||
311 | cet_feature_changed = true; | |
312 | } | |
313 | #endif | |
314 | ||
315 | if (cet_feature_changed) | |
316 | { | |
317 | unsigned int feature_1 = 0; | |
318 | if (enable_ibt) | |
319 | feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; | |
320 | if (enable_shstk) | |
321 | feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; | |
322 | struct pthread *self = THREAD_SELF; | |
323 | THREAD_SETMEM (self, header.feature_1, feature_1); | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | void | |
329 | _dl_cet_open_check (struct link_map *l) | |
330 | { | |
331 | dl_cet_check (l, NULL); | |
332 | } | |
333 | ||
334 | #ifdef SHARED | |
335 | ||
336 | # ifndef LINKAGE | |
337 | # define LINKAGE | |
338 | # endif | |
339 | ||
340 | LINKAGE | |
341 | void | |
342 | _dl_cet_check (struct link_map *main_map, const char *program) | |
343 | { | |
344 | dl_cet_check (main_map, program); | |
345 | } | |
346 | #endif /* SHARED */ |