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/doc-txt/ChangeLog | 8 +++++++- src/src/exim.c | 4 +--- src/src/expand.c | 2 +- src/src/functions.h | 1 + src/src/globals.c | 2 +- src/src/regex.c | 29 ++++++++++++++++++----------- src/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(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 6a4e10915..ba5bd23d9 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -30,11 +30,17 @@ JH/07 OpenSSL Fix auto-reload of changed server OCSP proof. Previously, if the file with the proof had an unchanged name, the new proof(s) were loaded on top of the old ones (and nover used; the old ones were stapled). +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. diff --git a/src/src/exim.c b/src/src/exim.c index ea4286af3..b9328f017 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2000,8 +2000,6 @@ regex_whitelisted_macro = regex_must_compile(US"^[A-Za-z0-9_/.-]*$", MCS_NOFLAGS, 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. */ @@ -6089,7 +6087,7 @@ MORELOOP: 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); } diff --git a/src/src/expand.c b/src/src/expand.c index ffbdc14e5..89de56255 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1860,7 +1860,7 @@ else if (Ustrncmp(name, "r_", 2) == 0) return node ? node->data.ptr : strict_acl_vars ? NULL : US""; } -/* Handle $auth variables. */ +/* Handle $auth, $regex variables. */ if (Ustrncmp(name, "auth", 4) == 0) { diff --git a/src/src/functions.h b/src/src/functions.h index 92a4831e3..345d7bce6 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -447,6 +447,7 @@ extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, in extern const pcre2_code *regex_compile(const uschar *, mcs_flags, uschar **, pcre2_compile_context *); extern const pcre2_code *regex_must_compile(const uschar *, mcs_flags, 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 **); diff --git a/src/src/globals.c b/src/src/globals.c index 574ee60a4..cafb15992 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1324,7 +1324,7 @@ const pcre2_code *regex_EARLY_PIPE = NULL; int regex_cachesize = 0; 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 diff --git a/src/src/regex.c b/src/src/regex.c index 5de1c1704..25496f950 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -93,19 +93,27 @@ 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, BOOL cacheable) +regex(const uschar ** listptr, BOOL cacheable) { 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 */ { @@ -167,14 +175,13 @@ return ret; int mime_regex(const uschar **listptr, BOOL cacheable) { -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, cacheable))) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 11e7436b9..a15280bdc 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2157,8 +2157,10 @@ prdr_requested = FALSE; #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. */ diff --git a/test/confs/4002 b/test/confs/4002 index d9a12a368..4578cc019 100644 --- a/test/confs/4002 +++ b/test/confs/4002 @@ -10,6 +10,7 @@ rfc1413_query_timeout = 0s acl_smtp_rcpt = check_rcpt acl_smtp_data = check_data +acl_smtp_mime = check_mime acl_not_smtp = check_data @@ -20,6 +21,15 @@ begin acl check_rcpt: accept +check_mime: + warn condition = ${if match{$mime_content_type}{text}} + mime_regex = \N(?s)([\w.+=-]+@\w[\w-]*\.[\w.-]+\w)\ + (.+?([\w.+=-]+@\w[\w-]*\.[\w.-]+\w))?\ + (.+?([\w.+=-]+@\w[\w-]*\.[\w.-]+\w))?\ + (.+?([\w.+=-]+@\w[\w-]*\.[\w.-]+\w))?\ + (.+?([\w.+=-]+@\w[\w-]*\.[\w.-]+\w))?\N + accept + check_data: warn regex = \N(THIS\s((\w+)\s)?REGEX)\N message = X-Regex: Regex matched <$regex1> <$regex3> diff --git a/test/mail/4002.userx b/test/mail/4002.userx index 6bc565c3f..8ec49da09 100644 --- a/test/mail/4002.userx +++ b/test/mail/4002.userx @@ -27,9 +27,16 @@ Subject: A real test message Date: Tue, 2 Mar 1999 09:44:33 +0000 Message-ID: <41C2F849.3060203@projectile.test.ex> FakeReject: test fakereject +MIME-Version: 1.0 +Content-Type: text/plain Sender: CALLER_NAME X-Regex: Regex matched OK, this should look like a genuine message, but it will trip on THIS gazornenplaz REGEX. +This checks proper release of variable used for mime_regex +firstname@foobar.com +secondname@blaz.com +thirdname@blaz.com + diff --git a/test/scripts/4000-scanning/4002 b/test/scripts/4000-scanning/4002 index a660f6805..fcfae698b 100644 --- a/test/scripts/4000-scanning/4002 +++ b/test/scripts/4000-scanning/4002 @@ -30,9 +30,16 @@ Subject: A real test message Date: Fri, 17 Dec 2004 16:13:04 +0100 Message-ID: <41C2F849.3060203@projectile.test.ex> FakeReject: test fakereject +MIME-Version: 1.0 +Content-Type: text/plain OK, this should look like a genuine message, but it will trip on THIS gazornenplaz REGEX. + +This checks proper release of variable used for mime_regex +firstname@foobar.com +secondname@blaz.com +thirdname@blaz.com . quit **** -- 2.30.2