Add new templates for IEEE wrappers
[glibc.git] / elf / dl-tunables.c
blobe42aa670033131f75a682aedf492c017f2bcb673
1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2017 Free Software Foundation, Inc.
5 This file is part of the GNU C Library.
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.
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.
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 <http://www.gnu.org/licenses/>. */
21 #include <stdint.h>
22 #include <stdbool.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <libc-internal.h>
26 #include <sysdep.h>
27 #include <fcntl.h>
28 #include <ldsodefs.h>
30 #define TUNABLES_INTERNAL 1
31 #include "dl-tunables.h"
33 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
34 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
35 #endif
37 /* Compare environment or tunable names, bounded by the name hardcoded in
38 glibc. */
39 static bool
40 is_name (const char *orig, const char *envname)
42 for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
43 if (*orig != *envname)
44 break;
46 /* The ENVNAME is immediately followed by a value. */
47 if (*orig == '\0' && *envname == '=')
48 return true;
49 else
50 return false;
53 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
54 static char *
55 tunables_strdup (const char *in)
57 size_t i = 0;
59 while (in[i++] != '\0');
60 char *out = __sbrk (i);
62 /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
63 set the thread-local errno since the TCB has not yet been set up. This
64 needs to be fixed with an __sbrk implementation that does not set
65 errno. */
66 if (out == (void *)-1)
67 return NULL;
69 i--;
71 while (i-- > 0)
72 out[i] = in[i];
74 return out;
76 #endif
78 static char **
79 get_next_env (char **envp, char **name, size_t *namelen, char **val,
80 char ***prev_envp)
82 while (envp != NULL && *envp != NULL)
84 char **prev = envp;
85 char *envline = *envp++;
86 int len = 0;
88 while (envline[len] != '\0' && envline[len] != '=')
89 len++;
91 /* Just the name and no value, go to the next one. */
92 if (envline[len] == '\0')
93 continue;
95 *name = envline;
96 *namelen = len;
97 *val = &envline[len + 1];
98 *prev_envp = prev;
100 return envp;
103 return NULL;
106 /* A stripped down strtoul-like implementation for very early use. It does not
107 set errno if the result is outside bounds because it gets called before
108 errno may have been set up. */
109 static unsigned long int
110 tunables_strtoul (const char *nptr)
112 unsigned long int result = 0;
113 long int sign = 1;
114 unsigned max_digit;
116 while (*nptr == ' ' || *nptr == '\t')
117 ++nptr;
119 if (*nptr == '-')
121 sign = -1;
122 ++nptr;
124 else if (*nptr == '+')
125 ++nptr;
127 if (*nptr < '0' || *nptr > '9')
128 return 0UL;
130 int base = 10;
131 max_digit = 9;
132 if (*nptr == '0')
134 if (nptr[1] == 'x' || nptr[1] == 'X')
136 base = 16;
137 nptr += 2;
139 else
141 base = 8;
142 max_digit = 7;
146 while (1)
148 unsigned long int digval;
149 if (*nptr >= '0' && *nptr <= '0' + max_digit)
150 digval = *nptr - '0';
151 else if (base == 16)
153 if (*nptr >= 'a' && *nptr <= 'f')
154 digval = *nptr - 'a' + 10;
155 else if (*nptr >= 'A' && *nptr <= 'F')
156 digval = *nptr - 'A' + 10;
157 else
158 break;
160 else
161 break;
163 if (result > ULONG_MAX / base
164 || (result == ULONG_MAX / base && digval > ULONG_MAX % base))
165 return ULONG_MAX;
166 result *= base;
167 result += digval;
168 ++nptr;
171 return result * sign;
174 /* Initialize the internal type if the value validates either using the
175 explicit constraints of the tunable or with the implicit constraints of its
176 type. */
177 static void
178 tunable_set_val_if_valid_range_signed (tunable_t *cur, const char *strval,
179 int64_t default_min, int64_t default_max)
181 int64_t val = (int64_t) tunables_strtoul (strval);
183 int64_t min = cur->type.min;
184 int64_t max = cur->type.max;
186 if (min == max)
188 min = default_min;
189 max = default_max;
192 if (val >= min && val <= max)
194 cur->val.numval = val;
195 cur->strval = strval;
199 static void
200 tunable_set_val_if_valid_range_unsigned (tunable_t *cur, const char *strval,
201 uint64_t default_min, uint64_t default_max)
203 uint64_t val = (uint64_t) tunables_strtoul (strval);
205 uint64_t min = cur->type.min;
206 uint64_t max = cur->type.max;
208 if (min == max)
210 min = default_min;
211 max = default_max;
214 if (val >= min && val <= max)
216 cur->val.numval = val;
217 cur->strval = strval;
221 /* Validate range of the input value and initialize the tunable CUR if it looks
222 good. */
223 static void
224 tunable_initialize (tunable_t *cur, const char *strval)
226 switch (cur->type.type_code)
228 case TUNABLE_TYPE_INT_32:
230 tunable_set_val_if_valid_range_signed (cur, strval, INT32_MIN, INT32_MAX);
231 break;
233 case TUNABLE_TYPE_SIZE_T:
235 tunable_set_val_if_valid_range_unsigned (cur, strval, 0, SIZE_MAX);
236 break;
238 case TUNABLE_TYPE_STRING:
240 cur->val.strval = cur->strval = strval;
241 break;
243 default:
244 __builtin_unreachable ();
248 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
249 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
250 be unsafe for AT_SECURE processes so that it can be used as the new
251 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
252 environment variable string which we use to make NULL terminated values so
253 that we don't have to allocate memory again for it. */
254 static void
255 parse_tunables (char *tunestr, char *valstring)
257 if (tunestr == NULL || *tunestr == '\0')
258 return;
260 char *p = tunestr;
262 while (true)
264 char *name = p;
265 size_t len = 0;
267 /* First, find where the name ends. */
268 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
269 len++;
271 /* If we reach the end of the string before getting a valid name-value
272 pair, bail out. */
273 if (p[len] == '\0')
274 return;
276 /* We did not find a valid name-value pair before encountering the
277 colon. */
278 if (p[len]== ':')
280 p += len + 1;
281 continue;
284 p += len + 1;
286 /* Take the value from the valstring since we need to NULL terminate it. */
287 char *value = &valstring[p - tunestr];
288 len = 0;
290 while (p[len] != ':' && p[len] != '\0')
291 len++;
293 /* Add the tunable if it exists. */
294 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
296 tunable_t *cur = &tunable_list[i];
298 if (is_name (cur->name, name))
300 /* If we are in a secure context (AT_SECURE) then ignore the tunable
301 unless it is explicitly marked as secure. Tunable values take
302 precendence over their envvar aliases. */
303 if (__libc_enable_secure)
305 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
307 if (p[len] == '\0')
309 /* Last tunable in the valstring. Null-terminate and
310 return. */
311 *name = '\0';
312 return;
314 else
316 /* Remove the current tunable from the string. We do
317 this by overwriting the string starting from NAME
318 (which is where the current tunable begins) with
319 the remainder of the string. We then have P point
320 to NAME so that we continue in the correct
321 position in the valstring. */
322 char *q = &p[len + 1];
323 p = name;
324 while (*q != '\0')
325 *name++ = *q++;
326 name[0] = '\0';
327 len = 0;
331 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
332 break;
335 value[len] = '\0';
336 tunable_initialize (cur, value);
337 break;
341 if (p[len] == '\0')
342 return;
343 else
344 p += len + 1;
347 #endif
349 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
350 the system administrator has created the /etc/suid-debug file. This is a
351 special case where we want to conditionally enable/disable a tunable even
352 for setuid binaries. We use the special version of access() to avoid
353 setting ERRNO, which is a TLS variable since TLS has not yet been set
354 up. */
355 static inline void
356 __always_inline
357 maybe_enable_malloc_check (void)
359 tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
360 if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
361 tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
364 /* Initialize the tunables list from the environment. For now we only use the
365 ENV_ALIAS to find values. Later we will also use the tunable names to find
366 values. */
367 void
368 __tunables_init (char **envp)
370 char *envname = NULL;
371 char *envval = NULL;
372 size_t len = 0;
373 char **prev_envp = envp;
375 maybe_enable_malloc_check ();
377 while ((envp = get_next_env (envp, &envname, &len, &envval,
378 &prev_envp)) != NULL)
380 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
381 if (is_name (GLIBC_TUNABLES, envname))
383 char *new_env = tunables_strdup (envname);
384 if (new_env != NULL)
385 parse_tunables (new_env + len + 1, envval);
386 /* Put in the updated envval. */
387 *prev_envp = new_env;
388 continue;
390 #endif
392 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
394 tunable_t *cur = &tunable_list[i];
396 /* Skip over tunables that have either been set already or should be
397 skipped. */
398 if (cur->strval != NULL || cur->env_alias == NULL)
399 continue;
401 const char *name = cur->env_alias;
403 /* We have a match. Initialize and move on to the next line. */
404 if (is_name (name, envname))
406 /* For AT_SECURE binaries, we need to check the security settings of
407 the tunable and decide whether we read the value and also whether
408 we erase the value so that child processes don't inherit them in
409 the environment. */
410 if (__libc_enable_secure)
412 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
414 /* Erase the environment variable. */
415 char **ep = prev_envp;
417 while (*ep != NULL)
419 if (is_name (name, *ep))
421 char **dp = ep;
424 dp[0] = dp[1];
425 while (*dp++);
427 else
428 ++ep;
430 /* Reset the iterator so that we read the environment again
431 from the point we erased. */
432 envp = prev_envp;
435 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
436 continue;
439 tunable_initialize (cur, envval);
440 break;
446 /* Set the tunable value. This is called by the module that the tunable exists
447 in. */
448 void
449 __tunable_set_val (tunable_id_t id, void *valp, tunable_callback_t callback)
451 tunable_t *cur = &tunable_list[id];
453 /* Don't do anything if our tunable was not set during initialization or if
454 it failed validation. */
455 if (cur->strval == NULL)
456 return;
458 /* Caller does not need the value, just call the callback with our tunable
459 value. */
460 if (valp == NULL)
461 goto cb;
463 switch (cur->type.type_code)
465 case TUNABLE_TYPE_INT_32:
467 *((int32_t *) valp) = (int32_t) cur->val.numval;
468 break;
470 case TUNABLE_TYPE_SIZE_T:
472 *((size_t *) valp) = (size_t) cur->val.numval;
473 break;
475 case TUNABLE_TYPE_STRING:
477 *((const char **)valp) = cur->val.strval;
478 break;
480 default:
481 __builtin_unreachable ();
485 if (callback)
486 callback (&cur->val);