Fix small typo
[exim.git] / src / src / store.c
index d15b9b4fc72ac18395dae2ed42b8a0469f4fce54..e8819e3e3f4d10de13b6eeb1123c713dd09ab2c6 100644 (file)
@@ -41,6 +41,9 @@ The following different types of store are recognized:
   a single message transaction but needed for longer than the use of the main
   pool permits.  Currently this means only receive-time DKIM information.
 
+- There is a dedicated pool for configuration data read from the config file(s).
+  Once complete, it is made readonly.
+
 . Orthogonal to the three pool types, there are two classes of memory: untainted
   and tainted.  The latter is used for values derived from untrusted input, and
   the string-expansion mechanism refuses to operate on such values (obviously,
@@ -154,6 +157,7 @@ static int nbytes[NPOOLS];  /* current bytes allocated */
 static int maxbytes[NPOOLS];   /* max number reached */
 static int nblocks[NPOOLS];    /* current number of blocks allocated */
 static int maxblocks[NPOOLS];
+static unsigned maxorder[NPOOLS];
 static int n_nonpool_blocks;   /* current number of direct store_malloc() blocks */
 static int max_nonpool_blocks;
 static int max_pool_malloc;    /* max value for pool_malloc */
@@ -164,21 +168,24 @@ static int max_nonpool_malloc;    /* max value for nonpool_malloc */
 static const uschar * pooluse[NPOOLS] = {
 [POOL_MAIN] =          US"main",
 [POOL_PERM] =          US"perm",
+[POOL_CONFIG] =                US"config",
 [POOL_SEARCH] =                US"search",
 [POOL_MESSAGE] =       US"message",
 [POOL_TAINT_MAIN] =    US"main",
 [POOL_TAINT_PERM] =    US"perm",
-[POOL_TAINT_SEARCH] =  US"search",
+[POOL_TAINT_CONFIG] =  US"config",
 [POOL_TAINT_SEARCH] =  US"search",
 [POOL_TAINT_MESSAGE] = US"message",
 };
 static const uschar * poolclass[NPOOLS] = {
 [POOL_MAIN] =          US"untainted",
 [POOL_PERM] =          US"untainted",
+[POOL_CONFIG] =                US"untainted",
 [POOL_SEARCH] =                US"untainted",
 [POOL_MESSAGE] =       US"untainted",
 [POOL_TAINT_MAIN] =    US"tainted",
 [POOL_TAINT_PERM] =    US"tainted",
+[POOL_TAINT_CONFIG] =  US"tainted",
 [POOL_TAINT_SEARCH] =  US"tainted",
 [POOL_TAINT_MESSAGE] = US"tainted",
 };
@@ -200,7 +207,6 @@ for (int i = 0; i < NPOOLS; i++)
   yield_length[i] = -1;
   store_block_order[i] = 12; /* log2(allocation_size) ie. 4kB */
   }
-store_block_order[POOL_MAIN] = 13;
 }
 
 /******************************************************************************/
@@ -245,6 +251,19 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n",
 
 
 
+/******************************************************************************/
+void
+store_writeprotect(int pool)
+{
+#if !defined(COMPILE_UTILITY) && !defined(MISSING_POSIX_MEMALIGN)
+for (storeblock * b = chainbase[pool]; b; b = b->next)
+  if (mprotect(b, ALIGNED_SIZEOF_STOREBLOCK + b->length, PROT_READ) != 0)
+    DEBUG(D_any) debug_printf("config block mprotect: (%d) %s\n", errno, strerror(errno));
+#endif
+}
+
+/******************************************************************************/
+
 /*************************************************
 *       Get a block from the current pool        *
 *************************************************/
@@ -268,6 +287,17 @@ store_get_3(int size, BOOL tainted, const char *func, int linenumber)
 {
 int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
 
+/* Ensure we've been asked to allocate memory.
+A negative size is a sign of a security problem.
+A zero size might be also suspect, but our internal usage deliberately
+does this to return a current watermark value for a later release of
+allocated store. */
+
+if (size < 0 || size >= INT_MAX/2)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+            "bad memory allocation requested (%d bytes) at %s %d",
+            size, func, linenumber);
+
 /* Round up the size to a multiple of the alignment. Although this looks a
 messy statement, because "alignment" is a constant expression, the compiler can
 do a reasonable job of optimizing, especially if the value of "alignment" is a
@@ -282,7 +312,9 @@ these functions are mostly called for small amounts of store. */
 
 if (size > yield_length[pool])
   {
-  int length = MAX(STORE_BLOCK_SIZE(store_block_order[pool]), size);
+  int length = MAX(
+         STORE_BLOCK_SIZE(store_block_order[pool]) - ALIGNED_SIZEOF_STOREBLOCK,
+         size);
   int mlength = length + ALIGNED_SIZEOF_STOREBLOCK;
   storeblock * newblock;
 
@@ -311,11 +343,20 @@ if (size > yield_length[pool])
     if (++nblocks[pool] > maxblocks[pool])
       maxblocks[pool] = nblocks[pool];
 
-    newblock = internal_store_malloc(mlength, func, linenumber);
+#ifndef MISSING_POSIX_MEMALIGN
+    if (pool == POOL_CONFIG)
+      {
+      long pgsize = sysconf(_SC_PAGESIZE);
+      posix_memalign((void **)&newblock, pgsize, (mlength + pgsize - 1) & ~(pgsize - 1));
+      }
+    else
+#endif
+      newblock = internal_store_malloc(mlength, func, linenumber);
     newblock->next = NULL;
     newblock->length = length;
 #ifndef RESTRICTED_MEMORY
-    store_block_order[pool]++;
+    if (store_block_order[pool]++ > maxorder[pool])
+      maxorder[pool] = store_block_order[pool];
 #endif
 
     if (!chainbase[pool])
@@ -414,6 +455,11 @@ int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
 int inc = newsize - oldsize;
 int rounded_oldsize = oldsize;
 
+if (oldsize < 0 || newsize < oldsize || newsize >= INT_MAX/2)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+            "bad memory extension requested (%d -> %d bytes) at %s %d",
+            oldsize, newsize, func, linenumber);
+
 /* Check that the block being extended was already of the required taint status;
 refuse to extend if not. */
 
