Patchwork [7/7] Add white list for environment variables.

login
register
mail settings
Submitter Andi Kleen
Date Dec. 19, 2014, 7:37 a.m.
Message ID <1418974667-32587-8-git-send-email-andi@firstfloor.org>
Download mbox | patch
Permalink /patch/4360/
State New
Headers show

Comments

Andi Kleen - Dec. 19, 2014, 7:37 a.m.
From: Andi Kleen <ak@linux.intel.com>

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  <ak@linux.intel.com>

	* 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(-)

Patch

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 <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
 #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 <elision-conf.h>
 #include <unistd.h>
 #include <glibc-var.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+
+#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