and tainted. The latter is used for values derived from untrusted input, and
the string-expansion mechanism refuses to operate on such values (obviously,
it can expand an untainted value to return a tainted result). The classes
- are implemented by duplicating the three pool types. Pool resets are requested
+ are implemented by duplicating the three pool types. Pool resets are requested
against the nontainted sibling and apply to both siblings.
+
+ Only memory blocks requested for tainted use are regarded as tainted; anything
+ else (including stack auto variables) is untainted. Care is needed when coding
+ to not copy untrusted data into untainted memory, as downstream taint-checks
+ would be avoided.
+
+ 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).
*/
static int max_nonpool_malloc; /* max value for nonpool_malloc */
+#ifndef COMPILE_UTILITY
static const uschar * pooluse[NPOOLS] = {
[POOL_MAIN] = US"main",
[POOL_PERM] = US"perm",
[POOL_TAINT_PERM] = US"tainted",
[POOL_TAINT_SEARCH] = US"tainted",
};
+#endif
static void * store_mmap(int, const char *, int);
static void * internal_store_malloc(int, const char *, int);
-static void internal_store_free(void *, const char *, int linenumber);
+static void internal_untainted_free(void *, const char *, int linenumber);
+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
+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 = 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;
+ }
+
+for (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;
+ }
+return FALSE;
+}
+#endif
+
+
void
die_tainted(const uschar * msg, const uschar * func, int line)
{
store_last_was_get.
Arguments:
- size amount wanted
+ size amount wanted, bytes
+ tainted class: set to true for untrusted data (eg. from smtp input)
func function from which called
linenumber line number in source file
/* Give up on this block, because it's too small */
nblocks[pool]--;
if (pool < POOL_TAINT_BASE)
- internal_store_free(newblock, func, linenumber);
+ internal_untainted_free(newblock, func, linenumber);
else
- {
-#ifndef COMPILE_UTILITY
- DEBUG(D_memory)
- debug_printf("---Unmap %6p %-20s %4d\n", newblock, func, linenumber);
-#endif
- munmap(newblock, newblock->length + ALIGNED_SIZEOF_STOREBLOCK);
- }
+ internal_tainted_free(newblock, func, linenumber);
newblock = NULL;
}
pool_malloc -= siz;
nblocks[pool]--;
if (pool < POOL_TAINT_BASE)
- internal_store_free(b, func, linenumber);
+ internal_untainted_free(b, func, linenumber);
else
- {
-#ifndef COMPILE_UTILITY
- DEBUG(D_memory)
- debug_printf("---Unmap %6p %-20s %4d\n", b, func, linenumber);
-#endif
- munmap(b, b->length + ALIGNED_SIZEOF_STOREBLOCK);
- }
+ internal_tainted_free(b, func, linenumber);
}
/* Cut out the debugging stuff for utilities, but stop picky compilers from
"called from line %d of %s", size, line, func);
if (yield < tainted_base) tainted_base = yield;
-if ((top = yield + size) > tainted_top) tainted_top = top;
+if ((top = US yield + size) > tainted_top) tainted_top = top;
return store_alloc_tail(yield, size, func, line, US"Mmap");
}
*/
static void
-internal_store_free(void *block, const char *func, int linenumber)
+internal_untainted_free(void * block, const char * func, int linenumber)
{
#ifdef COMPILE_UTILITY
func = func;
}
void
-store_free_3(void *block, const char *func, int linenumber)
+store_free_3(void * block, const char * func, int linenumber)
{
n_nonpool_blocks--;
-internal_store_free(block, func, linenumber);
+internal_untainted_free(block, func, linenumber);
+}
+
+/******************************************************************************/
+static void
+internal_tainted_free(storeblock * block, const char * func, int linenumber)
+{
+#ifdef COMPILE_UTILITY
+func = func;
+linenumber = linenumber;
+#else
+DEBUG(D_memory)
+ debug_printf("---Unmap %6p %-20s %4d\n", block, func, linenumber);
+#endif
+munmap((void *)block, block->length + ALIGNED_SIZEOF_STOREBLOCK);
}
/******************************************************************************/