first go. crashes in 0003
authorJeremy Harris <jgh146exb@wizmail.org>
Mon, 10 May 2021 21:47:01 +0000 (22:47 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 27 Jun 2021 23:30:02 +0000 (00:30 +0100)
src/src/exim.c
src/src/functions.h
src/src/readconf.c
src/src/store.c
src/src/store.h

index 6a9a1477f21e5774fe315050eab5e0373ce00ef3..eb1d83416151583f64e6ed6c01054e9ad5ce96e6 100644 (file)
@@ -3688,7 +3688,7 @@ else
   {
   struct rlimit rlp;
 
-  #ifdef RLIMIT_NOFILE
+#ifdef RLIMIT_NOFILE
   if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NOFILE) failed: %s",
@@ -3711,9 +3711,9 @@ else
           strerror(errno));
       }
     }
-  #endif
+#endif
 
-  #ifdef RLIMIT_NPROC
+#ifdef RLIMIT_NPROC
   if (getrlimit(RLIMIT_NPROC, &rlp) < 0)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NPROC) failed: %s",
@@ -3721,20 +3721,20 @@ else
     rlp.rlim_cur = rlp.rlim_max = 0;
     }
 
-  #ifdef RLIM_INFINITY
+ifdef RLIM_INFINITY
   if (rlp.rlim_cur != RLIM_INFINITY && rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY;
-  #else
+else
   if (rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = 1000;
-  #endif
+endif
     if (setrlimit(RLIMIT_NPROC, &rlp) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NPROC) failed: %s",
         strerror(errno));
     }
-  #endif
+#endif
   }
 
 /* Exim is normally entered as root (but some special configurations are
@@ -3857,6 +3857,7 @@ is equivalent to the ability to modify a setuid binary!
 This needs to happen before we read the main configuration. */
 init_lookup_list();
 
+/*XXX this excrescence could move to the testsuite standard config setup file */
 #ifdef SUPPORT_I18N
 if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL;
 #endif
@@ -3895,19 +3896,21 @@ issues (currently about tls_advertise_hosts and keep_environment not being
 defined) */
 
   {
+  int old_pool = store_pool;
 #ifdef MEASURE_TIMING
   struct timeval t0, diff;
   (void)gettimeofday(&t0, NULL);
 #endif
 
+  store_pool = POOL_CONFIG;
   readconf_main(checking || list_options);
+  store_pool = old_pool;
 
 #ifdef MEASURE_TIMING
   report_time_since(&t0, US"readconf_main (delta)");
 #endif
   }
 
-
 /* Now in directory "/" */
 
 if (cleanup_environment() == FALSE)
@@ -4494,7 +4497,13 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
   event_action gets expanded */
 
   if (msg_action == MSG_REMOVE)
