diff options
Diffstat (limited to 'elf/dl-tunables.c')
-rw-r--r-- | elf/dl-tunables.c | 490 |
1 files changed, 0 insertions, 490 deletions
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c deleted file mode 100644 index 76e8c5cae1..0000000000 --- a/elf/dl-tunables.c +++ /dev/null @@ -1,490 +0,0 @@ -/* The tunable framework. See the README.tunables to know how to use the - tunable in a glibc module. - - Copyright (C) 2016-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <http://www.gnu.org/licenses/>. */ - -#include <stdint.h> -#include <stdbool.h> -#include <unistd.h> -#include <stdlib.h> -#include <sysdep.h> -#include <fcntl.h> -#include <ldsodefs.h> - -#define TUNABLES_INTERNAL 1 -#include "dl-tunables.h" - -#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring -# define GLIBC_TUNABLES "GLIBC_TUNABLES" -#endif - -/* Compare environment or tunable names, bounded by the name hardcoded in - glibc. */ -static bool -is_name (const char *orig, const char *envname) -{ - for (;*orig != '\0' && *envname != '\0'; envname++, orig++) - if (*orig != *envname) - break; - - /* The ENVNAME is immediately followed by a value. */ - if (*orig == '\0' && *envname == '=') - return true; - else - return false; -} - -#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring -static char * -tunables_strdup (const char *in) -{ - size_t i = 0; - - while (in[i++] != '\0'); - char *out = __sbrk (i); - - /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to - set the thread-local errno since the TCB has not yet been set up. This - needs to be fixed with an __sbrk implementation that does not set - errno. */ - if (out == (void *)-1) - return NULL; - - i--; - - while (i-- > 0) - out[i] = in[i]; - - return out; -} -#endif - -static char ** -get_next_env (char **envp, char **name, size_t *namelen, char **val, - char ***prev_envp) -{ - while (envp != NULL && *envp != NULL) - { - char **prev = envp; - char *envline = *envp++; - int len = 0; - - while (envline[len] != '\0' && envline[len] != '=') - len++; - - /* Just the name and no value, go to the next one. */ - if (envline[len] == '\0') - continue; - - *name = envline; - *namelen = len; - *val = &envline[len + 1]; - *prev_envp = prev; - - return envp; - } - - return NULL; -} - -/* A stripped down strtoul-like implementation for very early use. It does not - set errno if the result is outside bounds because it gets called before - errno may have been set up. */ -static uint64_t -tunables_strtoul (const char *nptr) -{ - uint64_t result = 0; - long int sign = 1; - unsigned max_digit; - - while (*nptr == ' ' || *nptr == '\t') - ++nptr; - - if (*nptr == '-') - { - sign = -1; - ++nptr; - } - else if (*nptr == '+') - ++nptr; - - if (*nptr < '0' || *nptr > '9') - return 0UL; - - int base = 10; - max_digit = 9; - if (*nptr == '0') - { - if (nptr[1] == 'x' || nptr[1] == 'X') - { - base = 16; - nptr += 2; - } - else - { - base = 8; - max_digit = 7; - } - } - - while (1) - { - int digval; - if (*nptr >= '0' && *nptr <= '0' + max_digit) - digval = *nptr - '0'; - else if (base == 16) - { - if (*nptr >= 'a' && *nptr <= 'f') - digval = *nptr - 'a' + 10; - else if (*nptr >= 'A' && *nptr <= 'F') - digval = *nptr - 'A' + 10; - else - break; - } - else - break; - - if (result >= (UINT64_MAX - digval) / base) - return UINT64_MAX; - result *= base; - result += digval; - ++nptr; - } - - return result * sign; -} - -#define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type, __default_min, \ - __default_max) \ -({ \ - __type min = (__cur)->type.min; \ - __type max = (__cur)->type.max; \ - \ - if (min == max) \ - { \ - min = __default_min; \ - max = __default_max; \ - } \ - \ - if ((__type) (__val) >= min && (__type) (val) <= max) \ - { \ - (__cur)->val.numval = val; \ - (__cur)->initialized = true; \ - } \ -}) - -static void -do_tunable_update_val (tunable_t *cur, const void *valp) -{ - uint64_t val; - - if (cur->type.type_code != TUNABLE_TYPE_STRING) - val = *((int64_t *) valp); - - switch (cur->type.type_code) - { - case TUNABLE_TYPE_INT_32: - { - TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t, INT32_MIN, INT32_MAX); - break; - } - case TUNABLE_TYPE_UINT_64: - { - TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, UINT64_MAX); - break; - } - case TUNABLE_TYPE_SIZE_T: - { - TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, SIZE_MAX); - break; - } - case TUNABLE_TYPE_STRING: - { - cur->val.strval = valp; - break; - } - default: - __builtin_unreachable (); - } -} - -/* Validate range of the input value and initialize the tunable CUR if it looks - good. */ -static void -tunable_initialize (tunable_t *cur, const char *strval) -{ - uint64_t val; - const void *valp; - - if (cur->type.type_code != TUNABLE_TYPE_STRING) - { - val = tunables_strtoul (strval); - valp = &val; - } - else - { - cur->initialized = true; - valp = strval; - } - do_tunable_update_val (cur, valp); -} - -void -__tunable_set_val (tunable_id_t id, void *valp) -{ - tunable_t *cur = &tunable_list[id]; - - do_tunable_update_val (cur, valp); -} - -#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring -/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may - be unsafe for AT_SECURE processes so that it can be used as the new - environment variable value for GLIBC_TUNABLES. VALSTRING is the original - environment variable string which we use to make NULL terminated values so - that we don't have to allocate memory again for it. */ -static void -parse_tunables (char *tunestr, char *valstring) -{ - if (tunestr == NULL || *tunestr == '\0') - return; - - char *p = tunestr; - - while (true) - { - char *name = p; - size_t len = 0; - - /* First, find where the name ends. */ - while (p[len] != '=' && p[len] != ':' && p[len] != '\0') - len++; - - /* If we reach the end of the string before getting a valid name-value - pair, bail out. */ - if (p[len] == '\0') - return; - - /* We did not find a valid name-value pair before encountering the - colon. */ - if (p[len]== ':') - { - p += len + 1; - continue; - } - - p += len + 1; - - /* Take the value from the valstring since we need to NULL terminate it. */ - char *value = &valstring[p - tunestr]; - len = 0; - - while (p[len] != ':' && p[len] != '\0') - len++; - - /* Add the tunable if it exists. */ - for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++) - { - tunable_t *cur = &tunable_list[i]; - - if (is_name (cur->name, name)) - { - /* If we are in a secure context (AT_SECURE) then ignore the tunable - unless it is explicitly marked as secure. Tunable values take - precendence over their envvar aliases. */ - if (__libc_enable_secure) - { - if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE) - { - if (p[len] == '\0') - { - /* Last tunable in the valstring. Null-terminate and - return. */ - *name = '\0'; - return; - } - else - { - /* Remove the current tunable from the string. We do - this by overwriting the string starting from NAME - (which is where the current tunable begins) with - the remainder of the string. We then have P point - to NAME so that we continue in the correct - position in the valstring. */ - char *q = &p[len + 1]; - p = name; - while (*q != '\0') - *name++ = *q++; - name[0] = '\0'; - len = 0; - } - } - - if (cur->security_level != TUNABLE_SECLEVEL_NONE) - break; - } - - value[len] = '\0'; - tunable_initialize (cur, value); - break; - } - } - - if (p[len] == '\0') - return; - else - p += len + 1; - } -} -#endif - -/* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when - the system administrator has created the /etc/suid-debug file. This is a - special case where we want to conditionally enable/disable a tunable even - for setuid binaries. We use the special version of access() to avoid - setting ERRNO, which is a TLS variable since TLS has not yet been set - up. */ -static inline void -__always_inline -maybe_enable_malloc_check (void) -{ - tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check); - if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0) - tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE; -} - -/* Initialize the tunables list from the environment. For now we only use the - ENV_ALIAS to find values. Later we will also use the tunable names to find - values. */ -void -__tunables_init (char **envp) -{ - char *envname = NULL; - char *envval = NULL; - size_t len = 0; - char **prev_envp = envp; - - maybe_enable_malloc_check (); - - while ((envp = get_next_env (envp, &envname, &len, &envval, - &prev_envp)) != NULL) - { -#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring - if (is_name (GLIBC_TUNABLES, envname)) - { - char *new_env = tunables_strdup (envname); - if (new_env != NULL) - parse_tunables (new_env + len + 1, envval); - /* Put in the updated envval. */ - *prev_envp = new_env; - continue; - } -#endif - - for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++) - { - tunable_t *cur = &tunable_list[i]; - - /* Skip over tunables that have either been set already or should be - skipped. */ - if (cur->initialized || cur->env_alias == NULL) - continue; - - const char *name = cur->env_alias; - - /* We have a match. Initialize and move on to the next line. */ - if (is_name (name, envname)) - { - /* For AT_SECURE binaries, we need to check the security settings of - the tunable and decide whether we read the value and also whether - we erase the value so that child processes don't inherit them in - the environment. */ - if (__libc_enable_secure) - { - if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE) - { - /* Erase the environment variable. */ - char **ep = prev_envp; - - while (*ep != NULL) - { - if (is_name (name, *ep)) - { - char **dp = ep; - - do - dp[0] = dp[1]; - while (*dp++); - } - else - ++ep; - } - /* Reset the iterator so that we read the environment again - from the point we erased. */ - envp = prev_envp; - } - - if (cur->security_level != TUNABLE_SECLEVEL_NONE) - continue; - } - - tunable_initialize (cur, envval); - break; - } - } - } -} - -/* Set the tunable value. This is called by the module that the tunable exists - in. */ -void -__tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback) -{ - tunable_t *cur = &tunable_list[id]; - - switch (cur->type.type_code) - { - case TUNABLE_TYPE_UINT_64: - { - *((uint64_t *) valp) = (uint64_t) cur->val.numval; - break; - } - case TUNABLE_TYPE_INT_32: - { - *((int32_t *) valp) = (int32_t) cur->val.numval; - break; - } - case TUNABLE_TYPE_SIZE_T: - { - *((size_t *) valp) = (size_t) cur->val.numval; - break; - } - case TUNABLE_TYPE_STRING: - { - *((const char **)valp) = cur->val.strval; - break; - } - default: - __builtin_unreachable (); - } - - if (cur->initialized && callback != NULL) - callback (&cur->val); -} - -rtld_hidden_def (__tunable_get_val) |