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