*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim maintainers 2019 */
+/* Copyright (c) The Exim maintainers 2019 - 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Exim gets and frees all its store through these functions. In the original
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
+ 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.
-
- 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.
+ 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 void *next_yield[NPOOLS];
static int yield_length[NPOOLS] = { -1, -1, -1, -1, -1, -1 };
-/* The limits of the tainted pools. Tracking these on new allocations enables
-a fast is_tainted implementation. We assume the kernel only allocates mmaps using
-one side or the other of data+heap, not both. */
-
-void * tainted_base = (void *)-1;
-void * tainted_top = (void *)0;
-
/* pool_malloc holds the amount of memory used by the store pools; this goes up
and down as store is reset or released. nonpool_malloc is the total got by
malloc from other calls; this doesn't go down because it is just freed by
#endif
-static void * store_mmap(int, const char *, int);
static void * internal_store_malloc(int, const char *, int);
-static void internal_untainted_free(void *, const char *, int linenumber);
-static void internal_tainted_free(storeblock *, const char *, int linenumber);
+static void internal_store_free(void *, const char *, int linenumber);
/******************************************************************************/
-/* 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;
}
}
+
/*************************************************
* Get a block from the current pool *
*************************************************/
{
/* Give up on this block, because it's too small */
nblocks[pool]--;
- if (pool < POOL_TAINT_BASE)
- internal_untainted_free(newblock, func, linenumber);
- else
- internal_tainted_free(newblock, func, linenumber);
+ internal_store_free(newblock, func, linenumber);
newblock = NULL;
}
if (++nblocks[pool] > maxblocks[pool])
maxblocks[pool] = nblocks[pool];
- newblock = tainted
- ? store_mmap(mlength, func, linenumber)
- : internal_store_malloc(mlength, func, linenumber);
+ newblock = internal_store_malloc(mlength, func, linenumber);
newblock->next = NULL;
newblock->length = length;
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
-#ifdef COMPILE_UTILITY
-func = func;
-linenumber = linenumber;
-#else
+#ifndef COMPILE_UTILITY
DEBUG(D_memory)
debug_printf("---%d Get %6p %5d %-14s %4d\n", pool,
store_last_get[pool], size, func, linenumber);
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
-#ifdef COMPILE_UTILITY
-func = func;
-linenumber = linenumber;
-#else
+#ifndef COMPILE_UTILITY
DEBUG(D_memory)
debug_printf("---%d Ext %6p %5d %-14s %4d\n", pool, ptr, newsize,
func, linenumber);
nbytes[pool] -= siz;
pool_malloc -= siz;
nblocks[pool]--;
- if (pool < POOL_TAINT_BASE)
- internal_untainted_free(b, func, linenumber);
- else
- internal_tainted_free(b, func, linenumber);
+ internal_store_free(b, func, linenumber);
}
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
-#ifdef COMPILE_UTILITY
-func = func;
-linenumber = linenumber;
-#else
+#ifndef COMPILE_UTILITY
DEBUG(D_memory)
debug_printf("---%d Rst %6p %5d %-14s %4d %d\n", pool, ptr,
count + oldmalloc - pool_malloc,
/* Cut out the debugging stuff for utilities, but stop picky compilers from
giving warnings. */
-#ifdef COMPILE_UTILITY
- func = func;
- linenumber = linenumber;
-#else
+#ifndef COMPILE_UTILITY
DEBUG(D_memory)
debug_printf("---%d Rel %6p %5d %-14s %4d %d\n", pool, ptr, count,
func, linenumber, pool_malloc);
/* Cut out the debugging stuff for utilities, but stop picky compilers
from giving warnings. */
-#ifdef COMPILE_UTILITY
- func = func;
- linenumber = linenumber;
-#else
+#ifndef COMPILE_UTILITY
DEBUG(D_memory)
debug_printf("-Release %6p %-20s %4d %d\n", (void *)bb, func,
linenumber, pool_malloc);
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
-/******************************************************************************/
-static void *
-store_alloc_tail(void * yield, int size, const char * func, int line,
- const uschar * type)
-{
-if ((nonpool_malloc += size) > max_nonpool_malloc)
- max_nonpool_malloc = nonpool_malloc;
-
-/* Cut out the debugging stuff for utilities, but stop picky compilers from
-giving warnings. */
-
-#ifdef COMPILE_UTILITY
-func = func; line = line; type = type;
-#else
-
-/* If running in test harness, spend time making sure all the new store
-is not filled with zeros so as to catch problems. */
-
-if (f.running_in_test_harness)
- memset(yield, 0xF0, (size_t)size);
-DEBUG(D_memory) debug_printf("--%6s %6p %5d bytes\t%-14s %4d\tpool %5d nonpool %5d\n",
- type, yield, size, func, line, pool_malloc, nonpool_malloc);
-#endif /* COMPILE_UTILITY */
-
-return yield;
-}
-
-/*************************************************
-* Mmap store *
-*************************************************/
-
-static void *
-store_mmap(int size, const char * func, int line)
-{
-void * yield, * top;
-
-if (size < 16) size = 16;
-
-if (!(yield = mmap(NULL, (size_t)size,
- PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to mmap %d bytes of memory: "
- "called from line %d of %s", size, line, func);
-
-if (yield < tainted_base) tainted_base = yield;
-if ((top = US yield + size) > tainted_top) tainted_top = top;
-
-return store_alloc_tail(yield, size, func, line, US"Mmap");
-}
-
/*************************************************
* Malloc store *
*************************************************/
Arguments:
size amount of store wanted
func function from which called
- linenumber line number in source file
+ line line number in source file
Returns: pointer to gotten store (panic on failure)
*/
static void *
-internal_store_malloc(int size, const char *func, int linenumber)
+internal_store_malloc(int size, const char *func, int line)
{
void * yield;
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);
+ "called from line %d in %s", size, line, func);
+
+if ((nonpool_malloc += size) > max_nonpool_malloc)
+ max_nonpool_malloc = nonpool_malloc;
+
+/* Cut out the debugging stuff for utilities, but stop picky compilers from
+giving warnings. */
-return store_alloc_tail(yield, size, func, linenumber, US"Malloc");
+#ifndef COMPILE_UTILITY
+/* If running in test harness, spend time making sure all the new store
+is not filled with zeros so as to catch problems. */
+
+if (f.running_in_test_harness)
+ memset(yield, 0xF0, (size_t)size);
+DEBUG(D_memory) debug_printf("--Malloc %6p %5d bytes\t%-14s %4d\tpool %5d nonpool %5d\n",
+ yield, size, func, line, pool_malloc, nonpool_malloc);
+#endif /* COMPILE_UTILITY */
+
+return yield;
}
void *
*/
static void
-internal_untainted_free(void * block, const char * func, int linenumber)
+internal_store_free(void * block, const char * func, int linenumber)
{
-#ifdef COMPILE_UTILITY
-func = func;
-linenumber = linenumber;
-#else
+#ifndef COMPILE_UTILITY
DEBUG(D_memory)
debug_printf("----Free %6p %-20s %4d\n", block, func, linenumber);
#endif /* COMPILE_UTILITY */
store_free_3(void * block, const char * func, int linenumber)
{
n_nonpool_blocks--;
-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);
+internal_store_free(block, func, linenumber);
}
/******************************************************************************/