9bb5d6
Fix SXID_ERASE behavior in setuid programs (BZ #27471)
9bb5d6
9bb5d6
When parse_tunables tries to erase a tunable marked as SXID_ERASE for
9bb5d6
setuid programs, it ends up setting the envvar string iterator
9bb5d6
incorrectly, because of which it may parse the next tunable
9bb5d6
incorrectly.  Given that currently the implementation allows malformed
9bb5d6
and unrecognized tunables pass through, it may even allow SXID_ERASE
9bb5d6
tunables to go through.
9bb5d6
9bb5d6
This change revamps the SXID_ERASE implementation so that:
9bb5d6
9bb5d6
- Only valid tunables are written back to the tunestr string, because
9bb5d6
  of which children of SXID programs will only inherit a clean list of
9bb5d6
  identified tunables that are not SXID_ERASE.
9bb5d6
9bb5d6
- Unrecognized tunables get scrubbed off from the environment and
9bb5d6
  subsequently from the child environment.
9bb5d6
9bb5d6
- This has the side-effect that a tunable that is not identified by
9bb5d6
  the setxid binary, will not be passed on to a non-setxid child even
9bb5d6
  if the child could have identified that tunable.  This may break
9bb5d6
  applications that expect this behaviour but expecting such tunables
9bb5d6
  to cross the SXID boundary is wrong.
9bb5d6
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
9bb5d6
9bb5d6
(cherry picked from commit 2ed18c5b534d9e92fc006202a5af0df6b72e7aca)
9bb5d6
9bb5d6
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
9bb5d6
index 4c9d36e3980758b9..bbc3679e3564a766 100644
9bb5d6
--- a/elf/dl-tunables.c
9bb5d6
+++ b/elf/dl-tunables.c
9bb5d6
@@ -178,6 +178,7 @@ parse_tunables (char *tunestr, char *valstring)
9bb5d6
     return;
9bb5d6
 
9bb5d6
   char *p = tunestr;
9bb5d6
+  size_t off = 0;
9bb5d6
 
9bb5d6
   while (true)
9bb5d6
     {
9bb5d6
@@ -191,7 +192,11 @@ parse_tunables (char *tunestr, char *valstring)
9bb5d6
       /* If we reach the end of the string before getting a valid name-value
9bb5d6
 	 pair, bail out.  */
9bb5d6
       if (p[len] == '\0')
9bb5d6
-	return;
9bb5d6
+	{
9bb5d6
+	  if (__libc_enable_secure)
9bb5d6
+	    tunestr[off] = '\0';
9bb5d6
+	  return;
9bb5d6
+	}
9bb5d6
 
9bb5d6
       /* We did not find a valid name-value pair before encountering the
9bb5d6
 	 colon.  */
9bb5d6
@@ -217,35 +222,28 @@ parse_tunables (char *tunestr, char *valstring)
9bb5d6
 
9bb5d6
 	  if (tunable_is_name (cur->name, name))
9bb5d6
 	    {
9bb5d6
-	      /* If we are in a secure context (AT_SECURE) then ignore the tunable
9bb5d6
-		 unless it is explicitly marked as secure.  Tunable values take
9bb5d6
-		 precendence over their envvar aliases.  */
9bb5d6
+	      /* If we are in a secure context (AT_SECURE) then ignore the
9bb5d6
+		 tunable unless it is explicitly marked as secure.  Tunable
9bb5d6
+		 values take precedence over their envvar aliases.  We write
9bb5d6
+		 the tunables that are not SXID_ERASE back to TUNESTR, thus
9bb5d6
+		 dropping all SXID_ERASE tunables and any invalid or
9bb5d6
+		 unrecognized tunables.  */
9bb5d6
 	      if (__libc_enable_secure)
9bb5d6
 		{
9bb5d6
-		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
9bb5d6
+		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
9bb5d6
 		    {
9bb5d6
-		      if (p[len] == '\0')
9bb5d6
-			{
9bb5d6
-			  /* Last tunable in the valstring.  Null-terminate and
9bb5d6
-			     return.  */
9bb5d6
-			  *name = '\0';
9bb5d6
-			  return;
9bb5d6
-			}
9bb5d6
-		      else
9bb5d6
-			{
9bb5d6
-			  /* Remove the current tunable from the string.  We do
9bb5d6
-			     this by overwriting the string starting from NAME
9bb5d6
-			     (which is where the current tunable begins) with
9bb5d6
-			     the remainder of the string.  We then have P point
9bb5d6
-			     to NAME so that we continue in the correct
9bb5d6
-			     position in the valstring.  */
9bb5d6
-			  char *q = &p[len + 1];
9bb5d6
-			  p = name;
9bb5d6
-			  while (*q != '\0')
9bb5d6
-			    *name++ = *q++;
9bb5d6
-			  name[0] = '\0';
9bb5d6
-			  len = 0;
9bb5d6
-			}
9bb5d6
+		      if (off > 0)
9bb5d6
+			tunestr[off++] = ':';
9bb5d6
+
9bb5d6
+		      const char *n = cur->name;
9bb5d6
+
9bb5d6
+		      while (*n != '\0')
9bb5d6
+			tunestr[off++] = *n++;
9bb5d6
+
9bb5d6
+		      tunestr[off++] = '=';
9bb5d6
+
9bb5d6
+		      for (size_t j = 0; j < len; j++)
9bb5d6
+			tunestr[off++] = value[j];
9bb5d6
 		    }
9bb5d6
 
9bb5d6
 		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
9bb5d6
@@ -258,9 +256,7 @@ parse_tunables (char *tunestr, char *valstring)
9bb5d6
 	    }
9bb5d6
 	}
9bb5d6
 
9bb5d6
-      if (p[len] == '\0')
9bb5d6
-	return;
9bb5d6
-      else
9bb5d6
+      if (p[len] != '\0')
9bb5d6
 	p += len + 1;
9bb5d6
     }
9bb5d6
 }
9bb5d6
diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
9bb5d6
index a48281b175af6920..0b9b075c40598c6f 100644
9bb5d6
--- a/elf/tst-env-setuid-tunables.c
9bb5d6
+++ b/elf/tst-env-setuid-tunables.c
9bb5d6
@@ -45,11 +45,37 @@
9bb5d6
 const char *teststrings[] =
9bb5d6
 {
9bb5d6
   "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
9bb5d6
+  "glibc.malloc.perturb=0x800",
9bb5d6
+  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
9bb5d6
+  "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
9bb5d6
+  ":glibc.malloc.garbage=2:glibc.malloc.check=1",
9bb5d6
+  "glibc.malloc.check=1:glibc.malloc.check=2",
9bb5d6
+  "not_valid.malloc.check=2",
9bb5d6
+  "glibc.not_valid.check=2",
9bb5d6
 };
9bb5d6
 
9bb5d6
 const char *resultstrings[] =
9bb5d6
 {
9bb5d6
   "glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.perturb=0x800",
9bb5d6
+  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "glibc.malloc.mmap_threshold=4096",
9bb5d6
+  "",
9bb5d6
+  "",
9bb5d6
+  "",
9bb5d6
+  "",
9bb5d6
+  "",
9bb5d6
+  "",
9bb5d6
 };
9bb5d6
 
9bb5d6
 static int