Appendfile: release regex-match store every thousand files. Bug 3047
[exim.git] / src / src / exim.c
index 118fe5c7cede206c07ace57a57fe715e70b03ebb..e5b72f11ae8442603507279b8681b86e8e5d03d2 100644 (file)
@@ -49,6 +49,8 @@ optimize out the tail recursion and so not make them too expensive. */
 static void *
 function_store_malloc(PCRE2_SIZE size, void * tag)
 {
+if (size > INT_MAX)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "excessive memory alloc request");
 return store_malloc((int)size);
 }
 
@@ -63,12 +65,15 @@ if (block) store_free(block);
 static void *
 function_store_get(PCRE2_SIZE size, void * tag)
 {
+if (size > INT_MAX)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "excessive memory alloc request");
 return store_get((int)size, GET_UNTAINTED);    /* loses track of taint */
 }
 
 static void
 function_store_nullfree(void * block, void * tag)
 {
+/* We cannot free memory allocated using store_get() */
 }
 
 
@@ -847,7 +852,7 @@ exit(EXIT_FAILURE);
 
 /* fail if a length is too long */
 static inline void
-exim_len_fail_toolong(int itemlen, int maxlen, const char *description)
+exim_len_fail_toolong(int itemlen, int maxlen, const char * description)
 {
 if (itemlen <= maxlen)
   return;
@@ -858,8 +863,10 @@ exit(EXIT_FAILURE);
 
 /* only pass through the string item back to the caller if it's short enough */
 static inline const uschar *
-exim_str_fail_toolong(const uschar *item, int maxlen, const char *description)
+exim_str_fail_toolong(const uschar * item, int maxlen, const char * description)
 {
+if (!item)
+  exim_fail("exim: bad item for: %s\n", description);
 exim_len_fail_toolong(Ustrlen(item), maxlen, description);
 return item;
 }
@@ -884,10 +891,10 @@ log_write(0, LOG_MAIN|LOG_PANIC,
 struct stat buf;
 
 if (0 == (fd < 0 ? stat(name, &buf) : fstat(fd, &buf)))
-{
+  {
   if (buf.st_uid == owner && buf.st_gid == group) return 0;
   log_write(0, LOG_MAIN|LOG_PANIC, "Wrong ownership on %s", name);
-}
+  }
 else log_write(0, LOG_MAIN|LOG_PANIC, "Stat failed on %s: %s", name, strerror(errno));
 
 #endif
@@ -896,6 +903,18 @@ return -1;
 }
 
 
+/* Bump the index for argv, checking for overflow,
+and return the argument. */
+
+static const uschar *
+next_argv(const uschar ** argv, int * pi, int argc, const uschar * where)
+{
+int i = *pi;
+if (++i >= argc) exim_fail("exim: bad item for: %s\n", where);
+return argv[*pi = i];
+}
+
+
 /*************************************************
 *         Extract port from host address         *
 *************************************************/
@@ -1365,17 +1384,15 @@ Argument:    the local part
 Returns:     the local part, quoted if necessary
 */
 
-uschar *
-local_part_quote(uschar *lpart)
+const uschar *
+local_part_quote(const uschar * lpart)
 {
 BOOL needs_quote = FALSE;
 gstring * g;
 
-for (uschar * t = lpart; !needs_quote && *t != 0; t++)
-  {
+for (const uschar * t = lpart; !needs_quote && *t; t++)
   needs_quote = !isalnum(*t) && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL &&
     (*t != '.' || t == lpart || t[1] == 0);
-  }
 
 if (!needs_quote) return lpart;
 
@@ -1383,8 +1400,8 @@ g = string_catn(NULL, US"\"", 1);
 
 for (;;)
   {
-  uschar *nq = US Ustrpbrk(lpart, "\\\"");
-  if (nq == NULL)
+  uschar * nq = US Ustrpbrk(lpart, "\\\"");
+  if (!nq)
     {
     g = string_cat(g, lpart);
     break;
@@ -1537,7 +1554,7 @@ Returns:        DOES NOT RETURN
 */
 
 static void
-exim_usage(uschar *progname)
+exim_usage(const uschar * progname)
 {
 
 /* Handle specific program invocation variants */
@@ -1744,9 +1761,9 @@ Returns:    EXIT_SUCCESS if terminated successfully
 */
 
 int
-main(int argc, char **cargv)
+main(int argc, char ** cargv)
 {
-uschar **argv = USS cargv;
+const uschar ** argv = CUSS cargv;
 int  arg_receive_timeout = -1;
 int  arg_smtp_receive_timeout = -1;
 int  arg_error_handling = error_handling;
@@ -1795,20 +1812,20 @@ BOOL verify_address_mode = FALSE;
 BOOL verify_as_sender = FALSE;
 BOOL rcpt_verify_quota = FALSE;
 BOOL version_printed = FALSE;
-uschar *alias_arg = NULL;
-uschar *called_as = US"";
-uschar *cmdline_syslog_name = NULL;
-uschar *start_queue_run_id = NULL;
-uschar *stop_queue_run_id = NULL;
-uschar *expansion_test_message = NULL;
-const uschar *ftest_domain = NULL;
-const uschar *ftest_localpart = NULL;
-const uschar *ftest_prefix = NULL;
-const uschar *ftest_suffix = NULL;
-uschar *log_oneline = NULL;
-uschar *malware_test_file = NULL;
-uschar *real_sender_address;
-uschar *originator_home = US"/";
+const uschar * alias_arg = NULL;
+const uschar * called_as = US"";
+const uschar * cmdline_syslog_name = NULL;
+const uschar * start_queue_run_id = NULL;
+const uschar * stop_queue_run_id = NULL;
+const uschar * expansion_test_message = NULL;
+const uschar * ftest_domain = NULL;
+const uschar * ftest_localpart = NULL;
+const uschar * ftest_prefix = NULL;
+const uschar * ftest_suffix = NULL;
+uschar * log_oneline = NULL;
+const uschar * malware_test_file = NULL;
+const uschar * real_sender_address;
+uschar * originator_home = US"/";
 size_t sz;
 
 struct passwd *pw;
@@ -2152,8 +2169,8 @@ on the second character (the one after '-'), to save some effort. */
  for (i = 1; i < argc; i++)
   {
   BOOL badarg = FALSE;
-  uschar * arg = argv[i];
-  uschar * argrest;
+  const uschar * arg = argv[i];
+  const uschar * argrest;
   uschar switchchar;
 
   /* An argument not starting with '-' is the start of a recipients list;
@@ -2347,7 +2364,7 @@ on the second character (the one after '-'), to save some effort. */
        case 'I':
          if (Ustrlen(argrest) >= 1 && *argrest == ':')
            {
-           uschar *p = argrest+1;
+           const uschar * p = argrest+1;
            info_flag = CMDINFO_HELP;
            if (Ustrlen(p))
              if (strcmpic(p, CUS"sieve") == 0)
@@ -2674,9 +2691,9 @@ on the second character (the one after '-'), to save some effort. */
 #else
       {
       int ptr = 0;
-      macro_item *m;
+      macro_item * m;
       uschar name[24];
-      uschar *s = argrest;
+      const uschar * s = argrest;
 
       opt_D_used = TRUE;
       while (isspace(*s)) s++;
@@ -2830,10 +2847,14 @@ on the second character (the one after '-'), to save some effort. */
         if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; }
       (void) exim_str_fail_toolong(argrest, EXIM_DISPLAYMAIL_MAX, "-f");
       if (!*argrest)
-        *(sender_address = store_get(1, GET_UNTAINTED)) = '\0';  /* Ensure writeable memory */
+       {
+        uschar * s = store_get(1, GET_UNTAINTED);      /* Ensure writeable memory */
+        *s = '\0';
+        sender_address = s;
+       }
       else
         {
-        uschar * temp = argrest + Ustrlen(argrest) - 1;
+        const uschar * temp = argrest + Ustrlen(argrest) - 1;
         while (temp >= argrest && isspace(*temp)) temp--;
         if (temp >= argrest && *temp == '.') f_end_dot = TRUE;
         allow_domain_literals = TRUE;
@@ -3151,7 +3172,8 @@ on the second character (the one after '-'), to save some effort. */
       {
       msg_action = MSG_SETQUEUE;
       queue_name_dest = string_copy_taint(
-       exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"),
+       exim_str_fail_toolong(next_argv(argv, &i, argc, arg),
+                             EXIM_DRIVERNAME_MAX, "-MG"),
        GET_TAINTED);
       }
     else if (Ustrcmp(argrest, "mad") == 0) msg_action = MSG_MARK_ALL_DELIVERED;
@@ -3272,7 +3294,7 @@ on the second character (the one after '-'), to save some effort. */
       /* -oB: Set a connection message max value for remote deliveries */
       case 'B':
        {
-       uschar * p = argrest;
+       const uschar * p = argrest;
        if (!*p)
          if (i+1 < argc && isdigit((argv[i+1][0])))
            p = argv[++i];
@@ -3363,36 +3385,36 @@ on the second character (the one after '-'), to save some effort. */
 
        if (Ustrcmp(argrest, "a") == 0)
          sender_host_address = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"),
-           GET_TAINTED);
+           exim_str_fail_toolong(next_argv(argv, &i, argc, arg),
+                                 EXIM_IPADDR_MAX, "-oMa"), GET_TAINTED);
 
        /* -oMaa: Set authenticator name */
 
        else if (Ustrcmp(argrest, "aa") == 0)
          sender_host_authenticated = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"),
-           GET_TAINTED);
+           exim_str_fail_toolong(next_argv(argv, &i, argc, arg),
+                                 EXIM_DRIVERNAME_MAX, "-oMaa"), GET_TAINTED);
 
        /* -oMas: setting authenticated sender */
 
        else if (Ustrcmp(argrest, "as") == 0)
          authenticated_sender = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"),
-           GET_TAINTED);
+           exim_str_fail_toolong(next_argv(argv, &i, argc, arg),
+                                 EXIM_EMAILADDR_MAX, "-oMas"), GET_TAINTED);
 
        /* -oMai: setting authenticated id */
 
        else if (Ustrcmp(argrest, "ai") == 0)
          authenticated_id = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMai"),
-           GET_TAINTED);
+           exim_str_fail_toolong(next_argv(argv, &i, argc, arg),
+                                 EXIM_EMAILADDR_MAX, "-oMai"), GET_TAINTED);
 
        /* -oMi: Set incoming interface address */
 
        else if (Ustrcmp(argrest, "i") == 0)
          interface_address = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"),
-           GET_TAINTED);
+           exim_str_fail_toolong(next_argv(argv, &i, argc, arg),
+                                 EXIM_IPADDR_MAX, "-oMi"), GET_TAINTED);
 
        /* -oMm: Message reference */
 
