1 /* x86 CET initializers function.
2 Copyright (C) 2018-2022 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
24 /* GNU_PROPERTY_X86_FEATURE_1_IBT and GNU_PROPERTY_X86_FEATURE_1_SHSTK
25 are defined in <elf.h>, which are only available for C sources.
26 X86_FEATURE_1_IBT and X86_FEATURE_1_SHSTK are defined in <sysdep.h>
27 which are available for both C and asm sources. They must match. */
28 #if GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT
29 # error GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT
30 #endif
31 #if GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
32 # error GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK
33 #endif
34
35 /* Check if object M is compatible with CET. */
36
37 static void
dl_cet_check(struct link_map * m,const char * program)38 dl_cet_check (struct link_map *m, const char *program)
39 {
40 /* Check how IBT should be enabled. */
41 enum dl_x86_cet_control enable_ibt_type
42 = GL(dl_x86_feature_control).ibt;
43 /* Check how SHSTK should be enabled. */
44 enum dl_x86_cet_control enable_shstk_type
45 = GL(dl_x86_feature_control).shstk;
46
47 /* No legacy object check if both IBT and SHSTK are always on. */
48 if (enable_ibt_type == cet_always_on
49 && enable_shstk_type == cet_always_on)
50 {
51 THREAD_SETMEM (THREAD_SELF, header.feature_1, GL(dl_x86_feature_1));
52 return;
53 }
54
55 /* Check if IBT is enabled by kernel. */
56 bool ibt_enabled
57 = (GL(dl_x86_feature_1) & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0;
58 /* Check if SHSTK is enabled by kernel. */
59 bool shstk_enabled
60 = (GL(dl_x86_feature_1) & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0;
61
62 if (ibt_enabled || shstk_enabled)
63 {
64 struct link_map *l = NULL;
65 unsigned int ibt_legacy = 0, shstk_legacy = 0;
66 bool found_ibt_legacy = false, found_shstk_legacy = false;
67
68 /* Check if IBT and SHSTK are enabled in object. */
69 bool enable_ibt = (ibt_enabled
70 && enable_ibt_type != cet_always_off);
71 bool enable_shstk = (shstk_enabled
72 && enable_shstk_type != cet_always_off);
73 if (program)
74 {
75 /* Enable IBT and SHSTK only if they are enabled in executable.
76 NB: IBT and SHSTK may be disabled by environment variable:
77
78 GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK
79 */
80 enable_ibt &= (CPU_FEATURE_USABLE (IBT)
81 && (enable_ibt_type == cet_always_on
82 || (m->l_x86_feature_1_and
83 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0));
84 enable_shstk &= (CPU_FEATURE_USABLE (SHSTK)
85 && (enable_shstk_type == cet_always_on
86 || (m->l_x86_feature_1_and
87 & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0));
88 }
89
90 /* ld.so is CET-enabled by kernel. But shared objects may not
91 support IBT nor SHSTK. */
92 if (enable_ibt || enable_shstk)
93 {
94 unsigned int i;
95
96 i = m->l_searchlist.r_nlist;
97 while (i-- > 0)
98 {
99 /* Check each shared object to see if IBT and SHSTK are
100 enabled. */
101 l = m->l_initfini[i];
102
103 if (l->l_init_called)
104 continue;
105
106 #ifdef SHARED
107 /* Skip CET check for ld.so since ld.so is CET-enabled.
108 CET will be disabled later if CET isn't enabled in
109 executable. */
110 if (l == &GL(dl_rtld_map)
111 || l->l_real == &GL(dl_rtld_map)
112 || (program && l == m))
113 continue;
114 #endif
115
116 /* IBT is enabled only if it is enabled in executable as
117 well as all shared objects. */
118 enable_ibt &= (enable_ibt_type == cet_always_on
119 || (l->l_x86_feature_1_and
120 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0);
121 if (!found_ibt_legacy && enable_ibt != ibt_enabled)
122 {
123 found_ibt_legacy = true;
124 ibt_legacy = i;
125 }
126
127 /* SHSTK is enabled only if it is enabled in executable as
128 well as all shared objects. */
129 enable_shstk &= (enable_shstk_type == cet_always_on
130 || (l->l_x86_feature_1_and
131 & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0);
132 if (enable_shstk != shstk_enabled)
133 {
134 found_shstk_legacy = true;
135 shstk_legacy = i;
136 }
137 }
138 }
139
140 bool cet_feature_changed = false;
141
142 if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled)
143 {
144 if (!program)
145 {
146 if (enable_ibt_type != cet_permissive)
147 {
148 /* When IBT is enabled, we cannot dlopen a shared
149 object without IBT. */
150 if (found_ibt_legacy)
151 _dl_signal_error (0,
152 m->l_initfini[ibt_legacy]->l_name,
153 "dlopen",
154 N_("rebuild shared object with IBT support enabled"));
155 }
156
157 if (enable_shstk_type != cet_permissive)
158 {
159 /* When SHSTK is enabled, we cannot dlopen a shared
160 object without SHSTK. */
161 if (found_shstk_legacy)
162 _dl_signal_error (0,
163 m->l_initfini[shstk_legacy]->l_name,
164 "dlopen",
165 N_("rebuild shared object with SHSTK support enabled"));
166 }
167
168 if (enable_ibt_type != cet_permissive
169 && enable_shstk_type != cet_permissive)
170 return;
171 }
172
173 /* Disable IBT and/or SHSTK if they are enabled by kernel, but
174 disabled in executable or shared objects. */
175 unsigned int cet_feature = 0;
176
177 if (!enable_ibt)
178 cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT;
179 if (!enable_shstk)
180 cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
181
182 int res = dl_cet_disable_cet (cet_feature);
183 if (res != 0)
184 {
185 if (program)
186 _dl_fatal_printf ("%s: can't disable CET\n", program);
187 else
188 {
189 if (found_ibt_legacy)
190 l = m->l_initfini[ibt_legacy];
191 else
192 l = m->l_initfini[shstk_legacy];
193 _dl_signal_error (-res, l->l_name, "dlopen",
194 N_("can't disable CET"));
195 }
196 }
197
198 /* Clear the disabled bits in dl_x86_feature_1. */
199 GL(dl_x86_feature_1) &= ~cet_feature;
200
201 cet_feature_changed = true;
202 }
203
204 #ifdef SHARED
205 if (program && (ibt_enabled || shstk_enabled))
206 {
207 if ((!ibt_enabled
208 || enable_ibt_type != cet_permissive)
209 && (!shstk_enabled
210 || enable_shstk_type != cet_permissive))
211 {
212 /* Lock CET if IBT or SHSTK is enabled in executable unless
213 IBT or SHSTK is enabled permissively. */
214 int res = dl_cet_lock_cet ();
215 if (res != 0)
216 _dl_fatal_printf ("%s: can't lock CET\n", program);
217 }
218
219 /* Set feature_1 if IBT or SHSTK is enabled in executable. */
220 cet_feature_changed = true;
221 }
222 #endif
223
224 if (cet_feature_changed)
225 {
226 unsigned int feature_1 = 0;
227 if (enable_ibt)
228 feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT;
229 if (enable_shstk)
230 feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
231 struct pthread *self = THREAD_SELF;
232 THREAD_SETMEM (self, header.feature_1, feature_1);
233 }
234 }
235 }
236
237 void
_dl_cet_open_check(struct link_map * l)238 _dl_cet_open_check (struct link_map *l)
239 {
240 dl_cet_check (l, NULL);
241 }
242
243 #ifdef SHARED
244
245 # ifndef LINKAGE
246 # define LINKAGE
247 # endif
248
249 LINKAGE
250 void
_dl_cet_check(struct link_map * main_map,const char * program)251 _dl_cet_check (struct link_map *main_map, const char *program)
252 {
253 dl_cet_check (main_map, program);
254 }
255 #endif /* SHARED */
256