@@ -464,7 +510,8 @@ not call with a pointer returned by store_get().  Both the untainted and tainted
 pools corresposding to store_pool are reset.
 
 Arguments:
-  r           place to back up to
+  ptr         place to back up to
+  pool       pool holding the pointer
   func        function from which called
   linenumber  line number in source file
 
@@ -542,17 +589,13 @@ if (  yield_length[pool] < STOREPOOL_MIN_SIZE
   }
 
 bb = b->next;
-b->next = NULL;
-
-/* If there will be only one block left in the pool, drop one
-most-recent allocation size increase, ensuring it does not increase
-forever. */
-
-if (!bb && store_block_order[pool] > 12) store_block_order[pool]--;
+if (pool != POOL_CONFIG)
+  b->next = NULL;
 
 while ((b = bb))
   {
   int siz = b->length + ALIGNED_SIZEOF_STOREBLOCK;
+
 #ifndef COMPILE_UTILITY
   if (debug_store)
     assert_no_variables(b, b->length + ALIGNED_SIZEOF_STOREBLOCK,
@@ -562,7 +605,12 @@ while ((b = bb))
   nbytes[pool] -= siz;
   pool_malloc -= siz;
   nblocks[pool]--;
-  internal_store_free(b, func, linenumber);
+  if (pool != POOL_CONFIG)
+    internal_store_free(b, func, linenumber);
+
+#ifndef RESTRICTED_MEMORY
+  if (store_block_order[pool] > 13) store_block_order[pool]--;
+#endif
   }
 
 /* Cut out the debugging stuff for utilities, but stop picky compilers from
@@ -738,7 +786,7 @@ for (storeblock * b = chainbase[pool]; b; b = b->next)
       memset(bb, 0xF0, bb->length+ALIGNED_SIZEOF_STOREBLOCK);
 #endif  /* COMPILE_UTILITY */
 
-    free(bb);
+    internal_store_free(bb, func, linenumber);
     return;
     }
   }
@@ -782,6 +830,11 @@ if (is_tainted(block) != tainted)
   die_tainted(US"store_newblock", CUS func, linenumber);
 #endif
 
+if (len < 0 || len > newsize)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+            "bad memory extension requested (%d -> %d bytes) at %s %d",
+            len, newsize, func, linenumber);
+
 newtext = store_get(newsize, tainted);
 memcpy(newtext, block, len);
 if (release_ok) store_release_3(block, pool, func, linenumber);
@@ -812,12 +865,23 @@ internal_store_malloc(int size, const char *func, int line)
 {
 void * yield;
 
+if (size < 0 || size >= INT_MAX/2)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+            "bad memory allocation requested (%d bytes) at %s %d",
+            size, func, line);
+
+size += sizeof(int);   /* space to store the size, used under debug */
 if (size < 16) size = 16;
 
 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, line, func);
 
+#ifndef COMPILE_UTILITY
+DEBUG(D_any) *(int *)yield = size;
+#endif
+yield = US yield + sizeof(int);
+
 if ((nonpool_malloc += size) > max_nonpool_malloc)
   max_nonpool_malloc = nonpool_malloc;
 
@@ -829,8 +893,8 @@ giving warnings. */
 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",
+  memset(yield, 0xF0, (size_t)size - sizeof(int));
+DEBUG(D_memory) debug_printf("--Malloc %6p %5d bytes\t%-20s %4d\tpool %5d  nonpool %5d\n",
   yield, size, func, line, pool_malloc, nonpool_malloc);
 #endif  /* COMPILE_UTILITY */
 
@@ -863,11 +927,12 @@ Returns:      nothing
 static void
 internal_store_free(void * block, const char * func, int linenumber)
 {
+uschar * p = US block - sizeof(int);
 #ifndef COMPILE_UTILITY
-DEBUG(D_memory)
-  debug_printf("----Free %6p %-20s %4d\n", block, func, linenumber);
-#endif  /* COMPILE_UTILITY */
-free(block);
+DEBUG(D_any) nonpool_malloc -= *(int *)p;
+DEBUG(D_memory) debug_printf("----Free %6p %5d bytes\t%-20s %4d\n", block, *(int *)p, func, linenumber);
+#endif
+free(p);
 }
 
 void
@@ -889,8 +954,9 @@ DEBUG(D_memory)
   (max_nonpool_malloc+1023)/1024, max_nonpool_blocks);
  debug_printf("----Exit npools  max: %3d kB\n", max_pool_malloc/1024);
  for (int i = 0; i < NPOOLS; i++)
-  debug_printf("----Exit  pool %d max: %3d kB in %d blocks\t%s %s\n",
-    i, maxbytes[i]/1024, maxblocks[i], poolclass[i], pooluse[i]);
+  debug_printf("----Exit  pool %d max: %3d kB in %d blocks at order %u\t%s %s\n",
+    i, (maxbytes[i]+1023)/1024, maxblocks[i], maxorder[i],
+    poolclass[i], pooluse[i]);
  }
 #endif
 }