*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
for store allocation via Exim's store manager. The normal calls are actually
macros that pass over location information to make tracing easier. These
functions just interface to the standard macro calls. A good compiler will
-optimize out the tail recursion and so not make them too expensive. There
-are two sets of functions; one for use when we want to retain the compiled
-regular expression for a long time; the other for short-term use. */
-
-static void *
-function_store_get(PCRE2_SIZE size, void * tag)
-{
-/* For now, regard all RE results as potentially tainted. We might need
-more intelligence on this point. */
-return store_get((int)size, TRUE);
-}
-
-static void
-function_dummy_free(void * block, void * tag) {}
+optimize out the tail recursion and so not make them too expensive. */
static void *
function_store_malloc(PCRE2_SIZE size, void * tag)
static void
function_store_free(void * block, void * tag)
{
-store_free(block);
+/* At least some version of pcre2 pass a null pointer */
+if (block) store_free(block);
}
uschar errbuf[128];
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
- "%s at offset %d while compiling %s", errbuf, (long)offset, pattern);
+ "%s at offset %ld while compiling %s", errbuf, (long)offset, pattern);
}
if (use_malloc)
***********************************************/
static void
+#ifdef SA_SIGINFO
+segv_handler(int sig, siginfo_t * info, void * uctx)
+{
+log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (fault address: %p)", info->si_addr);
+switch (info->si_code)
+ {
+ case SEGV_MAPERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_MAPERR"); break;
+ case SEGV_ACCERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_ACCERR"); break;
+ case SEGV_BNDERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_BNDERR"); break;
+ case SEGV_PKUERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_PKUERR"); break;
+ }
+if (US info->si_addr < US 4096)
+ log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (null pointer indirection)");
+else
+ log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
+signal(SIGSEGV, SIG_DFL);
+kill(getpid(), sig);
+}
+
+#else
segv_handler(int sig)
{
log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
signal(SIGSEGV, SIG_DFL);
kill(getpid(), sig);
}
+#endif
/*************************************************
g = string_cat(g, US" OCSP");
#endif
#ifndef DISABLE_PIPE_CONNECT
- g = string_cat(g, US" PIPE_CONNECT");
+ g = string_cat(g, US" PIPECONNECT");
#endif
#ifndef DISABLE_PRDR
g = string_cat(g, US" PRDR");
g = string_cat(g, US" PROXY");
#endif
#ifndef DISABLE_QUEUE_RAMP
- g = string_cat(g, US" Experimental_Queue_Ramp");
+ g = string_cat(g, US" Queue_Ramp");
#endif
#ifdef SUPPORT_SOCKS
g = string_cat(g, US" SOCKS");
get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *))
{
gstring * g = NULL;
+BOOL had_input = FALSE;
if (!fn_readline) { printf("> "); fflush(stdout); }
for (int i = 0;; i++)
{
uschar buffer[1024];
- uschar *p, *ss;
+ uschar * p, * ss;
- #ifdef USE_READLINE
+#ifdef USE_READLINE
char *readline_line = NULL;
if (fn_readline)
{
if (!(readline_line = fn_readline((i > 0)? "":"> "))) break;
- if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line);
+ if (*readline_line && fn_addhist) fn_addhist(readline_line);
p = US readline_line;
}
else
- #endif
+#endif
/* readline() not in use */
{
- if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break;
+ if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break; /*EOF*/
p = buffer;
}
/* Handle the line */
- ss = p + (int)Ustrlen(p);
- while (ss > p && isspace(ss[-1])) ss--;
+ had_input = TRUE;
+ ss = p + Ustrlen(p);
+ while (ss > p && isspace(ss[-1])) ss--; /* strip trailing newline (and spaces) */
if (i > 0)
- while (p < ss && isspace(*p)) p++; /* leading space after cont */
+ while (p < ss && isspace(*p)) p++; /* strip leading space after cont */
g = string_catn(g, p, ss - p);
- #ifdef USE_READLINE
+#ifdef USE_READLINE
if (fn_readline) free(readline_line);
- #endif
+#endif
/* g can only be NULL if ss==p */
- if (ss == p || g->s[g->ptr-1] != '\\')
+ if (ss == p || g->s[g->ptr-1] != '\\') /* not continuation; done */
break;
- --g->ptr;
- (void) string_from_gstring(g);
+ --g->ptr; /* drop the \ */
}
-if (!g) printf("\n");
-return string_from_gstring(g);
+if (had_input) return g ? string_from_gstring(g) : US"";
+printf("\n");
+return NULL;
}
process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */
set_process_info("initializing");
os_restarting_signal(SIGUSR1, usr1_handler); /* exiwhat */
+#ifdef SA_SIGINFO
+ {
+ struct sigaction act = { .sa_sigaction = segv_handler, .sa_flags = SA_RESETHAND | SA_SIGINFO };
+ sigaction(SIGSEGV, &act, NULL);
+ }
+#else
signal(SIGSEGV, segv_handler); /* log faults */
+#endif
/* If running in a dockerized environment, the TERM signal is only
delegated to the PID 1 if we request it by setting an signal handler */
store_pool = POOL_CONFIG;
readconf_rest();
store_pool = old_pool;
- store_writeprotect(POOL_CONFIG);
+
+ /* -be can add macro definitions, needing to link to the macro structure
+ chain. Otherwise, make the memory used for config data readonly. */
+
+ if (!expansion_test)
+ store_writeprotect(POOL_CONFIG);
#ifdef MEASURE_TIMING
report_time_since(&t0, US"readconf_rest (delta)");
{
if (!f.is_inetd) set_process_info("accepting a local %sSMTP message from <%s>",
smtp_batched_input? "batched " : "",
- (sender_address!= NULL)? sender_address : originator_login);
+ sender_address ? sender_address : originator_login);
}
else
{
}
}
-/* Otherwise, set up the input size limit here. */
+/* Otherwise, set up the input size limit here and set no stdin stdio buffer
+(we handle buferring so as to have visibility of fill level). */
else
{
else
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "invalid value for "
"message_size_limit: %s", expand_string_message);
+
+ setvbuf(stdin, NULL, _IONBF, 0);
}
/* Loop for several messages when reading SMTP input. If we fork any child
the file copy. */
if (!receive_timeout)
- {
- struct timeval t = { .tv_sec = 30*60, .tv_usec = 0 }; /* 30 minutes */
- fd_set r;
-
- FD_ZERO(&r); FD_SET(0, &r);
- if (select(1, &r, NULL, NULL, &t) == 0) mainlog_close();
- }
+ if (poll_one_fd(0, POLLIN, 30*60*1000) == 0) /* 30 minutes */
+ mainlog_close();
/* Read the data for the message. If filter_test is not FTEST_NONE, this
will just read the headers for the message, and not write anything onto the