+    {
+    int old_pool = store_pool;
+    store_pool = POOL_CONFIG;
     readconf_rest();
+    store_pool = old_pool;
+    store_writeprotect(POOL_CONFIG);
+    }
 
   if (!one_msg_action)
     {
@@ -4519,12 +4528,16 @@ Now, since the intro of the ${acl } expansion, ACL definitions may be
 needed in transports so we lost the optimisation. */
 
   {
+  int old_pool = store_pool;
 #ifdef MEASURE_TIMING
   struct timeval t0, diff;
   (void)gettimeofday(&t0, NULL);
 #endif
 
+  store_pool = POOL_CONFIG;
   readconf_rest();
+  store_pool = old_pool;
+  store_writeprotect(POOL_CONFIG);
 
 #ifdef MEASURE_TIMING
   report_time_since(&t0, US"readconf_rest (delta)");
index e349721706a0c2f65ab97ccd2f293227e9f1be33..38c8e40a00a4019fa5692c7e6b50959e66aad702 100644 (file)
@@ -527,6 +527,7 @@ extern int     stdin_ungetc(int);
 
 extern void    store_exit(void);
 extern void    store_init(void);
+extern void    store_writeprotect(int);
 
 extern gstring *string_append(gstring *, int, ...) WARN_UNUSED_RESULT;
 extern gstring *string_append_listele(gstring *, uschar, const uschar *) WARN_UNUSED_RESULT;
index 34ebf87690777ce927f7a0ff37bc6f5e3c77e09d..4602dd262194d09196787db7d52ca50c82d7259f 100644 (file)
@@ -3142,55 +3142,54 @@ while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
 
   /* Cut out all the fancy processing unless specifically wanted */
 
-  #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
+#if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
   uschar *suffix = filename + Ustrlen(filename);
 
   /* Try for the node-specific file if a node name exists */
 
-  #ifdef CONFIGURE_FILE_USE_NODE
+ifdef CONFIGURE_FILE_USE_NODE
   struct utsname uts;
   if (uname(&uts) >= 0)
     {
-    #ifdef CONFIGURE_FILE_USE_EUID
+#  ifdef CONFIGURE_FILE_USE_EUID
     sprintf(CS suffix, ".%ld.%.256s", (long int)original_euid, uts.nodename);
-    config_file = Ufopen(filename, "rb");
-    if (config_file == NULL)
-    #endif  /* CONFIGURE_FILE_USE_EUID */
+    if (!(config_file = Ufopen(filename, "rb")))
+#  endif  /* CONFIGURE_FILE_USE_EUID */
       {
       sprintf(CS suffix, ".%.256s", uts.nodename);
       config_file = Ufopen(filename, "rb");
       }
     }
-  #endif  /* CONFIGURE_FILE_USE_NODE */
+endif  /* CONFIGURE_FILE_USE_NODE */
 
   /* Otherwise, try the generic name, possibly with the euid added */
 
-  #ifdef CONFIGURE_FILE_USE_EUID
-  if (config_file == NULL)
+ifdef CONFIGURE_FILE_USE_EUID
+  if (!config_file)
     {
     sprintf(CS suffix, ".%ld", (long int)original_euid);
     config_file = Ufopen(filename, "rb");
     }
-  #endif  /* CONFIGURE_FILE_USE_EUID */
+endif  /* CONFIGURE_FILE_USE_EUID */
 
   /* Finally, try the unadorned name */
 
-  if (config_file == NULL)
+  if (!config_file)
     {
     *suffix = 0;
     config_file = Ufopen(filename, "rb");
     }
-  #else  /* if neither defined */
+#else  /* if neither defined */
 
   /* This is the common case when the fancy processing is not included. */
 
   config_file = Ufopen(filename, "rb");
-  #endif
+#endif
 
   /* If the file does not exist, continue to try any others. For any other
   error, break out (and die). */
 
-  if (config_file != NULL || errno != ENOENT) break;
+  if (config_file || errno != ENOENT) break;
   }
 
 /* On success, save the name for verification; config_filename is used when
@@ -3213,39 +3212,37 @@ if (config_file)
     config_main_directory = last_slash == filename ? US"/" : string_copyn(filename, last_slash - filename);
   else
     {
-      /* relative configuration file name: working dir + / + basename(filename) */
+    /* relative configuration file name: working dir + / + basename(filename) */
 
-      uschar buf[PATH_MAX];
-      gstring * g;
+    uschar buf[PATH_MAX];
+    gstring * g;
 
-      if (os_getcwd(buf, PATH_MAX) == NULL)
-        {
-        perror("exim: getcwd");
-        exit(EXIT_FAILURE);
-        }
-      g = string_cat(NULL, buf);
+    if (os_getcwd(buf, PATH_MAX) == NULL)
+      {
+      perror("exim: getcwd");
+      exit(EXIT_FAILURE);
+      }
+    g = string_cat(NULL, buf);
 
-      /* If the dir does not end with a "/", append one */
-      if (g->s[g->ptr-1] != '/')
-        g = string_catn(g, US"/", 1);
+    /* If the dir does not end with a "/", append one */
+    if (g->s[g->ptr-1] != '/')
+      g = string_catn(g, US"/", 1);
 
-      /* If the config file contains a "/", extract the directory part */
-      if (last_slash)
-        g = string_catn(g, filename, last_slash - filename);
+    /* If the config file contains a "/", extract the directory part */
+    if (last_slash)
+      g = string_catn(g, filename, last_slash - filename);
 
-      config_main_directory = string_from_gstring(g);
+    config_main_directory = string_from_gstring(g);
     }
   config_directory = config_main_directory;
   }
 else
