X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/6e05345b427859b32248c75c6f179283f6cf0382..1ddb1855402d48ad735e46abaf0d662e45600ecd:/src/src/exim.c diff --git a/src/src/exim.c b/src/src/exim.c index 133761de9..9da921a5c 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -262,12 +262,35 @@ exit(1); ***********************************************/ 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); +# if defined(SEGV_MAPERR) && defined(SEGV_ACCERR) && defined(SEGV_BNDERR) && defined(SEGV_PKUERR) +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; + } +# endif +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 /************************************************* @@ -931,55 +954,66 @@ else * Show supported features * *************************************************/ -static void -show_db_version(FILE * f) +void +show_string(BOOL is_stdout, gstring * g) +{ +const uschar * s = string_from_gstring(g); +if (is_stdout) fputs(CCS s, stdout); +else debug_printf("%s", s); +} + + +static gstring * +show_db_version(gstring * g) { #ifdef DB_VERSION_STRING DEBUG(D_any) { - fprintf(f, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); - fprintf(f, " Runtime: %s\n", + g = string_fmt_append(g, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); + g = string_fmt_append(g, " Runtime: %s\n", db_version(NULL, NULL, NULL)); } else - fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING); + g = string_fmt_append(g, "Berkeley DB: %s\n", DB_VERSION_STRING); #elif defined(BTREEVERSION) && defined(HASHVERSION) - #ifdef USE_DB - fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n"); - #else - fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n"); - #endif +# ifdef USE_DB + g = string_cat(g, US"Probably Berkeley DB version 1.8x (native mode)\n"); +# else + g = string_cat(g, US"Probably Berkeley DB version 1.8x (compatibility mode)\n"); +# endif #elif defined(_DBM_RDONLY) || defined(dbm_dirfno) -fprintf(f, "Probably ndbm\n"); +g = string_cat(g, US"Probably ndbm\n"); #elif defined(USE_TDB) -fprintf(f, "Using tdb\n"); +g = string_cat(g, US"Using tdb\n"); #else - #ifdef USE_GDBM - fprintf(f, "Probably GDBM (native mode)\n"); - #else - fprintf(f, "Probably GDBM (compatibility mode)\n"); - #endif +# ifdef USE_GDBM + g = string_cat(g, US"Probably GDBM (native mode)\n"); +# else + g = string_cat(g, US"Probably GDBM (compatibility mode)\n"); +# endif #endif +return g; } /* This function is called for -bV/--version and for -d to output the optional features of the current Exim binary. -Arguments: a FILE for printing +Arguments: BOOL, true for stdout else debug channel Returns: nothing */ static void -show_whats_supported(FILE * fp) +show_whats_supported(BOOL is_stdout) { rmark reset_point = store_mark(); -gstring * g; -DEBUG(D_any) {} else show_db_version(fp); +gstring * g = NULL; + +DEBUG(D_any) {} else g = show_db_version(g); -g = string_cat(NULL, US"Support for:"); +g = string_cat(g, US"Support for:"); #ifdef SUPPORT_CRYPTEQ g = string_cat(g, US" crypteq"); #endif @@ -1158,6 +1192,7 @@ g = transport_show_supported(g); #ifdef WITH_CONTENT_SCAN g = malware_show_supported(g); #endif +show_string(is_stdout, g); g = NULL; if (fixed_never_users[0] > 0) { @@ -1169,19 +1204,19 @@ if (fixed_never_users[0] > 0) } g = string_fmt_append(g, "Configure owner: %d:%d\n", config_uid, config_gid); -fputs(CS string_from_gstring(g), fp); -fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); +g = string_fmt_append(g, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); /* Everything else is details which are only worth reporting when debugging. Perhaps the tls_version_report should move into this too. */ -DEBUG(D_any) do { +DEBUG(D_any) + { /* clang defines __GNUC__ (at least, for me) so test for it first */ #if defined(__clang__) - fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__); + g = string_fmt_append(g, "Compiler: CLang [%s]\n", __clang_version__); #elif defined(__GNUC__) - fprintf(fp, "Compiler: GCC [%s]\n", + g = string_fmt_append(g, "Compiler: GCC [%s]\n", # ifdef __VERSION__ __VERSION__ # else @@ -1189,35 +1224,38 @@ DEBUG(D_any) do { # endif ); #else - fprintf(fp, "Compiler: \n"); + g = string_cat(g, US"Compiler: \n"); #endif #if defined(__GLIBC__) && !defined(__UCLIBC__) - fprintf(fp, "Library version: Glibc: Compile: %d.%d\n", + g = string_fmt_append(g, "Library version: Glibc: Compile: %d.%d\n", __GLIBC__, __GLIBC_MINOR__); if (__GLIBC_PREREQ(2, 1)) - fprintf(fp, " Runtime: %s\n", + g = string_fmt_append(g, " Runtime: %s\n", gnu_get_libc_version()); #endif -show_db_version(fp); +g = show_db_version(g); #ifndef DISABLE_TLS - tls_version_report(fp); + g = tls_version_report(g); #endif #ifdef SUPPORT_I18N - utf8_version_report(fp); + g = utf8_version_report(g); #endif #ifdef SUPPORT_DMARC - dmarc_version_report(fp); + g = dmarc_version_report(g); #endif #ifdef SUPPORT_SPF - spf_lib_version_report(fp); + g = spf_lib_version_report(g); #endif - for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi) - if (authi->version_report) - (*authi->version_report)(fp); +show_string(is_stdout, g); +g = NULL; + +for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi) + if (authi->version_report) + g = (*authi->version_report)(g); /* PCRE_PRERELEASE is either defined and empty or a bare sequence of characters; unless it's an ancient version of PCRE in which case it @@ -1230,7 +1268,7 @@ show_db_version(fp); { uschar buf[24]; pcre2_config(PCRE2_CONFIG_VERSION, buf); - fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n" + g = string_fmt_append(g, "Library version: PCRE2: Compile: %d.%d%s\n" " Runtime: %s\n", PCRE2_MAJOR, PCRE2_MINOR, EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "", @@ -1239,23 +1277,29 @@ show_db_version(fp); #undef QUOTE #undef EXPAND_AND_QUOTE - init_lookup_list(); - for (int i = 0; i < lookup_list_count; i++) - if (lookup_list[i]->version_report) - lookup_list[i]->version_report(fp); +show_string(is_stdout, g); +g = NULL; + +init_lookup_list(); +for (int i = 0; i < lookup_list_count; i++) + if (lookup_list[i]->version_report) + g = lookup_list[i]->version_report(g); +show_string(is_stdout, g); +g = NULL; #ifdef WHITELIST_D_MACROS - fprintf(fp, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); + g = string_fmt_append(g, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); #else - fprintf(fp, "WHITELIST_D_MACROS unset\n"); + g = string_cat(g, US"WHITELIST_D_MACROS unset\n"); #endif #ifdef TRUSTED_CONFIG_LIST - fprintf(fp, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); + g = string_fmt_append(g, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); #else - fprintf(fp, "TRUSTED_CONFIG_LIST unset\n"); + g = string_cat(g, US"TRUSTED_CONFIG_LIST unset\n"); #endif + } -} while (0); +show_string(is_stdout, g); store_reset(reset_point); } @@ -1407,56 +1451,58 @@ static uschar * 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; } @@ -1665,7 +1711,7 @@ int i, rv; int list_queue_option = 0; int msg_action = 0; int msg_action_arg = -1; -int namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]); +int namelen = argv[0] ? Ustrlen(argv[0]) : 0; int queue_only_reason = 0; #ifdef EXIM_PERL int perl_start_option = 0; @@ -1809,6 +1855,7 @@ if (f.running_in_test_harness) debug_store = TRUE; /* Protect against abusive argv[0] */ +if (!argv[0] || !argc) exim_fail("exim: executable name required\n"); exim_str_fail_toolong(argv[0], PATH_MAX, "argv[0]"); /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed @@ -1856,7 +1903,14 @@ descriptive text. */ 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 */ @@ -2408,7 +2462,7 @@ on the second character (the one after '-'), to save some effort. */ version_cnumber, version_date); printf("%s\n", CS version_copyright); version_printed = TRUE; - show_whats_supported(stdout); + show_whats_supported(TRUE); f.log_testing_mode = TRUE; } else badarg = TRUE; @@ -3721,7 +3775,7 @@ if (debug_selector != 0) version_string, (long int)real_uid, (long int)real_gid, (int)getpid(), debug_selector); if (!version_printed) - show_whats_supported(stderr); + show_whats_supported(FALSE); } } @@ -5735,13 +5789,8 @@ for (BOOL more = TRUE; more; ) 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