1 /* The tunable framework.  See the README.tunables to know how to use the
2    tunable in a glibc module.
3 
4    Copyright (C) 2016-2022 Free Software Foundation, Inc.
5    This file is part of the GNU C Library.
6 
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11 
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, see
19    <https://www.gnu.org/licenses/>.  */
20 
21 /* Mark symbols hidden in static PIE for early self relocation to work.  */
22 #if BUILD_PIE_DEFAULT
23 # pragma GCC visibility push(hidden)
24 #endif
25 #include <startup.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <sysdep.h>
31 #include <fcntl.h>
32 #include <ldsodefs.h>
33 #include <array_length.h>
34 #include <dl-minimal-malloc.h>
35 
36 #define TUNABLES_INTERNAL 1
37 #include "dl-tunables.h"
38 
39 #include <not-errno.h>
40 
41 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
42 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
43 #endif
44 
45 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
46 static char *
tunables_strdup(const char * in)47 tunables_strdup (const char *in)
48 {
49   size_t i = 0;
50 
51   while (in[i++] != '\0');
52   char *out = __minimal_malloc (i + 1);
53 
54   /* For most of the tunables code, we ignore user errors.  However,
55      this is a system error - and running out of memory at program
56      startup should be reported, so we do.  */
57   if (out == NULL)
58     _dl_fatal_printf ("failed to allocate memory to process tunables\n");
59 
60   while (i-- > 0)
61     out[i] = in[i];
62 
63   return out;
64 }
65 #endif
66 
67 static char **
get_next_env(char ** envp,char ** name,size_t * namelen,char ** val,char *** prev_envp)68 get_next_env (char **envp, char **name, size_t *namelen, char **val,
69 	      char ***prev_envp)
70 {
71   while (envp != NULL && *envp != NULL)
72     {
73       char **prev = envp;
74       char *envline = *envp++;
75       int len = 0;
76 
77       while (envline[len] != '\0' && envline[len] != '=')
78 	len++;
79 
80       /* Just the name and no value, go to the next one.  */
81       if (envline[len] == '\0')
82 	continue;
83 
84       *name = envline;
85       *namelen = len;
86       *val = &envline[len + 1];
87       *prev_envp = prev;
88 
89       return envp;
90     }
91 
92   return NULL;
93 }
94 
95 static void
do_tunable_update_val(tunable_t * cur,const tunable_val_t * valp,const tunable_num_t * minp,const tunable_num_t * maxp)96 do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
97 		       const tunable_num_t *minp,
98 		       const tunable_num_t *maxp)
99 {
100   tunable_num_t val, min, max;
101 
102   if (cur->type.type_code == TUNABLE_TYPE_STRING)
103     {
104       cur->val.strval = valp->strval;
105       cur->initialized = true;
106       return;
107     }
108 
109   bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
110 
111   val = valp->numval;
112   min = minp != NULL ? *minp : cur->type.min;
113   max = maxp != NULL ? *maxp : cur->type.max;
114 
115   /* We allow only increasingly restrictive bounds.  */
116   if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
117     min = cur->type.min;
118 
119   if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
120     max = cur->type.max;
121 
122   /* Skip both bounds if they're inconsistent.  */
123   if (tunable_val_gt (min, max, unsigned_cmp))
124     {
125       min = cur->type.min;
126       max = cur->type.max;
127     }
128 
129   /* Bail out if the bounds are not valid.  */
130   if (tunable_val_lt (val, min, unsigned_cmp)
131       || tunable_val_lt (max, val, unsigned_cmp))
132     return;
133 
134   cur->val.numval = val;
135   cur->type.min = min;
136   cur->type.max = max;
137   cur->initialized = true;
138 }
139 
140 /* Validate range of the input value and initialize the tunable CUR if it looks
141    good.  */
142 static void
tunable_initialize(tunable_t * cur,const char * strval)143 tunable_initialize (tunable_t *cur, const char *strval)
144 {
145   tunable_val_t val;
146 
147   if (cur->type.type_code != TUNABLE_TYPE_STRING)
148     val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
149   else
150     val.strval = strval;
151   do_tunable_update_val (cur, &val, NULL, NULL);
152 }
153 
154 void
__tunable_set_val(tunable_id_t id,tunable_val_t * valp,tunable_num_t * minp,tunable_num_t * maxp)155 __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
156 		   tunable_num_t *maxp)
157 {
158   tunable_t *cur = &tunable_list[id];
159 
160   do_tunable_update_val (cur, valp, minp, maxp);
161 }
162 
163 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
164 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
165    be unsafe for AT_SECURE processes so that it can be used as the new
166    environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
167    environment variable string which we use to make NULL terminated values so
168    that we don't have to allocate memory again for it.  */
169 static void
parse_tunables(char * tunestr,char * valstring)170 parse_tunables (char *tunestr, char *valstring)
171 {
172   if (tunestr == NULL || *tunestr == '\0')
173     return;
174 
175   char *p = tunestr;
176   size_t off = 0;
177 
178   while (true)
179     {
180       char *name = p;
181       size_t len = 0;
182 
183       /* First, find where the name ends.  */
184       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
185 	len++;
186 
187       /* If we reach the end of the string before getting a valid name-value
188 	 pair, bail out.  */
189       if (p[len] == '\0')
190 	{
191 	  if (__libc_enable_secure)
192 	    tunestr[off] = '\0';
193 	  return;
194 	}
195 
196       /* We did not find a valid name-value pair before encountering the
197 	 colon.  */
198       if (p[len]== ':')
199 	{
200 	  p += len + 1;
201 	  continue;
202 	}
203 
204       p += len + 1;
205 
206       /* Take the value from the valstring since we need to NULL terminate it.  */
207       char *value = &valstring[p - tunestr];
208       len = 0;
209 
210       while (p[len] != ':' && p[len] != '\0')
211 	len++;
212 
213       /* Add the tunable if it exists.  */
214       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
215 	{
216 	  tunable_t *cur = &tunable_list[i];
217 
218 	  if (tunable_is_name (cur->name, name))
219 	    {
220 	      /* If we are in a secure context (AT_SECURE) then ignore the
221 		 tunable unless it is explicitly marked as secure.  Tunable
222 		 values take precedence over their envvar aliases.  We write
223 		 the tunables that are not SXID_ERASE back to TUNESTR, thus
224 		 dropping all SXID_ERASE tunables and any invalid or
225 		 unrecognized tunables.  */
226 	      if (__libc_enable_secure)
227 		{
228 		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
229 		    {
230 		      if (off > 0)
231 			tunestr[off++] = ':';
232 
233 		      const char *n = cur->name;
234 
235 		      while (*n != '\0')
236 			tunestr[off++] = *n++;
237 
238 		      tunestr[off++] = '=';
239 
240 		      for (size_t j = 0; j < len; j++)
241 			tunestr[off++] = value[j];
242 		    }
243 
244 		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
245 		    break;
246 		}
247 
248 	      value[len] = '\0';
249 	      tunable_initialize (cur, value);
250 	      break;
251 	    }
252 	}
253 
254       if (p[len] != '\0')
255 	p += len + 1;
256     }
257 }
258 #endif
259 
260 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
261    the system administrator has created the /etc/suid-debug file.  This is a
262    special case where we want to conditionally enable/disable a tunable even
263    for setuid binaries.  We use the special version of access() to avoid
264    setting ERRNO, which is a TLS variable since TLS has not yet been set
265    up.  */
266 static __always_inline void
maybe_enable_malloc_check(void)267 maybe_enable_malloc_check (void)
268 {
269   tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
270   if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
271     tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
272 }
273 
274 /* Initialize the tunables list from the environment.  For now we only use the
275    ENV_ALIAS to find values.  Later we will also use the tunable names to find
276    values.  */
277 void
__tunables_init(char ** envp)278 __tunables_init (char **envp)
279 {
280   char *envname = NULL;
281   char *envval = NULL;
282   size_t len = 0;
283   char **prev_envp = envp;
284 
285   maybe_enable_malloc_check ();
286 
287   while ((envp = get_next_env (envp, &envname, &len, &envval,
288 			       &prev_envp)) != NULL)
289     {
290 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
291       if (tunable_is_name (GLIBC_TUNABLES, envname))
292 	{
293 	  char *new_env = tunables_strdup (envname);
294 	  if (new_env != NULL)
295 	    parse_tunables (new_env + len + 1, envval);
296 	  /* Put in the updated envval.  */
297 	  *prev_envp = new_env;
298 	  continue;
299 	}
300 #endif
301 
302       for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
303 	{
304 	  tunable_t *cur = &tunable_list[i];
305 
306 	  /* Skip over tunables that have either been set already or should be
307 	     skipped.  */
308 	  if (cur->initialized || cur->env_alias[0] == '\0')
309 	    continue;
310 
311 	  const char *name = cur->env_alias;
312 
313 	  /* We have a match.  Initialize and move on to the next line.  */
314 	  if (tunable_is_name (name, envname))
315 	    {
316 	      /* For AT_SECURE binaries, we need to check the security settings of
317 		 the tunable and decide whether we read the value and also whether
318 		 we erase the value so that child processes don't inherit them in
319 		 the environment.  */
320 	      if (__libc_enable_secure)
321 		{
322 		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
323 		    {
324 		      /* Erase the environment variable.  */
325 		      char **ep = prev_envp;
326 
327 		      while (*ep != NULL)
328 			{
329 			  if (tunable_is_name (name, *ep))
330 			    {
331 			      char **dp = ep;
332 
333 			      do
334 				dp[0] = dp[1];
335 			      while (*dp++);
336 			    }
337 			  else
338 			    ++ep;
339 			}
340 		      /* Reset the iterator so that we read the environment again
341 			 from the point we erased.  */
342 		      envp = prev_envp;
343 		    }
344 
345 		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
346 		    continue;
347 		}
348 
349 	      tunable_initialize (cur, envval);
350 	      break;
351 	    }
352 	}
353     }
354 }
355 
356 void
__tunables_print(void)357 __tunables_print (void)
358 {
359   for (int i = 0; i < array_length (tunable_list); i++)
360     {
361       const tunable_t *cur = &tunable_list[i];
362       if (cur->type.type_code == TUNABLE_TYPE_STRING
363 	  && cur->val.strval == NULL)
364 	_dl_printf ("%s:\n", cur->name);
365       else
366 	{
367 	  _dl_printf ("%s: ", cur->name);
368 	  switch (cur->type.type_code)
369 	    {
370 	    case TUNABLE_TYPE_INT_32:
371 	      _dl_printf ("%d (min: %d, max: %d)\n",
372 			  (int) cur->val.numval,
373 			  (int) cur->type.min,
374 			  (int) cur->type.max);
375 	      break;
376 	    case TUNABLE_TYPE_UINT_64:
377 	      _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
378 			  (long int) cur->val.numval,
379 			  (long int) cur->type.min,
380 			  (long int) cur->type.max);
381 	      break;
382 	    case TUNABLE_TYPE_SIZE_T:
383 	      _dl_printf ("0x%Zx (min: 0x%Zx, max: 0x%Zx)\n",
384 			  (size_t) cur->val.numval,
385 			  (size_t) cur->type.min,
386 			  (size_t) cur->type.max);
387 	      break;
388 	    case TUNABLE_TYPE_STRING:
389 	      _dl_printf ("%s\n", cur->val.strval);
390 	      break;
391 	    default:
392 	      __builtin_unreachable ();
393 	    }
394 	}
395     }
396 }
397 
398 /* Set the tunable value.  This is called by the module that the tunable exists
399    in. */
400 void
__tunable_get_val(tunable_id_t id,void * valp,tunable_callback_t callback)401 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
402 {
403   tunable_t *cur = &tunable_list[id];
404 
405   switch (cur->type.type_code)
406     {
407     case TUNABLE_TYPE_UINT_64:
408 	{
409 	  *((uint64_t *) valp) = (uint64_t) cur->val.numval;
410 	  break;
411 	}
412     case TUNABLE_TYPE_INT_32:
413 	{
414 	  *((int32_t *) valp) = (int32_t) cur->val.numval;
415 	  break;
416 	}
417     case TUNABLE_TYPE_SIZE_T:
418 	{
419 	  *((size_t *) valp) = (size_t) cur->val.numval;
420 	  break;
421 	}
422     case TUNABLE_TYPE_STRING:
423 	{
424 	  *((const char **)valp) = cur->val.strval;
425 	  break;
426 	}
427     default:
428       __builtin_unreachable ();
429     }
430 
431   if (cur->initialized && callback != NULL)
432     callback (&cur->val);
433 }
434 
435 rtld_hidden_def (__tunable_get_val)
436