-  {
   if (!filename)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "non-existent configuration file(s): "
       "%s", config_main_filelist);
   else
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
       string_open_failed("configuration file %s", filename));
-  }
 
 /* Now, once we found and opened our configuration file, we change the directory
 to a safe place. Later we change to $spool_directory. */
@@ -3265,19 +3262,19 @@ if (f.trusted_config && Ustrcmp(filename, US"/dev/null"))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to stat configuration file %s",
       big_buffer);
 
-  if ((statbuf.st_uid != root_uid                /* owner not root */
-       #ifdef CONFIGURE_OWNER
-       && statbuf.st_uid != config_uid           /* owner not the special one */
-       #endif
-         ) ||                                    /* or */
-      (statbuf.st_gid != root_gid                /* group not root & */
-       #ifdef CONFIGURE_GROUP
-       && statbuf.st_gid != config_gid           /* group not the special one */
-       #endif
-       && (statbuf.st_mode & 020) != 0) ||       /* group writeable  */
-                                                 /* or */
-      ((statbuf.st_mode & 2) != 0))              /* world writeable  */
-
+  if (    statbuf.st_uid != root_uid           /* owner not root */
+#ifdef CONFIGURE_OWNER
+       && statbuf.st_uid != config_uid         /* owner not the special one */
+#endif
+     ||                                                /* or */
+         statbuf.st_gid != root_gid            /* group not root & */
+#ifdef CONFIGURE_GROUP
+       && statbuf.st_gid != config_gid         /* group not the special one */
+#endif
+       && (statbuf.st_mode & 020) != 0         /* group writeable  */
+     ||                                                /* or */
+       (statbuf.st_mode & 2) != 0              /* world writeable  */
+     )
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Exim configuration file %s has the "
       "wrong owner, group, or mode", big_buffer);
 
@@ -3324,11 +3321,11 @@ while ((s = get_config_line()))
     read_named_list(&hostlist_anchor, &hostlist_count,
       MAX_NAMED_LIST, t+8, US"host list", hide);
 
-  else if (Ustrncmp(t, US"addresslist", 11) == 0)
+  else if (Ustrncmp(t, "addresslist", 11) == 0)
     read_named_list(&addresslist_anchor, &addresslist_count,
       MAX_NAMED_LIST, t+11, US"address list", hide);
 
-  else if (Ustrncmp(t, US"localpartlist", 13) == 0)
+  else if (Ustrncmp(t, "localpartlist", 13) == 0)
     read_named_list(&localpartlist_anchor, &localpartlist_count,
       MAX_NAMED_LIST, t+13, US"local part list", hide);
 
@@ -3347,7 +3344,7 @@ if (local_sender_retain && local_from_check)
 /* If the timezone string is empty, set it to NULL, implying no TZ variable
 wanted. */
 
-if (timezone_string != NULL && *timezone_string == 0) timezone_string = NULL;
+if (timezone_string && !*timezone_string) timezone_string = NULL;
 
 /* The max retry interval must not be greater than 24 hours. */
 
@@ -3492,7 +3489,7 @@ if (syslog_facility_str)
 
 /* Expand pid_file_path */
 