@@ -3402,7 +3424,7 @@ on the second character (the one after '-'), to save some effort. */
              exim_fail("-oMm must be a valid message ID\n");
          if (!f.trusted_config)
              exim_fail("-oMm must be called by a trusted user/config\n");
-           message_reference = argv[++i];
+           message_reference = next_argv(argv, &i, argc, arg);
          }
 
        /* -oMr: Received protocol */
@@ -3412,26 +3434,32 @@ on the second character (the one after '-'), to save some effort. */
          if (received_protocol)
            exim_fail("received_protocol is set already\n");
          else
-           received_protocol = string_copy_taint(
-             exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"),
-             GET_TAINTED);
+           if (++i >= argc) badarg = TRUE;
+           else
+             received_protocol = string_copy_taint(
+               exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-oMr"),
+               GET_TAINTED);
 
        /* -oMs: Set sender host name */
 
        else if (Ustrcmp(argrest, "s") == 0)
-         sender_host_name = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"),
-           GET_TAINTED);
+         if (++i >= argc) badarg = TRUE;
+         else
+           sender_host_name = string_copy_taint(
+             exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-oMs"),
+             GET_TAINTED);
 
        /* -oMt: Set sender ident */
 
        else if (Ustrcmp(argrest, "t") == 0)
