From 4e9ed49f8f12eb331b29bd5b6dc3693c520fddc2 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 31 Aug 2022 15:37:40 +0100 Subject: [PATCH] Fix $regex use-after-free. Bug 2915 --- doc/ChangeLog | 8 +++++++- src/exim.c | 4 +--- src/expand.c | 2 +- src/functions.h | 1 + src/globals.c | 2 +- src/regex.c | 29 ++++++++++++++++++----------- src/smtp_in.c | 2 ++ test/confs/4002 | 10 ++++++++++ test/mail/4002.userx | 7 +++++++ test/scripts/4000-scanning/4002 | 7 +++++++ 10 files changed, 55 insertions(+), 17 deletions(-) --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -4,15 +4,21 @@ JH/04 Bug 2903: avoid exit on an attempt to rewrite a malformed address. Make the rewrite never match and keep the logging. Trust the admin to be using verify=header-syntax (to actually reject the message). +JH/08 Bug 2915: Fix use-after-free for $regex variables. Previously when + more than one message arrived in a single connection a reference from + the earlier message could be re-used. Often a sigsegv resulted. + These variables were introduced in Exim 4.87. + Debug help from Graeme Fowler. + Exim version 4.96 ----------------- -JH/01 Move the wait-for-next-tick (needed for unique messmage IDs) from +JH/01 Move the wait-for-next-tick (needed for unique message IDs) from after reception to before a subsequent reception. This should mean slightly faster delivery, and also confirmation of reception to senders. JH/02 Move from using the pcre library to pcre2. The former is no longer --- a/src/exim.c +++ b/src/exim.c @@ -1999,12 +1999,10 @@ regex_whitelisted_macro = regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); #endif -for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; - /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic links called "mailq" in standard OS configurations. */ if ((namelen == 5 && Ustrcmp(argv[0], "mailq") == 0) || @@ -6082,11 +6080,11 @@ callout_address = NULL; sending_ip_address = NULL; deliver_localpart_data = deliver_domain_data = recipient_data = sender_data = NULL; acl_var_m = NULL; - for(int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; + regex_vars_clear(); store_reset(reset_point); } exim_exit(EXIT_SUCCESS); /* Never returns */ --- a/src/expand.c +++ b/src/expand.c @@ -1871,11 +1871,11 @@ { tree_node * node = tree_search(router_var, name + 2); return node ? node->data.ptr : strict_acl_vars ? NULL : US""; } -/* Handle $auth variables. */ +/* Handle $auth, $regex variables. */ if (Ustrncmp(name, "auth", 4) == 0) { uschar *endptr; int n = Ustrtoul(name + 4, &endptr, 10); --- a/src/functions.h +++ b/src/functions.h @@ -436,10 +436,11 @@ extern int regex(const uschar **); #endif extern BOOL regex_match(const pcre2_code *, const uschar *, int, uschar **); extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, int); extern const pcre2_code *regex_must_compile(const uschar *, BOOL, BOOL); +extern void regex_vars_clear(void); extern void retry_add_item(address_item *, uschar *, int); extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, uschar **, uschar **); extern retry_config *retry_find_config(const uschar *, const uschar *, int, int); extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *, --- a/src/globals.c +++ b/src/globals.c @@ -1313,11 +1313,11 @@ #ifndef DISABLE_PIPE_CONNECT const pcre2_code *regex_EARLY_PIPE = NULL; #endif const pcre2_code *regex_ismsgid = NULL; const pcre2_code *regex_smtp_code = NULL; -const uschar *regex_vars[REGEX_VARS]; +const uschar *regex_vars[REGEX_VARS] = { 0 };; #ifdef WHITELIST_D_MACROS const pcre2_code *regex_whitelisted_macro = NULL; #endif #ifdef WITH_CONTENT_SCAN uschar *regex_match_string = NULL; --- a/src/regex.c +++ b/src/regex.c @@ -94,22 +94,32 @@ } pcre2_match_data_free(md); return FAIL; } + +/* reset expansion variables */ +void +regex_vars_clear(void) +{ +regex_match_string = NULL; +for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +} + + + int -regex(const uschar **listptr) +regex(const uschar ** listptr) { unsigned long mbox_size; -FILE *mbox_file; -pcre_list *re_list_head; -uschar *linebuffer; +FILE * mbox_file; +pcre_list * re_list_head; +uschar * linebuffer; long f_pos = 0; int ret = FAIL; -/* reset expansion variable */ -regex_match_string = NULL; +regex_vars_clear(); if (!mime_stream) /* We are in the DATA ACL */ { if (!(mbox_file = spool_mbox(&mbox_size, NULL, NULL))) { /* error while spooling */ @@ -167,18 +177,17 @@ int mime_regex(const uschar **listptr) { -pcre_list *re_list_head = NULL; -FILE *f; -uschar *mime_subject = NULL; +pcre_list * re_list_head = NULL; +FILE * f; +uschar * mime_subject = NULL; int mime_subject_len = 0; int ret; -/* reset expansion variable */ -regex_match_string = NULL; +regex_vars_clear(); /* precompile our regexes */ if (!(re_list_head = compile(*listptr))) return FAIL; /* no regexes -> nothing to do */ --- a/src/smtp_in.c +++ b/src/smtp_in.c @@ -2155,12 +2155,14 @@ prdr_requested = FALSE; #endif #ifdef SUPPORT_I18N message_smtputf8 = FALSE; #endif +regex_vars_clear(); body_linecount = body_zerocount = 0; +lookup_value = NULL; /* Can be set by ACL */ sender_rate = sender_rate_limit = sender_rate_period = NULL; ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ /* Note that ratelimiters_conn persists across resets. */ /* Reset message ACL variables */