Taint: fix hybrid-mode transition
[users/jgh/exim.git] / src / src / store.c
index 61f9464af58bdd76b42d07529a05cce8bc78001c..3192b9774f35e3c30e168be9b8724c39a4849eb8 100644 (file)
@@ -186,7 +186,6 @@ static void   internal_tainted_free(storeblock *, const char *, int linenumber);
 
 /******************************************************************************/
 
-#ifndef TAINT_CHECK_FAST
 /* Test if a pointer refers to tainted memory.
 
 Slower version check, for use when platform intermixes malloc and mmap area
@@ -200,24 +199,22 @@ BOOL
 is_tainted_fn(const void * p)
 {
 storeblock * b;
-int pool;
 
-for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++)
+for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++)
   if ((b = current_block[pool]))
     {
-    char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK;
-    if (CS p >= bc && CS p <= bc + b->length) return TRUE;
+    uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK;
+    if (US p >= bc && US p <= bc + b->length) return TRUE;
     }
 
-for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++)
+for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++)
   for (b = chainbase[pool]; b; b = b->next)
     {
-    char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK;
-    if (CS p >= bc && CS p <= bc + b->length) return TRUE;
+    uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK;
+    if (US p >= bc && US p <= bc + b->length) return TRUE;
     }
 return FALSE;
 }
-#endif
 
 
 void
@@ -227,6 +224,37 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n",
        msg, func, line);
 }
 
+static void
+use_slow_taint_check(const uschar * why)
+{
+#ifndef COMPILE_UTILITY
+DEBUG(D_any)
+  debug_printf("switching to slow-mode taint checking (after %s) "
+             "taint bounds %p %p\n", why, tainted_base, tainted_top);
+#endif
+f.taint_check_slow = TRUE;
+}
+
+/* If the creation of a new tainted region results in any of the
+untainted regions appearing to be tainted, using the fast-mode test,
+we need to switch to safe-but-slow mode. */
+
+static void
+verify_all_untainted(void)
+{
+for (int pool = 0; pool < POOL_TAINT_BASE; pool++)
+  for (storeblock * b = chainbase[pool]; b; b = b->next)
+    {
+    uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK;
+    if (is_tainted(bc))
+      {
+      use_slow_taint_check(US"mmap");
+      return;
+      }
+    }
+}
+
+
 
 /*************************************************
 *       Get a block from the current pool        *
@@ -760,7 +788,7 @@ int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
 BOOL release_ok = !tainted && store_last_get[pool] == block;
 uschar * newtext;
 
-#ifndef MACRO_PREDEF
+#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY)
 if (is_tainted(block) != tainted)
   die_tainted(US"store_newblock", CUS func, linenumber);
 #endif
@@ -819,6 +847,7 @@ if (!(yield = mmap(NULL, (size_t)size,
 
 if (yield < tainted_base) tainted_base = yield;
 if ((top = US yield + size) > tainted_top) tainted_top = top;
+if (!f.taint_check_slow) verify_all_untainted();
 
 return store_alloc_tail(yield, size, func, line, US"Mmap");
 }
@@ -850,6 +879,14 @@ if (!(yield = malloc((size_t)size)))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to malloc %d bytes of memory: "
     "called from line %d in %s", size, linenumber, func);
 
+/* If malloc ever returns apparently tainted memory, which glibc
+malloc will as it uses mmap for larger requests, we must switch to
+the slower checking for tainting (checking an address against all
+the tainted pool block spans, rather than just the mmap span) */
+
+if (!f.taint_check_slow && is_tainted(yield))
+  use_slow_taint_check(US"malloc");
+
 return store_alloc_tail(yield, size, func, linenumber, US"Malloc");
 }