]>
Commit | Line | Data |
---|---|---|
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 | ||
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) | |
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 | { | |
200 | mprotect_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 | ||
332 | void | |
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 | ||
344 | LINKAGE | |
345 | void | |
346 | _dl_cet_check (struct link_map *main_map, const char *program) | |
347 | { | |
348 | dl_cet_check (main_map, program); | |
349 | } | |
350 | #endif /* SHARED */ |