From patchwork Fri Dec 19 07:37:47 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andi Kleen X-Patchwork-Id: 4360 Received: (qmail 15587 invoked by alias); 19 Dec 2014 07:38:11 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 15475 invoked by uid 89); 19 Dec 2014 07:38:10 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=AWL, BAYES_00, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mga09.intel.com X-ExtLoop1: 1 From: Andi Kleen To: libc-alpha@sourceware.org Cc: Andi Kleen Subject: [PATCH 7/7] Add white list for environment variables. Date: Thu, 18 Dec 2014 23:37:47 -0800 Message-Id: <1418974667-32587-8-git-send-email-andi@firstfloor.org> In-Reply-To: <1418974667-32587-1-git-send-email-andi@firstfloor.org> References: <1418974667-32587-1-git-send-email-andi@firstfloor.org> From: Andi Kleen This was based on a proposal from Roland. He was concerned that tuning elision may expose some problems with suid programs. This adds a white list interface to allow to enable/disable environment tuning. When the tuning is not white listed it cannot be done. This is implemented using a simple config file in /etc. When /etc/glibc-env.cfg contains "allow_elision_tuning" the GLIBC_PTHREAD_* variables can be used, otherwise they are disabled. When it contains "disallow_elision_tuning" tuning is always disabled. I made it a config switch because I'm not sure it's generally needed, but people who want it can enable it. 2014-12-17 Andi Kleen * sysdeps/unix/sysv/linux/x86/elision-conf.c (match): Helper function to match command in config file. (whitelist_env_var): New function to parse /etc/glibc-env.cfg (elision_init): Disallow tuning if not white listed. * configure.ac: Add --enable-elision-tune-whitelist switch. * INSTALL: Regenerate. * config.h.in: Regenerate. * manual/install.texi. Document --enable-elision-tune-whitelist. * nptl/tst-elision-common.c: Handle /etc/glibc-tune.cfg in test. --- INSTALL | 4 + config.h.in | 4 + configure | 18 ++++ configure.ac | 10 +++ manual/install.texi | 4 + nptl/tst-elision-common.c | 16 ++++ sysdeps/unix/sysv/linux/x86/elision-conf.c | 132 ++++++++++++++++++++++++++++- 7 files changed, 184 insertions(+), 4 deletions(-) diff --git a/INSTALL b/INSTALL index 35f70e0..983f7b5 100644 --- a/INSTALL +++ b/INSTALL @@ -131,6 +131,10 @@ will be used, and CFLAGS sets optimization options for the compiler. `--enable-lock-elision=yes' Enable lock elision for pthread mutexes by default. +`--enable-elision-tune-whitelist=yes' + Enable white list for elision environment variable tuning through + /etc/glibc-env.cfg. + `--enable-pt_chown' The file `pt_chown' is a helper binary for `grantpt' (*note Pseudo-Terminals: Allocation.) that is installed setuid root to diff --git a/config.h.in b/config.h.in index 695ca35..a3f2d9e 100644 --- a/config.h.in +++ b/config.h.in @@ -166,6 +166,10 @@ /* Define if lock elision should be enabled by default. */ #undef ENABLE_LOCK_ELISION +/* Define if environment variable for lock elision tuning should be white listed + through /etc/glibc-env.cfg. */ +#undef ENABLE_ELISION_TUNE_WHITELIST + /* Package description. */ #undef PKGVERSION diff --git a/configure b/configure index cde55de..b6bfa89 100755 --- a/configure +++ b/configure @@ -672,6 +672,7 @@ enable_werror all_warnings force_install bindnow +enable_elision_tune_whitelist enable_lock_elision hardcoded_path_in_tests use_default_link @@ -760,6 +761,7 @@ enable_profile enable_hardcoded_path_in_tests enable_stackguard_randomization enable_lock_elision +enable_elision_tune_whitelist enable_add_ons enable_hidden_plt enable_bind_now @@ -1416,6 +1418,9 @@ Optional Features: number at program start --enable-lock-elision=yes/no Enable lock elision for pthread mutexes by default + --enable-elision-tune-whitelist=yes/no + Enable white list for elision environment variable + tuning through /etc/glibc-env.cfg --enable-add-ons[=DIRS...] configure and build add-ons in DIR1,DIR2,... search for add-ons if no parameter given @@ -3488,6 +3493,19 @@ if test "$enable_lock_elision" = yes ; then fi +# Check whether --enable-elision-tune-whitelist was given. +if test "${enable_elision_tune_whitelist+set}" = set; then : + enableval=$enable_elision_tune_whitelist; enable_elision_tune_whitelist=$enableval +else + enable_elision_tune_whitelist=no +fi + + +if test "$enable_elision_tune_whitelist" = yes ; then + $as_echo "#define ENABLE_ELISION_TUNE_WHITELIST 1" >>confdefs.h + +fi + # Check whether --enable-add-ons was given. if test "${enable_add_ons+set}" = set; then : enableval=$enable_add_ons; diff --git a/configure.ac b/configure.ac index f1e6394..fcb007d 100644 --- a/configure.ac +++ b/configure.ac @@ -177,6 +177,16 @@ if test "$enable_lock_elision" = yes ; then AC_DEFINE(ENABLE_LOCK_ELISION) fi +AC_ARG_ENABLE([elision-tune-whitelist], + AC_HELP_STRING([--enable-elision-tune-whitelist[=yes/no]], + [Enable white list for elision environment variable tuning through /etc/glibc-env.cfg]), + [enable_elision_tune_whitelist=$enableval], + [enable_elision_tune_whitelist=no]) +AC_SUBST(enable_elision_tune_whitelist) +if test "$enable_elision_tune_whitelist" = yes ; then + AC_DEFINE(ENABLE_ELISION_TUNE_WHITELIST) +fi + dnl Generic infrastructure for drop-in additions to libc. AC_ARG_ENABLE([add-ons], AC_HELP_STRING([--enable-add-ons@<:@=DIRS...@:>@], diff --git a/manual/install.texi b/manual/install.texi index 2b9b57c..e0d80d2 100644 --- a/manual/install.texi +++ b/manual/install.texi @@ -160,6 +160,10 @@ so that they can be invoked directly. @item --enable-lock-elision=yes Enable lock elision for pthread mutexes by default. +@item --enable-elision-tune-whitelist=yes +Enable white listing for elision environment variable tuning through +/etc/glibc-env.cfg. + @pindex pt_chown @findex grantpt @item --enable-pt_chown diff --git a/nptl/tst-elision-common.c b/nptl/tst-elision-common.c index 5aaf0f6..d23bbcf 100644 --- a/nptl/tst-elision-common.c +++ b/nptl/tst-elision-common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "config.h" #define CPUID_FEATURE_RTM (1U << 11) @@ -111,6 +112,21 @@ elision_override (const char *var, int iter) { char *s = getenv(var); +#ifdef ENABLE_ELISION_TUNE_WHITELIST + if (access ("/etc/glibc-env.cfg", R_OK)) + { + if (system("grep 2>/dev/null disallow_elision_tuning /etc/glibc-env.cfg")) + { + printf ("elision env var override disabled\n"); + return iter; + } + if (system("grep 2>/dev/null allow_elision_tuning /etc/glibc-env.cfg")) + printf("elision env var override enabled\n"); + } + else + return iter; +#endif + s = getenv(var); if (s && strstr (s, "none")) return 0; diff --git a/sysdeps/unix/sysv/linux/x86/elision-conf.c b/sysdeps/unix/sysv/linux/x86/elision-conf.c index b0c0ea8..f2f0c3d 100644 --- a/sysdeps/unix/sysv/linux/x86/elision-conf.c +++ b/sysdeps/unix/sysv/linux/x86/elision-conf.c @@ -22,6 +22,10 @@ #include #include #include +#include +#include + +#define PAIR(x) x, sizeof (x)-1 /* Reasonable initial tuning values, may be revised in the future. This is a conservative initial value. */ @@ -80,8 +84,6 @@ static const struct tune tunings[] = {} }; -#define PAIR(x) x, sizeof (x)-1 - /* Complain. */ static void @@ -192,6 +194,125 @@ elision_rwlock_init (const char *s) complain (PAIR ("pthreads: Unknown setting for GLIBC_PTHREAD_RWLOCK\n")); } +#define X86_PAGE_SIZE 4096 +#define ENV_VAR_WHITELIST "/etc/glibc-env.cfg" + +/* Does specific M match LINE before END? Write length back to LENP. */ + +static bool +match (char *line, char *end, const char *m, int *lenp) +{ + int len = strlen (m); + + *lenp = 0; + if (len > end - line) + return false; + if (!strncmp (line, m, len)) + { + *lenp = len; + return true; + } + return false; +} + +/* INTERNAL_SYSCALL does not support 6 argument calls on i386, so do our own + mmap stub here. addr and offset are hardcoded to 0 to simplify the + assembler. */ + +static inline void * +mmap_00(size_t length, int prot, int flags, int fd) +{ + void *ret; + +#ifdef __i386__ + asm( + "push %%ebx\n\t" + "push %%ebp\n\t" + "mov %1,%%eax\n\t" /* syscall number */ + "xor %%ebx,%%ebx\n\t" /* addr */ + "xor %%ebp,%%ebp\n\t" /* offset */ + "int $0x80\n\t" + "pop %%ebp\n\t" + "pop %%ebx\n\t" + : "=a" (ret) + : "i" (__NR_mmap2), "c" (length), "d" (prot), "S" (flags), "D" (fd)); +#else + INTERNAL_SYSCALL_DECL (err); + ret = (void *)INTERNAL_SYSCALL (mmap, err, 6, NULL, length, + prot, flags, fd, 0); +#endif + return ret; +} + +/* Check extra file to see if we're allowed to use environment variables. + Should be moved elsewhere if extended for other purposes. */ + +static bool +whitelist_env_var (void) +{ + int fd; + struct stat st; + INTERNAL_SYSCALL_DECL (err); + bool ok = false; + +#ifndef ENABLE_ELISION_TUNE_WHITELIST + return true; +#endif + + fd = INTERNAL_SYSCALL (open, err, 2, ENV_VAR_WHITELIST, O_RDONLY); + if (fd < 0) + return false; + if (INTERNAL_SYSCALL (fstat, err, 2, fd, &st) >= 0) + { + size_t size = (st.st_size + X86_PAGE_SIZE - 1) & ~(X86_PAGE_SIZE - 1); + char *map = mmap_00 (size, PROT_READ, MAP_SHARED, fd); + if (map != (char *)-1L) + { + char *line; + char *end = map + st.st_size; + + for (line = map; line < end; ) + { + while (line < end && (*line == ' ' || *line == '\t' || *line == '\n')) + line++; + if (line >= end) + break; + if (*line == '#') + { + while (line < end && *line != '\n') + line++; + } + else if (*line != '\n') + { + int len = 0; + + if (match (line, end, "allow_elision_tuning", &len)) + ok = true; + else if (match (line, end, "disallow_elision_tuning", &len)) + ok = false; + else + { + complain (PAIR ("Unknown configuration specifier in " ENV_VAR_WHITELIST "\n")); + len = end - line; + if (len > 20) + len = 20; + complain (line, len); + complain (PAIR ("\n")); + ok = false; + break; + } + line += len; + while (line < end && (*line == ' ' || *line == '\t' || *line == '\n')) + line++; + } + } + INTERNAL_SYSCALL (munmap, err, 2, map, size); + } + } + INTERNAL_SYSCALL (close, err, 1, fd); + return ok; +} + /* Initialize elison. */ static void @@ -210,8 +331,11 @@ elision_init (int argc __attribute__ ((unused)), /* For static builds need to call this explicitely. Noop for dynamic. */ __glibc_var_init (argc, argv, environ); - elision_mutex_init (_dl_glibc_var[GLIBC_VAR_PTHREAD_MUTEX].val); - elision_rwlock_init (_dl_glibc_var[GLIBC_VAR_PTHREAD_RWLOCK].val); + if (whitelist_env_var ()) + { + elision_mutex_init (_dl_glibc_var[GLIBC_VAR_PTHREAD_MUTEX].val); + elision_rwlock_init (_dl_glibc_var[GLIBC_VAR_PTHREAD_RWLOCK].val); + } } #ifdef SHARED