-if (*pid_file_path != 0)
+if (*pid_file_path)
   {
   if (!(s = expand_string(pid_file_path)))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand pid_file_path "
@@ -3502,7 +3499,7 @@ if (*pid_file_path != 0)
 
 /* Set default value of process_log_path */
 
-if (!process_log_path || *process_log_path =='\0')
+if (!process_log_path || !*process_log_path)
   process_log_path = string_sprintf("%s/exim-process.info", spool_directory);
 
 /* Compile the regex for matching a UUCP-style "From_" line in an incoming
@@ -3554,7 +3551,7 @@ if (errors_reply_to)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "error in errors_reply_to (%s): %s", errors_reply_to, errmess);
 
-  if (domain == 0)
+  if (!domain)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "errors_reply_to (%s) does not contain a domain", errors_reply_to);
   }
@@ -3562,8 +3559,7 @@ if (errors_reply_to)
 /* If smtp_accept_queue or smtp_accept_max_per_host is set, then
 smtp_accept_max must also be set. */
 
-if (smtp_accept_max == 0 &&
-    (smtp_accept_queue > 0 || smtp_accept_max_per_host != NULL))
+if (smtp_accept_max == 0 && (smtp_accept_queue > 0 || smtp_accept_max_per_host))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "smtp_accept_max must be set if smtp_accept_queue or "
     "smtp_accept_max_per_host is set");
@@ -3584,7 +3580,7 @@ if (host_number_string)
         host_number_string, expand_string_message);
   n = Ustrtol(s, &end, 0);
   while (isspace(*end)) end++;
-  if (*end != 0)
+  if (*end)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "localhost_number value is not a number: %s", s);
   if (n > LOCALHOST_MAX)
index 2a32e9b5c40caa50cf7743ce8328ab29d918ff84..ad4da3c2e5faf4a794f33c7b6d2972f7be2b2745 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,
@@ -165,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",
 };
@@ -245,6 +251,22 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n",
 
 
 
+/******************************************************************************/
+void
+store_writeprotect(int pool)
+{
+for (storeblock * b = chainbase[pool]; b; b = b->next)
+  {
+#ifndef COMPILE_UTILITY
+  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        *
 *************************************************/
@@ -324,7 +346,13 @@ if (size > yield_length[pool])
     if (++nblocks[pool] > maxblocks[pool])
       maxblocks[pool] = nblocks[pool];
 
-    newblock = internal_store_malloc(mlength, func, linenumber);
+    if (pool == POOL_CONFIG)
+      {
+      long pgsize = sysconf(_SC_PAGESIZE);
+      posix_memalign((void **)&newblock, pgsize, (mlength + pgsize - 1) & ~(pgsize - 1));
+      }
+    else
+      newblock = internal_store_malloc(mlength, func, linenumber);
     newblock->next = NULL;
     newblock->length = length;
 #ifndef RESTRICTED_MEMORY
@@ -483,7 +511,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
 
@@ -561,7 +590,8 @@ if (  yield_length[pool] < STOREPOOL_MIN_SIZE
   }
 
 bb = b->next;
-b->next = NULL;
+if (pool != POOL_CONFIG)
+  b->next = NULL;
 
 while ((b = bb))
   {
@@ -576,7 +606,8 @@ 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]--;
index 92deabf9bbe0e228a4a220f85042ac4ebd9c641c..58561ac2832e6e87344489b55a139cc5b0463eb3 100644 (file)
 
 /* Define symbols for identifying the store pools. */
 
-#define NPOOLS 8
+#define NPOOLS 10
 enum { POOL_MAIN,
        POOL_PERM,
+       POOL_CONFIG,
        POOL_SEARCH,
        POOL_MESSAGE,
 
@@ -23,6 +24,7 @@ enum { POOL_MAIN,
 
        POOL_TAINT_MAIN = POOL_TAINT_BASE,
        POOL_TAINT_PERM,
+       POOL_TAINT_CONFIG,
        POOL_TAINT_SEARCH,
        POOL_TAINT_MESSAGE };