aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog41
-rw-r--r--INSTALL5
-rw-r--r--Makeconfig16
-rw-r--r--README.tunables85
-rw-r--r--config.h.in3
-rw-r--r--config.make.in1
-rwxr-xr-xconfigure16
-rw-r--r--configure.ac10
-rw-r--r--csu/init-first.c2
-rw-r--r--csu/libc-start.c8
-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
-rw-r--r--malloc/Makefile2
-rw-r--r--malloc/arena.c54
-rw-r--r--malloc/tst-malloc-usable-static.c1
-rw-r--r--manual/install.texi5
-rw-r--r--scripts/gen-tunables.awk157
-rw-r--r--sysdeps/mach/hurd/dl-sysdep.c4
25 files changed, 947 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index d219e5432b..85a4ac30df 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,44 @@
+2016-12-31 Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+ * manual/install.texi: Add --enable-tunables option.
+ * INSTALL: Regenerate.
+ * README.tunables: New file.
+ * Makeconfig (CPPFLAGS): Define TOP_NAMESPACE.
+ (before-compile): Generate dl-tunable-list.h early.
+ * config.h.in: Add HAVE_TUNABLES.
+ * config.make.in: Add have-tunables.
+ * configure.ac: Add --enable-tunables option.
+ * configure: Regenerate.
+ * csu/init-first.c (__libc_init_first): Move
+ __libc_init_secure earlier...
+ * csu/init-first.c (LIBC_START_MAIN):... to here.
+ Include dl-tunables.h, libc-internal.h.
+ (LIBC_START_MAIN) [!SHARED]: Initialize tunables for static
+ binaries.
+ * elf/Makefile (dl-routines): Add dl-tunables.
+ * elf/Versions (ld): Add __tunable_set_val to GLIBC_PRIVATE
+ namespace.
+ * elf/dl-support (_dl_nondynamic_init): Unset MALLOC_CHECK_
+ only when !HAVE_TUNABLES.
+ * elf/rtld.c (process_envvars): Likewise.
+ * elf/dl-sysdep.c [HAVE_TUNABLES]: Include dl-tunables.h
+ (_dl_sysdep_start): Call __tunables_init.
+ * elf/dl-tunable-types.h: New file.
+ * elf/dl-tunables.c: New file.
+ * elf/dl-tunables.h: New file.
+ * elf/dl-tunables.list: New file.
+ * malloc/tst-malloc-usable-static.c: New test case.
+ * malloc/Makefile (tests-static): Add it.
+ * malloc/arena.c [HAVE_TUNABLES]: Include dl-tunables.h.
+ Define TUNABLE_NAMESPACE.
+ (DL_TUNABLE_CALLBACK (set_mallopt_check)): New function.
+ (DL_TUNABLE_CALLBACK_FNDECL): New macro. Use it to define
+ callback functions.
+ (ptmalloc_init): Set tunable values.
+ * scripts/gen-tunables.awk: New file.
+ * sysdeps/mach/hurd/dl-sysdep.c: Include dl-tunables.h.
+ (_dl_sysdep_start): Call __tunables_init.
+
2016-12-31 Florian Weimer <fweimer@redhat.com>
* resolv/resolv.h (RES_BLAST): Deprecate.
diff --git a/INSTALL b/INSTALL
index 104f36b0bf..25619fc520 100644
--- a/INSTALL
+++ b/INSTALL
@@ -169,6 +169,11 @@ will be used, and CFLAGS sets optimization options for the compiler.
By default for x86_64, the GNU C Library is built with the vector
math library. Use this option to disable the vector math library.
+'--enable-tunables'
+ Tunables support allows additional library parameters to be
+ customized at runtime. This is an experimental feature and affects
+ startup time and is thus disabled by default.
+
'--build=BUILD-SYSTEM'
'--host=HOST-SYSTEM'
These options are for cross-compiling. If you specify both options
diff --git a/Makeconfig b/Makeconfig
index 0158eaa76e..b173e4cc08 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -934,6 +934,11 @@ CPPFLAGS = $(config-extra-cppflags) $(CPPUNDEFS) $(CPPFLAGS-config) \
$(foreach lib,$(libof-$(basename $(@F))) \
$(libof-$(<F)) $(libof-$(@F)),$(CPPFLAGS-$(lib))) \
$(CPPFLAGS-$(<F)) $(CPPFLAGS-$(@F)) $(CPPFLAGS-$(basename $(@F)))
+
+ifeq (yes,$(have-tunables))
+CPPFLAGS += -DTOP_NAMESPACE=glibc
+endif
+
override CFLAGS = -std=gnu11 -fgnu89-inline $(config-extra-cflags) \
$(filter-out %frame-pointer,$(+cflags)) $(+gccwarn-c) \
$(sysdep-CFLAGS) $(CFLAGS-$(suffix $@)) $(CFLAGS-$(<F)) \
@@ -1108,6 +1113,17 @@ $(common-objpfx)libc-modules.stmp: $(..)scripts/gen-libc-modules.awk \
endif
+# Build the tunables list header early since it could be used by any module in
+# glibc.
+ifeq (yes,$(have-tunables))
+before-compile += $(common-objpfx)dl-tunable-list.h
+
+$(common-objpfx)dl-tunable-list.h: $(..)scripts/gen-tunables.awk \
+ $(..)elf/dl-tunables.list
+ $(AWK) -f $^ > $@.tmp
+ mv $@.tmp $@
+endif
+
common-generated += libc-modules.h libc-modules.stmp
# The name under which the run-time dynamic linker is installed.
diff --git a/README.tunables b/README.tunables
new file mode 100644
index 0000000000..df74f3b24b
--- /dev/null
+++ b/README.tunables
@@ -0,0 +1,85 @@
+ TUNABLE FRAMEWORK
+ =================
+
+Tunables is a feature in the GNU C Library that allows application authors and
+distribution maintainers to alter the runtime library behaviour to match their
+workload.
+
+The tunable framework allows modules within glibc to register variables that
+may be tweaked through an environment variable. It aims to enforce a strict
+namespace rule to bring consistency to naming of these tunable environment
+variables across the project. This document is a guide for glibc developers to
+add tunables to the framework.
+
+ADDING A NEW TUNABLE
+--------------------
+
+The TOP_NAMESPACE macro is defined by default as 'glibc'. If distributions
+intend to add their own tunables, they should do so in a different top
+namespace by overriding the TOP_NAMESPACE macro for that tunable. Downstream
+implementations are discouraged from using the 'glibc' top namespace for
+tunables they don't already have consensus to push upstream.
+
+There are two steps to adding a tunable:
+
+1. Add a tunable ID:
+
+Modules that wish to use the tunables interface must define the
+TUNABLE_NAMESPACE macro. Following this, for each tunable you want to
+add, make an entry in elf/dl-tunables.list. The format of the file is as
+follows:
+
+TOP_NAMESPACE {
+ NAMESPACE1 {
+ TUNABLE1 {
+ # tunable attributes, one per line
+ }
+ # A tunable with default attributes, i.e. string variable.
+ TUNABLE2
+ TUNABLE3 {
+ # its attributes
+ }
+ }
+ NAMESPACE2 {
+ ...
+ }
+}
+
+The list of allowed attributes are:
+
+- type: Data type. Defaults to STRING. Allowed types are:
+ INT_32, SIZE_T and STRING.
+
+- minval: Optional minimum acceptable value. For a string type
+ this is the minimum length of the value.
+
+- maxval: Optional maximum acceptable value. For a string type
+ this is the maximum length of the value.
+
+- env_alias: An alias environment variable
+
+- is_secure: Specify whether the tunable should be read for setuid
+ binaries. True allows the tunable to be read for
+ setuid binaries while false disables it. Note that
+ even if this is set as true and the value is read, it
+ may not be used if it does not validate against the
+ acceptable values or is not considered safe by the
+ module.
+
+2. Call either the TUNABLE_SET_VALUE and pass into it the tunable name and a
+ pointer to the variable that should be set with the tunable value.
+ If additional work needs to be done after setting the value, use the
+ TUNABLE_SET_VALUE_WITH_CALLBACK instead and additionally pass a pointer to
+ the function that should be called if the tunable value has been set.
+
+FUTURE WORK
+-----------
+
+The framework currently only allows a one-time initialization of variables
+through environment variables and in some cases, modification of variables via
+an API call. A future goals for this project include:
+
+- Setting system-wide and user-wide defaults for tunables through some
+ mechanism like a configuration file.
+
+- Allow tweaking of some tunables at runtime
diff --git a/config.h.in b/config.h.in
index 82f95a6dbd..7bfe923c06 100644
--- a/config.h.in
+++ b/config.h.in
@@ -256,4 +256,7 @@
/* PowerPC32 uses fctidz for floating point to long long conversions. */
#define HAVE_PPC_FCTIDZ 0
+/* Build glibc with tunables support. */
+#define HAVE_TUNABLES 0
+
#endif
diff --git a/config.make.in b/config.make.in
index 4422025e59..2f8dae213d 100644
--- a/config.make.in
+++ b/config.make.in
@@ -96,6 +96,7 @@ use-nscd = @use_nscd@
build-hardcoded-path-in-tests= @hardcoded_path_in_tests@
build-pt-chown = @build_pt_chown@
enable-lock-elision = @enable_lock_elision@
+have-tunables = @have_tunables@
# Build tools.
CC = @CC@
diff --git a/configure b/configure
index c88f6fe88c..d80d738fd1 100755
--- a/configure
+++ b/configure
@@ -666,6 +666,7 @@ libc_cv_ssp
base_machine
add_on_subdirs
add_ons
+have_tunables
build_pt_chown
build_nscd
link_obsolete_rpc
@@ -782,6 +783,7 @@ enable_systemtap
enable_build_nscd
enable_nscd
enable_pt_chown
+enable_tunables
enable_mathvec
with_cpu
'
@@ -1452,6 +1454,7 @@ Optional Features:
--disable-build-nscd disable building and installing the nscd daemon
--disable-nscd library functions will not contact the nscd daemon
--enable-pt_chown Enable building and installing pt_chown
+ --enable-tunables Enable tunables support
--enable-mathvec Enable building and installing mathvec [default
depends on architecture]
@@ -3698,6 +3701,19 @@ if test "$build_pt_chown" = yes; then
fi
+# Check whether --enable-tunables was given.
+if test "${enable_tunables+set}" = set; then :
+ enableval=$enable_tunables; have_tunables=$enableval
+else
+ have_tunables=no
+fi
+
+
+if test "$have_tunables" = yes; then
+ $as_echo "#define HAVE_TUNABLES 1" >>confdefs.h
+
+fi
+
# The abi-tags file uses a fairly simplistic model for name recognition that
# can't distinguish i486-pc-linux-gnu fully from i486-pc-gnu. So we mutate a
# $host_os of `gnu*' here to be `gnu-gnu*' just so that it can tell.
diff --git a/configure.ac b/configure.ac
index 2782bfaf08..22f5cab200 100644
--- a/configure.ac
+++ b/configure.ac
@@ -421,6 +421,16 @@ if test "$build_pt_chown" = yes; then
AC_DEFINE(HAVE_PT_CHOWN)
fi
+AC_ARG_ENABLE([tunables],
+ [AS_HELP_STRING([--enable-tunables],
+ [Enable tunables support])],
+ [have_tunables=$enableval],
+ [have_tunables=no])
+AC_SUBST(have_tunables)
+if test "$have_tunables" = yes; then
+ AC_DEFINE(HAVE_TUNABLES)
+fi
+
# The abi-tags file uses a fairly simplistic model for name recognition that
# can't distinguish i486-pc-linux-gnu fully from i486-pc-gnu. So we mutate a
# $host_os of `gnu*' here to be `gnu-gnu*' just so that it can tell.
diff --git a/csu/init-first.c b/csu/init-first.c
index 77c6e1cb9e..465f25b722 100644
--- a/csu/init-first.c
+++ b/csu/init-first.c
@@ -72,8 +72,6 @@ _init (int argc, char **argv, char **envp)
__environ = envp;
#ifndef SHARED
- __libc_init_secure ();
-
/* First the initialization which normally would be done by the
dynamic linker. */
_dl_non_dynamic_init ();
diff --git a/csu/libc-start.c b/csu/libc-start.c
index cc59073abe..15db9b4684 100644
--- a/csu/libc-start.c
+++ b/csu/libc-start.c
@@ -21,6 +21,9 @@
#include <unistd.h>
#include <ldsodefs.h>
#include <exit-thread.h>
+#include <libc-internal.h>
+
+#include <elf/dl-tunables.h>
extern void __libc_init_first (int argc, char **argv, char **envp);
@@ -174,6 +177,11 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
}
}
+ /* Initialize very early so that tunables can use it. */
+ __libc_init_secure ();
+
+ __tunables_init (__environ);
+
/* Perform IREL{,A} relocations. */
apply_irel ();
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;
}
diff --git a/malloc/Makefile b/malloc/Makefile
index b8efcd68bc..4e4104ec8b 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -37,6 +37,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tests-static := \
tst-interpose-static-nothread \
tst-interpose-static-thread \
+ tst-malloc-usable-static
tests += $(tests-static)
test-srcs = tst-mtrace
@@ -158,6 +159,7 @@ endif
tst-mcheck-ENV = MALLOC_CHECK_=3
tst-malloc-usable-ENV = MALLOC_CHECK_=3
+tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV)
# Uncomment this for test releases. For public releases it is too expensive.
#CPPFLAGS-malloc.o += -DMALLOC_DEBUG=1
diff --git a/malloc/arena.c b/malloc/arena.c
index eed42471a7..234035f14f 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -19,6 +19,11 @@
#include <stdbool.h>
+#if HAVE_TUNABLES
+# define TUNABLE_NAMESPACE malloc
+#endif
+#include <elf/dl-tunables.h>
+
/* Compile-time constants. */
#define HEAP_MIN_SIZE (32 * 1024)
@@ -204,6 +209,34 @@ __malloc_fork_unlock_child (void)
__libc_lock_init (list_lock);
}
+#if HAVE_TUNABLES
+static inline int do_set_mallopt_check (int32_t value);
+void
+DL_TUNABLE_CALLBACK (set_mallopt_check) (void *valp)
+{
+ int32_t value = *(int32_t *) valp;
+ do_set_mallopt_check (value);
+ if (check_action != 0)
+ __malloc_check_init ();
+}
+
+# define DL_TUNABLE_CALLBACK_FNDECL(__name, __type) \
+static inline int do_ ## __name (__type value); \
+void \
+DL_TUNABLE_CALLBACK (__name) (void *valp) \
+{ \
+ __type value = *(__type *) valp; \
+ do_ ## __name (value); \
+}
+
+DL_TUNABLE_CALLBACK_FNDECL (set_mmap_threshold, size_t)
+DL_TUNABLE_CALLBACK_FNDECL (set_mmaps_max, int32_t)
+DL_TUNABLE_CALLBACK_FNDECL (set_top_pad, size_t)
+DL_TUNABLE_CALLBACK_FNDECL (set_perturb_byte, int32_t)
+DL_TUNABLE_CALLBACK_FNDECL (set_trim_threshold, size_t)
+DL_TUNABLE_CALLBACK_FNDECL (set_arena_max, size_t)
+DL_TUNABLE_CALLBACK_FNDECL (set_arena_test, size_t)
+#else
/* Initialization routine. */
#include <string.h>
extern char **_environ;
@@ -238,6 +271,7 @@ next_env_entry (char ***position)
return result;
}
+#endif
#ifdef SHARED
@@ -272,6 +306,24 @@ ptmalloc_init (void)
#endif
thread_arena = &main_arena;
+
+#if HAVE_TUNABLES
+ /* Ensure initialization/consolidation and do it under a lock so that a
+ thread attempting to use the arena in parallel waits on us till we
+ finish. */
+ __libc_lock_lock (main_arena.mutex);
+ malloc_consolidate (&main_arena);
+
+ TUNABLE_SET_VAL_WITH_CALLBACK (check, NULL, set_mallopt_check);
+ TUNABLE_SET_VAL_WITH_CALLBACK (top_pad, NULL, set_top_pad);
+ TUNABLE_SET_VAL_WITH_CALLBACK (perturb, NULL, set_perturb_byte);
+ TUNABLE_SET_VAL_WITH_CALLBACK (mmap_threshold, NULL, set_mmap_threshold);
+ TUNABLE_SET_VAL_WITH_CALLBACK (trim_threshold, NULL, set_trim_threshold);
+ TUNABLE_SET_VAL_WITH_CALLBACK (mmap_max, NULL, set_mmaps_max);
+ TUNABLE_SET_VAL_WITH_CALLBACK (arena_max, NULL, set_arena_max);
+ TUNABLE_SET_VAL_WITH_CALLBACK (arena_test, NULL, set_arena_test);
+ __libc_lock_unlock (main_arena.mutex);
+#else
const char *s = NULL;
if (__glibc_likely (_environ != NULL))
{
@@ -340,6 +392,8 @@ ptmalloc_init (void)
if (check_action != 0)
__malloc_check_init ();
}
+#endif
+
#if HAVE_MALLOC_INIT_HOOK
void (*hook) (void) = atomic_forced_read (__malloc_initialize_hook);
if (hook != NULL)
diff --git a/malloc/tst-malloc-usable-static.c b/malloc/tst-malloc-usable-static.c
new file mode 100644
index 0000000000..8907db01a5
--- /dev/null
+++ b/malloc/tst-malloc-usable-static.c
@@ -0,0 +1 @@
+#include <malloc/tst-malloc-usable.c>
diff --git a/manual/install.texi b/manual/install.texi
index d02e87091f..d41296294e 100644
--- a/manual/install.texi
+++ b/manual/install.texi
@@ -200,6 +200,11 @@ configure with @option{--disable-werror}.
By default for x86_64, @theglibc{} is built with the vector math library.
Use this option to disable the vector math library.
+@item --enable-tunables
+Tunables support allows additional library parameters to be customized at
+runtime. This is an experimental feature and affects startup time and is thus
+disabled by default.
+
@item --build=@var{build-system}
@itemx --host=@var{host-system}
These options are for cross-compiling. If you specify both options and
diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
new file mode 100644
index 0000000000..b65b5a4a33
--- /dev/null
+++ b/scripts/gen-tunables.awk
@@ -0,0 +1,157 @@
+# Generate dl-tunable-list.h from dl-tunables.list
+
+BEGIN {
+ tunable=""
+ ns=""
+ top_ns=""
+}
+
+# Skip over blank lines and comments.
+/^#/ {
+ next
+}
+
+/^[ \t]*$/ {
+ next
+}
+
+# Beginning of either a top namespace, tunable namespace or a tunable, decided
+# on the current value of TUNABLE, NS or TOP_NS.
+$2 == "{" {
+ if (top_ns == "") {
+ top_ns = $1
+ }
+ else if (ns == "") {
+ ns = $1
+ }
+ else if (tunable == "") {
+ tunable = $1
+ }
+ else {
+ printf ("Unexpected occurrence of '{': %s:%d\n", FILENAME, FNR)
+ exit 1
+ }
+
+ next
+}
+
+# End of either a top namespace, tunable namespace or a tunable.
+$1 == "}" {
+ if (tunable != "") {
+ # Tunables definition ended, now fill in default attributes.
+ if (!types[top_ns][ns][tunable]) {
+ types[top_ns][ns][tunable] = "STRING"
+ }
+ if (!minvals[top_ns][ns][tunable]) {
+ minvals[top_ns][ns][tunable] = "0"
+ }
+ if (!maxvals[top_ns][ns][tunable]) {
+ maxvals[top_ns][ns][tunable] = "0"
+ }
+ if (!env_alias[top_ns][ns][tunable]) {
+ env_alias[top_ns][ns][tunable] = "NULL"
+ }
+ if (!is_secure[top_ns][ns][tunable]) {
+ is_secure[top_ns][ns][tunable] = "false"
+ }
+
+ tunable = ""
+ }
+ else if (ns != "") {
+ ns = ""
+ }
+ else if (top_ns != "") {
+ top_ns = ""
+ }
+ else {
+ printf ("syntax error: extra }: %s:%d\n", FILENAME, FNR)
+ exit 1
+ }
+ next
+}
+
+# Everything else, which could either be a tunable without any attributes or a
+# tunable attribute.
+{
+ if (ns == "") {
+ printf("Line %d: Invalid tunable outside a namespace: %s\n", NR, $0)
+ exit 1
+ }
+
+ if (tunable == "") {
+ # We encountered a tunable without any attributes, so note it with a
+ # default.
+ types[top_ns][ns][$1] = "STRING"
+ next
+ }
+
+ # Otherwise, we have encountered a tunable attribute.
+ split($0, arr, ":")
+ attr = gensub(/^[ \t]+|[ \t]+$/, "", "g", arr[1])
+ val = gensub(/^[ \t]+|[ \t]+$/, "", "g", arr[2])
+
+ if (attr == "type") {
+ types[top_ns][ns][tunable] = val
+ }
+ else if (attr == "minval") {
+ minvals[top_ns][ns][tunable] = val
+ }
+ else if (attr == "maxval") {
+ maxvals[top_ns][ns][tunable] = val
+ }
+ else if (attr == "env_alias") {
+ env_alias[top_ns][ns][tunable] = sprintf("\"%s\"", val)
+ }
+ else if (attr == "is_secure") {
+ if (val == "true" || val == "false") {
+ is_secure[top_ns][ns][tunable] = val
+ }
+ else {
+ printf("Line %d: Invalid value (%s) for is_secure: %s, ", NR, val,
+ $0)
+ print("Allowed values are 'true' or 'false'")
+ exit 1
+ }
+ }
+}
+
+END {
+ if (ns != "") {
+ print "Unterminated namespace. Is a closing brace missing?"
+ exit 1
+ }
+
+ print "/* AUTOGENERATED by gen-tunables.awk. */"
+ print "#ifndef _TUNABLES_H_"
+ print "# error \"Do not include this file directly.\""
+ print "# error \"Include tunables.h instead.\""
+ print "#endif"
+
+ # Now, the enum names
+ print "\ntypedef enum"
+ print "{"
+ for (t in types) {
+ for (n in types[t]) {
+ for (m in types[t][n]) {
+ printf (" TUNABLE_ENUM_NAME(%s, %s, %s),\n", t, n, m);
+ }
+ }
+ }
+ print "} tunable_id_t;\n"
+
+ # Finally, the tunable list.
+ print "\n#ifdef TUNABLES_INTERNAL"
+ print "static tunable_t tunable_list[] = {"
+ for (t in types) {
+ for (n in types[t]) {
+ for (m in types[t][n]) {
+ printf (" {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
+ printf (", {TUNABLE_TYPE_%s, %s, %s}, {.numval = 0}, NULL, %s, %s},\n",
+ types[t][n][m], minvals[t][n][m], maxvals[t][n][m],
+ is_secure[t][n][m], env_alias[t][n][m]);
+ }
+ }
+ }
+ print "};"
+ print "#endif"
+}
diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c
index d730f82280..42bccdab79 100644
--- a/sysdeps/mach/hurd/dl-sysdep.c
+++ b/sysdeps/mach/hurd/dl-sysdep.c
@@ -44,6 +44,8 @@
#include <dl-machine.h>
#include <dl-procinfo.h>
+#include <dl-tunables.h>
+
extern void __mach_init (void);
extern int _dl_argc;
@@ -143,6 +145,8 @@ _dl_sysdep_start (void **start_argptr,
__libc_enable_secure = _dl_hurd_data->flags & EXEC_SECURE;
+ __tunables_init (_environ);
+
if (_dl_hurd_data->flags & EXEC_STACK_ARGS &&
_dl_hurd_data->user_entry == 0)
_dl_hurd_data->user_entry = (vm_address_t) ENTRY_POINT;