to not copy untrusted data into untainted memory, as downstream taint-checks
would be avoided.
- Intermediate layers (eg. the string functions) can test for taint, and use this
- for ensuringn that results have proper state. For example the
- string_vformat_trc() routing supporting the string_sprintf() interface will
- recopy a string being built into a tainted allocation if it meets a %s for a
- tainted argument.
-
Internally we currently use malloc for nontainted pools, and mmap for tainted
pools. The disparity is for speed of testing the taintedness of pointers;
because Linux appears to use distinct non-overlapping address allocations for
mmap vs. everything else, which means only two pointer-compares suffice for the
test. Other OS' cannot use that optimisation, and a more lengthy test against
the limits of tainted-pool allcations has to be done.
+
+ Intermediate layers (eg. the string functions) can test for taint, and use this
+ for ensurinng that results have proper state. For example the
+ string_vformat_trc() routing supporting the string_sprintf() interface will
+ recopy a string being built into a tainted allocation if it meets a %s for a
+ tainted argument. Any intermediate-layer function that (can) return a new
+ allocation should behave this way; returning a tainted result if any tainted
+ content is used. Intermediate-layer functions (eg. Ustrncpy) that modify
+ existing allocations fail if tainted data is written into an untainted area.
+ Users of functions that modify existing allocations should check if a tainted
+ source and an untainted destination is used, and fail instead (sprintf() being
+ the classic case).
*/
/******************************************************************************/
-#ifndef TAINT_CHECK_FAST
-/* Slower version check, for use when platform intermixes malloc and mmap area
-addresses. */
+/* Test if a pointer refers to tainted memory.
+
+Slower version check, for use when platform intermixes malloc and mmap area
+addresses. Test against the current-block of all tainted pools first, then all
+blocks of all tainted pools.
+
+Return: TRUE iff tainted
+*/
BOOL
is_tainted_fn(const void * p)
{
storeblock * b;
-int pool;
-for (pool = 0; 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) goto hit;
+ uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK;
+ if (US p >= bc && US p <= bc + b->length) return TRUE;
}
-for (pool = 0; 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) goto hit;
+ uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK;
+ if (US p >= bc && US p <= bc + b->length) return TRUE;
}
return FALSE;
-
-hit:
-return pool >= POOL_TAINT_BASE;
}
-#endif
void
msg, func, line);
}
+static void
+use_slow_taint_check(void)
+{
+#ifndef COMPILE_UTILITY
+DEBUG(D_any) debug_printf("switching to slow-mode taint checking\n");
+#endif
+f.taint_check_slow = TRUE;
+}
+
+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();
+ return;
+ }
+ }
+}
+
+
/*************************************************
* Get a block from the current 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
if (yield < tainted_base) tainted_base = yield;
if ((top = US yield + size) > tainted_top) tainted_top = top;
+if (!f.taint_check_slow) use_slow_taint_check();
return store_alloc_tail(yield, size, func, line, US"Mmap");
}
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();
+
return store_alloc_tail(yield, size, func, linenumber, US"Malloc");
}