-         {
-         sender_ident_set = TRUE;
-         sender_ident = string_copy_taint(
-           exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"),
-           GET_TAINTED);
-         }
+         if (++i >= argc) badarg = TRUE;
+         else
+           {
+           sender_ident_set = TRUE;
+           sender_ident = string_copy_taint(
+             exim_str_fail_toolong(argv[i], EXIM_IDENTUSER_MAX, "-oMt"),
+             GET_TAINTED);
+           }
 
        /* Else a bad argument */
 
@@ -3459,7 +3487,9 @@ on the second character (the one after '-'), to save some effort. */
          exim_fail("exim: only uid=%d or uid=%d can use -oP and -oPX "
                     "(uid=%d euid=%d | %d)\n",
                     root_uid, exim_uid, getuid(), geteuid(), real_uid);
-       if (!*argrest) override_pid_file_path = argv[++i];
+       if (!*argrest)
+         if (++i < argc) override_pid_file_path = argv[i];
+         else badarg = TRUE;
        else if (Ustrcmp(argrest, "X") == 0) delete_pid_file();
        else badarg = TRUE;
        break;
@@ -3487,10 +3517,9 @@ on the second character (the one after '-'), to save some effort. */
       /* Limits: Is there a real limit we want here?  1024 is very arbitrary. */
 
       case 'X':
