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