aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile5
-rw-r--r--elf/Versions3
-rw-r--r--elf/dl-support.c2
-rw-r--r--elf/dl-sysdep.c4
-rw-r--r--elf/dl-tunable-types.h46
-rw-r--r--elf/dl-tunables.c320
-rw-r--r--elf/dl-tunables.h88
-rw-r--r--elf/dl-tunables.list69
-rw-r--r--elf/rtld.c2
9 files changed, 539 insertions, 0 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 8a2ce02cd5..de28d99224 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -35,6 +35,11 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \
ifeq (yes,$(use-ldconfig))
dl-routines += dl-cache
endif
+
+ifeq (yes,$(have-tunables))
+dl-routines += dl-tunables
+endif
+
all-dl-routines = $(dl-routines) $(sysdep-dl-routines)
# But they are absent from the shared libc, because that code is in ld.so.
elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
diff --git a/elf/Versions b/elf/Versions
index 3d57e36fd2..6abe9db8bd 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -70,5 +70,8 @@ ld {
# Internal error handling support. Interposed by libc.so.
_dl_signal_error; _dl_catch_error;
+
+ # Set value of a tunable.
+ __tunable_set_val;
}
}
diff --git a/elf/dl-support.c b/elf/dl-support.c
index c30194c7b7..d350d6d0d0 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -354,8 +354,10 @@ _dl_non_dynamic_init (void)
cp = (const char *) __rawmemchr (cp, '\0') + 1;
}
+#if !HAVE_TUNABLES
if (__access ("/etc/suid-debug", F_OK) != 0)
__unsetenv ("MALLOC_CHECK_");
+#endif
}
#ifdef DL_PLATFORM_INIT
diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c
index eaa71556d2..4283767fe0 100644
--- a/elf/dl-sysdep.c
+++ b/elf/dl-sysdep.c
@@ -44,6 +44,8 @@
#include <hp-timing.h>
#include <tls.h>
+#include <dl-tunables.h>
+
extern char **_environ attribute_hidden;
extern char _end[] attribute_hidden;
@@ -219,6 +221,8 @@ _dl_sysdep_start (void **start_argptr,
}
#endif
+ __tunables_init (_environ);
+
#ifdef DL_SYSDEP_INIT
DL_SYSDEP_INIT;
#endif
diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
new file mode 100644
index 0000000000..d1591b6efb
--- /dev/null
+++ b/elf/dl-tunable-types.h
@@ -0,0 +1,46 @@
+/* Tunable type information.
+
+ Copyright (C) 2016 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/>. */
+
+#ifndef _TUNABLE_TYPES_H_
+# define _TUNABLE_TYPES_H_
+#include <stddef.h>
+
+typedef void (*tunable_callback_t) (void *);
+
+typedef enum
+{
+ TUNABLE_TYPE_INT_32,
+ TUNABLE_TYPE_SIZE_T,
+ TUNABLE_TYPE_STRING
+} tunable_type_code_t;
+
+typedef struct
+{
+ tunable_type_code_t type_code;
+ int64_t min;
+ int64_t max;
+} tunable_type_t;
+
+typedef union
+{
+ int64_t numval;
+ const char *strval;
+} tunable_val_t;
+
+#endif
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
new file mode 100644
index 0000000000..472747b3da
--- /dev/null
+++ b/elf/dl-tunables.c
@@ -0,0 +1,320 @@
+/* The tunable framework. See the README.tunables to know how to use the
+ tunable in a glibc module.
+
+ Copyright (C) 2016 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 <libc-internal.h>
+#include <sysdep.h>
+#include <fcntl.h>
+#include <ldsodefs.h>
+
+#define TUNABLES_INTERNAL 1
+#include "dl-tunables.h"
+
+/* Compare environment 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;
+}
+
+static char **
+get_next_env (char **envp, char **name, size_t *namelen, char **val)
+{
+ while (envp != NULL && *envp != NULL)
+ {
+ 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];
+
+ return ++envp;
+ }
+
+ return NULL;
+}
+
+static int
+tunables_unsetenv (char **ep, const char *name)
+{
+ while (*ep != NULL)
+ {
+ size_t cnt = 0;
+
+ while ((*ep)[cnt] == name[cnt] && name[cnt] != '\0')
+ ++cnt;
+
+ if (name[cnt] == '\0' && (*ep)[cnt] == '=')
+ {
+ /* Found it. Remove this pointer by moving later ones to
+ the front. */
+ char **dp = ep;
+
+ do
+ dp[0] = dp[1];
+ while (*dp++);
+ /* Continue the loop in case NAME appears again. */
+ }
+ else
+ ++ep;
+ }
+
+ return 0;
+}
+
+/* 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 unsigned long int
+tunables_strtoul (const char *nptr)
+{
+ unsigned long int 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)
+ {
+ unsigned long 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 > ULONG_MAX / base
+ || (result == ULONG_MAX / base && digval > ULONG_MAX % base))
+ return ULONG_MAX;
+ result *= base;
+ result += digval;
+ ++nptr;
+ }
+
+ return result * sign;
+}
+
+/* Initialize the internal type if the value validates either using the
+ explicit constraints of the tunable or with the implicit constraints of its
+ type. */
+static void
+tunable_set_val_if_valid_range (tunable_t *cur, const char *strval,
+ int64_t default_min, int64_t default_max)
+{
+ int64_t val = tunables_strtoul (strval);
+
+ int64_t min = cur->type.min;
+ int64_t max = cur->type.max;
+
+ if (min == max)
+ {
+ min = default_min;
+ max = default_max;
+ }
+
+ if (val >= min && val <= max)
+ {
+ cur->val.numval = val;
+ cur->strval = strval;
+ }
+}
+
+/* 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)
+{
+ switch (cur->type.type_code)
+ {
+ case TUNABLE_TYPE_INT_32:
+ {
+ tunable_set_val_if_valid_range (cur, strval, INT32_MIN, INT32_MAX);
+ break;
+ }
+ case TUNABLE_TYPE_SIZE_T:
+ {
+ tunable_set_val_if_valid_range (cur, strval, 0, SIZE_MAX);
+ break;
+ }
+ case TUNABLE_TYPE_STRING:
+ {
+ cur->val.strval = cur->strval = strval;
+ break;
+ }
+ default:
+ __builtin_unreachable ();
+ }
+}
+
+/* Disable a tunable if it is set. */
+static void
+disable_tunable (tunable_id_t id, char **envp)
+{
+ const char *env_alias = tunable_list[id].env_alias;
+
+ if (env_alias != NULL)
+ tunables_unsetenv (envp, tunable_list[id].env_alias);
+}
+
+/* Disable the glibc.malloc.check tunable in SETUID/SETGID programs unless
+ the system administrator overrides it by creating 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_disable_malloc_check (void)
+{
+ if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) != 0)
+ disable_tunable (TUNABLE_ENUM_NAME(glibc, malloc, check), __environ);
+}
+
+/* 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;
+
+ maybe_disable_malloc_check ();
+
+ while ((envp = get_next_env (envp, &envname, &len, &envval)) != NULL)
+ {
+ 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->strval != NULL || cur->env_alias == NULL
+ || (__libc_enable_secure && !cur->is_secure))
+ continue;
+
+ const char *name = cur->env_alias;
+
+ /* We have a match. Initialize and move on to the next line. */
+ if (is_name (name, envname))
+ {
+ tunable_initialize (cur, envval);
+ break;
+ }
+ }
+ }
+}
+
+/* Set the tunable value. This is called by the module that the tunable exists
+ in. */
+void
+__tunable_set_val (tunable_id_t id, void *valp, tunable_callback_t callback)
+{
+ tunable_t *cur = &tunable_list[id];
+
+ /* Don't do anything if our tunable was not set during initialization or if
+ it failed validation. */
+ if (cur->strval == NULL)
+ return;
+
+ if (valp == NULL)
+ goto cb;
+
+ switch (cur->type.type_code)
+ {
+ 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 ();
+ }
+
+cb:
+ if (callback)
+ callback (&cur->val);
+}
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
new file mode 100644
index 0000000000..a3f5472b45
--- /dev/null
+++ b/elf/dl-tunables.h
@@ -0,0 +1,88 @@
+/* The tunable framework. See the README to know how to use the tunable in
+ a glibc module.
+
+ Copyright (C) 2016 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/>. */
+
+#ifndef _TUNABLES_H_
+#define _TUNABLES_H_
+
+#if !HAVE_TUNABLES
+static inline void
+__always_inline
+__tunables_init (char **unused __attribute_unused)
+{
+ /* This is optimized out if tunables are not enabled. */
+}
+#else
+
+# include <stddef.h>
+# include "dl-tunable-types.h"
+
+/* A tunable. */
+struct _tunable
+{
+ const char *name; /* Internal name of the tunable. */
+ tunable_type_t type; /* Data type of the tunable. */
+ tunable_val_t val; /* The value. */
+ const char *strval; /* The string containing the value,
+ points into envp. */
+ bool is_secure; /* Whether the tunable must be read
+ even for setuid binaries. Note that
+ even if the tunable is read, it may
+ not get used by the target module if
+ the value is considered unsafe. */
+ /* Compatibility elements. */
+ const char *env_alias; /* The compatibility environment
+ variable name. */
+};
+
+typedef struct _tunable tunable_t;
+
+/* Full name for a tunable is top_ns.tunable_ns.id. */
+# define TUNABLE_NAME_S(top,ns,id) #top "." #ns "." #id
+
+# define TUNABLE_ENUM_NAME(top,ns,id) TUNABLE_ENUM_NAME1 (top,ns,id)
+# define TUNABLE_ENUM_NAME1(top,ns,id) top ## _ ## ns ## _ ## id
+
+# include "dl-tunable-list.h"
+
+extern void __tunables_init (char **);
+extern void __tunable_set_val (tunable_id_t, void *, tunable_callback_t);
+
+/* Check if the tunable has been set to a non-default value and if it is, copy
+ it over into __VAL. */
+# define TUNABLE_SET_VAL(__id,__val) \
+({ \
+ __tunable_set_val \
+ (TUNABLE_ENUM_NAME (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id), (__val), \
+ NULL); \
+})
+
+/* Same as TUNABLE_SET_VAL, but also call the callback function __CB. */
+# define TUNABLE_SET_VAL_WITH_CALLBACK(__id,__val,__cb) \
+({ \
+ __tunable_set_val \
+ (TUNABLE_ENUM_NAME (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id), (__val), \
+ DL_TUNABLE_CALLBACK (__cb)); \
+})
+
+/* Namespace sanity for callback functions. Use this macro to keep the
+ namespace of the modules clean. */
+# define DL_TUNABLE_CALLBACK(__name) _dl_tunable_ ## __name
+#endif
+#endif
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
new file mode 100644
index 0000000000..11504c49e3
--- /dev/null
+++ b/elf/dl-tunables.list
@@ -0,0 +1,69 @@
+# Copyright (C) 2016 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/>.
+
+# Allowed attributes for tunables:
+#
+# type: Defaults to STRING
+# minval: Optional minimum acceptable value
+# maxval: Optional maximum acceptable value
+# env_alias: An alias environment variable
+# is_secure: Specify whether the environment variable should be read for
+# setuid binaries.
+
+glibc {
+ malloc {
+ check {
+ type: INT_32
+ minval: 0
+ maxval: 3
+ env_alias: MALLOC_CHECK_
+ is_secure: true
+ }
+ top_pad {
+ type: SIZE_T
+ env_alias: MALLOC_TOP_PAD_
+ }
+ perturb {
+ type: INT_32
+ minval: 0
+ maxval: 0xff
+ env_alias: MALLOC_PERTURB_
+ }
+ mmap_threshold {
+ type: SIZE_T
+ env_alias: MALLOC_MMAP_THRESHOLD_
+ }
+ trim_threshold {
+ type: SIZE_T
+ env_alias: MALLOC_TRIM_THRESHOLD_
+ }
+ mmap_max {
+ type: INT_32
+ env_alias: MALLOC_MMAP_MAX_
+ }
+ arena_max {
+ type: SIZE_T
+ env_alias: MALLOC_ARENA_MAX
+ minval: 1
+ }
+ arena_test {
+ type: SIZE_T
+ env_alias: MALLOC_ARENA_TEST
+ minval: 1
+ }
+ }
+}
diff --git a/elf/rtld.c b/elf/rtld.c
index 4ec25d7c30..a60ead693b 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2510,7 +2510,9 @@ process_envvars (enum mode *modep)
if (__access ("/etc/suid-debug", F_OK) != 0)
{
+#if !HAVE_TUNABLES
unsetenv ("MALLOC_CHECK_");
+#endif
GLRO(dl_debug_mask) = 0;
}