-       if (*argrest) badarg = TRUE;
+       if (*argrest || ++i >= argc) badarg = TRUE;
        else override_local_interfaces = string_copy_taint(
-         exim_str_fail_toolong(argv[++i], 1024, "-oX"),
-         GET_TAINTED);
+         exim_str_fail_toolong(argv[i], 1024, "-oX"), GET_TAINTED);
        break;
 
       /* -oY: Override creation of daemon notifier socket */
@@ -3528,7 +3557,7 @@ on the second character (the one after '-'), to save some effort. */
     which sets the host protocol and host name */
 
     if (!*argrest)
-      if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; }
+      argrest = next_argv(argv, &i, argc, arg);
 
     if (*argrest)
       {
@@ -3614,8 +3643,14 @@ on the second character (the one after '-'), to save some effort. */
 
        else
          {
-         int intvl = readconf_readtime(*argrest ? argrest : argv[++i], 0, FALSE);
-         if (intvl <= 0)
+         int intvl;
+         const uschar * s;
+
+         if (*argrest) s = argrest;
+         else if (++i < argc) { badarg = TRUE; break; }
+         else s = argv[i];
+
+         if ((intvl = readconf_readtime(s, 0, FALSE)) <= 0)
            exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
 
          for (qrunner * qq = qrunners; qq; qq = qq->next)
@@ -3658,7 +3693,7 @@ on the second character (the one after '-'), to save some effort. */
     in all cases provided there are no further characters in this
     argument. */
 
-      alloc_onetime_qrunner();
+      if (!qrunners) alloc_onetime_qrunner();
       qrunners->queue_2stage = f.queue_2stage;
       if (*argrest)
        for (int i = 0; i < nelem(rsopts); i++)
@@ -3706,8 +3741,8 @@ on the second character (the one after '-'), to save some effort. */
     tested. Otherwise variability of clock ticks etc. cause problems. */
 
     case 'T':
-    if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0)
-      fudged_queue_times = string_copy_taint(argv[++i], GET_TAINTED);
+    if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0 && ++i < argc)
+      fudged_queue_times = string_copy_taint(argv[i], GET_TAINTED);
     else badarg = TRUE;
     break;
 
@@ -4432,7 +4467,7 @@ if (bi_option)
   if (bi_command && *bi_command)
     {
     int i = 0;
-    uschar *argv[3];
+    const uschar * argv[3];
     argv[i++] = bi_command;    /* nonexpanded option so assume untainted */
     if (alias_arg) argv[i++] = alias_arg;
     argv[i++] = NULL;
@@ -4761,7 +4796,7 @@ if (rcpt_verify_quota)
     exim_fail("exim: missing recipient for quota check\n");
   else
     {
-    verify_quota(argv[recipients_arg]);
+    verify_quota(US argv[recipients_arg]);     /*XXX we lose track of const here */
     exim_exit(EXIT_SUCCESS);
     }
 
@@ -5768,7 +5803,7 @@ for (BOOL more = TRUE; more; )
     {
     int rcount = 0;
     int count = argc - recipients_arg;
-    uschar **list = argv + recipients_arg;
+    const uschar ** list = argv + recipients_arg;
 
     /* These options cannot be changed dynamically for non-SMTP messages */