Fix include paths in include/bits/types/*.h.
[glibc.git] / elf / dl-tunables.c
blob76e8c5cae16dc9dd32f97df22b04505984f09caf
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 <sysdep.h>
26 #include <fcntl.h>
27 #include <ldsodefs.h>
29 #define TUNABLES_INTERNAL 1
30 #include "dl-tunables.h"
32 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
33 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
34 #endif
36 /* Compare environment or tunable names, bounded by the name hardcoded in
37 glibc. */
38 static bool
39 is_name (const char *orig, const char *envname)
41 for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
42 if (*orig != *envname)
43 break;
45 /* The ENVNAME is immediately followed by a value. */
46 if (*orig == '\0' && *envname == '=')
47 return true;
48 else
49 return false;
52 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
53 static char *
54 tunables_strdup (const char *in)
56 size_t i = 0;
58 while (in[i++] != '\0');
59 char *out = __sbrk (i);
61 /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
62 set the thread-local errno since the TCB has not yet been set up. This
63 needs to be fixed with an __sbrk implementation that does not set
64 errno. */
65 if (out == (void *)-1)
66 return NULL;
68 i--;
70 while (i-- > 0)
71 out[i] = in[i];
73 return out;
75 #endif
77 static char **
78 get_next_env (char **envp, char **name, size_t *namelen, char **val,
79 char ***prev_envp)
81 while (envp != NULL && *envp != NULL)
83 char **prev = envp;
84 char *envline = *envp++;
85 int len = 0;
87 while (envline[len] != '\0' && envline[len] != '=')
88 len++;
90 /* Just the name and no value, go to the next one. */
91 if (envline[len] == '\0')
92 continue;
94 *name = envline;
95 *namelen = len;
96 *val = &envline[len + 1];
97 *prev_envp = prev;
99 return envp;
102 return NULL;
105 /* A stripped down strtoul-like implementation for very early use. It does not
106 set errno if the result is outside bounds because it gets called before
107 errno may have been set up. */
108 static uint64_t
109 tunables_strtoul (const char *nptr)
111 uint64_t result = 0;
112 long int sign = 1;
113 unsigned max_digit;
115 while (*nptr == ' ' || *nptr == '\t')
116 ++nptr;
118 if (*nptr == '-')
120 sign = -1;
121 ++nptr;
123 else if (*nptr == '+')
124 ++nptr;
126 if (*nptr < '0' || *nptr > '9')
127 return 0UL;
129 int base = 10;
130 max_digit = 9;
131 if (*nptr == '0')
133 if (nptr[1] == 'x' || nptr[1] == 'X')
135 base = 16;
136 nptr += 2;
138 else
140 base = 8;
141 max_digit = 7;
145 while (1)
147 int digval;
148 if (*nptr >= '0' && *nptr <= '0' + max_digit)
149 digval = *nptr - '0';
150 else if (base == 16)
152 if (*nptr >= 'a' && *nptr <= 'f')
153 digval = *nptr - 'a' + 10;
154 else if (*nptr >= 'A' && *nptr <= 'F')
155 digval = *nptr - 'A' + 10;
156 else
157 break;
159 else
160 break;
162 if (result >= (UINT64_MAX - digval) / base)
163 return UINT64_MAX;
164 result *= base;
165 result += digval;
166 ++nptr;
169 return result * sign;
172 #define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type, __default_min, \
173 __default_max) \
174 ({ \
175 __type min = (__cur)->type.min; \
176 __type max = (__cur)->type.max; \
178 if (min == max) \
180 min = __default_min; \
181 max = __default_max; \
184 if ((__type) (__val) >= min && (__type) (val) <= max) \
186 (__cur)->val.numval = val; \
187 (__cur)->initialized = true; \
191 static void
192 do_tunable_update_val (tunable_t *cur, const void *valp)
194 uint64_t val;
196 if (cur->type.type_code != TUNABLE_TYPE_STRING)
197 val = *((int64_t *) valp);
199 switch (cur->type.type_code)
201 case TUNABLE_TYPE_INT_32:
203 TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t, INT32_MIN, INT32_MAX);
204 break;
206 case TUNABLE_TYPE_UINT_64:
208 TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, UINT64_MAX);
209 break;
211 case TUNABLE_TYPE_SIZE_T:
213 TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, SIZE_MAX);
214 break;
216 case TUNABLE_TYPE_STRING:
218 cur->val.strval = valp;
219 break;
221 default:
222 __builtin_unreachable ();
226 /* Validate range of the input value and initialize the tunable CUR if it looks
227 good. */
228 static void
229 tunable_initialize (tunable_t *cur, const char *strval)
231 uint64_t val;
232 const void *valp;
234 if (cur->type.type_code != TUNABLE_TYPE_STRING)
236 val = tunables_strtoul (strval);
237 valp = &val;
239 else
241 cur->initialized = true;
242 valp = strval;
244 do_tunable_update_val (cur, valp);
247 void
248 __tunable_set_val (tunable_id_t id, void *valp)
250 tunable_t *cur = &tunable_list[id];
252 do_tunable_update_val (cur, valp);
255 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
256 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
257 be unsafe for AT_SECURE processes so that it can be used as the new
258 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
259 environment variable string which we use to make NULL terminated values so
260 that we don't have to allocate memory again for it. */
261 static void
262 parse_tunables (char *tunestr, char *valstring)
264 if (tunestr == NULL || *tunestr == '\0')
265 return;
267 char *p = tunestr;
269 while (true)
271 char *name = p;
272 size_t len = 0;
274 /* First, find where the name ends. */
275 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
276 len++;
278 /* If we reach the end of the string before getting a valid name-value
279 pair, bail out. */
280 if (p[len] == '\0')
281 return;
283 /* We did not find a valid name-value pair before encountering the
284 colon. */
285 if (p[len]== ':')
287 p += len + 1;
288 continue;
291 p += len + 1;
293 /* Take the value from the valstring since we need to NULL terminate it. */
294 char *value = &valstring[p - tunestr];
295 len = 0;
297 while (p[len] != ':' && p[len] != '\0')
298 len++;
300 /* Add the tunable if it exists. */
301 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
303 tunable_t *cur = &tunable_list[i];
305 if (is_name (cur->name, name))
307 /* If we are in a secure context (AT_SECURE) then ignore the tunable
308 unless it is explicitly marked as secure. Tunable values take
309 precendence over their envvar aliases. */
310 if (__libc_enable_secure)
312 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
314 if (p[len] == '\0')
316 /* Last tunable in the valstring. Null-terminate and
317 return. */
318 *name = '\0';
319 return;
321 else
323 /* Remove the current tunable from the string. We do
324 this by overwriting the string starting from NAME
325 (which is where the current tunable begins) with
326 the remainder of the string. We then have P point
327 to NAME so that we continue in the correct
328 position in the valstring. */
329 char *q = &p[len + 1];
330 p = name;
331 while (*q != '\0')
332 *name++ = *q++;
333 name[0] = '\0';
334 len = 0;
338 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
339 break;
342 value[len] = '\0';
343 tunable_initialize (cur, value);
344 break;
348 if (p[len] == '\0')
349 return;
350 else
351 p += len + 1;
354 #endif
356 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
357 the system administrator has created the /etc/suid-debug file. This is a
358 special case where we want to conditionally enable/disable a tunable even
359 for setuid binaries. We use the special version of access() to avoid
360 setting ERRNO, which is a TLS variable since TLS has not yet been set
361 up. */
362 static inline void
363 __always_inline
364 maybe_enable_malloc_check (void)
366 tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
367 if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
368 tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
371 /* Initialize the tunables list from the environment. For now we only use the
372 ENV_ALIAS to find values. Later we will also use the tunable names to find
373 values. */
374 void
375 __tunables_init (char **envp)
377 char *envname = NULL;
378 char *envval = NULL;
379 size_t len = 0;
380 char **prev_envp = envp;
382 maybe_enable_malloc_check ();
384 while ((envp = get_next_env (envp, &envname, &len, &envval,
385 &prev_envp)) != NULL)
387 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
388 if (is_name (GLIBC_TUNABLES, envname))
390 char *new_env = tunables_strdup (envname);
391 if (new_env != NULL)
392 parse_tunables (new_env + len + 1, envval);
393 /* Put in the updated envval. */
394 *prev_envp = new_env;
395 continue;
397 #endif
399 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
401 tunable_t *cur = &tunable_list[i];
403 /* Skip over tunables that have either been set already or should be
404 skipped. */
405 if (cur->initialized || cur->env_alias == NULL)
406 continue;
408 const char *name = cur->env_alias;
410 /* We have a match. Initialize and move on to the next line. */
411 if (is_name (name, envname))
413 /* For AT_SECURE binaries, we need to check the security settings of
414 the tunable and decide whether we read the value and also whether
415 we erase the value so that child processes don't inherit them in
416 the environment. */
417 if (__libc_enable_secure)
419 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
421 /* Erase the environment variable. */
422 char **ep = prev_envp;
424 while (*ep != NULL)
426 if (is_name (name, *ep))
428 char **dp = ep;
431 dp[0] = dp[1];
432 while (*dp++);
434 else
435 ++ep;
437 /* Reset the iterator so that we read the environment again
438 from the point we erased. */
439 envp = prev_envp;
442 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
443 continue;
446 tunable_initialize (cur, envval);
447 break;
453 /* Set the tunable value. This is called by the module that the tunable exists
454 in. */
455 void
456 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
458 tunable_t *cur = &tunable_list[id];
460 switch (cur->type.type_code)
462 case TUNABLE_TYPE_UINT_64:
464 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
465 break;
467 case TUNABLE_TYPE_INT_32:
469 *((int32_t *) valp) = (int32_t) cur->val.numval;
470 break;
472 case TUNABLE_TYPE_SIZE_T:
474 *((size_t *) valp) = (size_t) cur->val.numval;
475 break;
477 case TUNABLE_TYPE_STRING:
479 *((const char **)valp) = cur->val.strval;
480 break;
482 default:
483 __builtin_unreachable ();
486 if (cur->initialized && callback != NULL)
487 callback (&cur->val);
490 rtld_hidden_def (__tunable_get_val)