bruceg / nullmailer

Relay-only sendmail/qmail/etc replacement MTA
http://untroubled.org/nullmailer/
GNU General Public License v2.0
188 stars 44 forks source link

reproducible crash when calling nullmailer-smtpd from swaks #95

Open bremner opened 5 months ago

bremner commented 5 months ago

In 1 Axel observes that

   swaks -t foo@example.com --pipe '/usr/lib/sendmail -bs'

reliably segfaults nullmailer.

I duplicated this also with

   swaks -t foo@example.com --pipe 'nullmailer-smtpd'

I ran nullmailer-smtpd under gdb and valgrind and it succeeded in both cases. I tried rebuilding with ASAN, and the test "Testing protocol success with smtp (stdin)" finds one memory leak.

    220 OK
    smtp: Succeeded: 220 OK

    =================================================================
    ==3035435==ERROR: LeakSanitizer: detected memory leaks

    Direct leak of 13 byte(s) in 1 object(s) allocated from:
        #0 0x7f64762edd10 in strdup
../../../../src/libsanitizer/asan/asan_interceptors.cpp:578
        #1 0x563a16835173 in parse_option
/home/bremner/software/debian/nullmailer/protocols/protocol.cc:116
        #2 0x563a16835173 in parse_options
/home/bremner/software/debian/nullmailer/protocols/protocol.cc:130
        #3 0x563a16835173 in cli_main(int, char**)
/home/bremner/software/debian/nullmailer/protocols/protocol.cc:138

    SUMMARY: AddressSanitizer: 13 byte(s) leaked in 1 allocation(s).

Looking at the code there is indeed a leak, but it's hard to see how it would lead to a crash.

bernhardu commented 5 months ago

There are new findings in the Debian bug report which lead to the crash happens when an error message should be printed.

Program received signal SIGSEGV, Segmentation fault.
__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:76
76      in ../sysdeps/x86_64/multiarch/strlen-avx2.S
1: x/i $pc
=> 0x7f0527973dd9 <__strlen_avx2+25>:   vpcmpeqb (%rdi),%ymm0,%ymm1
(rr) bt
#0  __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:76
#1  0x00005639bd91306e in fdobuf::operator<< (str=0x6c69616d6c6c756e <error: Cannot access memory at address 0x6c69616d6c6c756e>, this=<optimized out>) at ./fdbuf/fdobuf.h:59
#2  fork_exec::wait (this=this@entry=0x7ffc4099cd00) at ./lib/forkexec.cc:125
#3  0x00005639bd91201f in DATA (param=...) at ./src/smtpd.cc:159
#4  DATA (param=...) at ./src/smtpd.cc:127
#5  0x00005639bd91144f in dispatch () at ./src/smtpd.cc:252
#6  main () at ./src/smtpd.cc:263

A git bisect showed 5850a49a as the first commit causing this crash.

Following diff would avoid the crash:

--- src/smtpd.cc.orig   2023-04-22 19:06:36.000000000 +0200
+++ src/smtpd.cc        2024-06-21 10:45:25.982395298 +0200
@@ -53,5 +53,5 @@ static mystring sender;
 static mystring recipients;

-extern const char cli_program[] = "nullmailer-smtpd";
+const char* cli_program = "nullmailer-smtpd";

 static int readline()
schongallam commented 2 months ago

I also recently stumbled across this bug, through a different mechanism. As bernhardu alludes to, it is a result of any error message printed to ferr by smtpd where cli_program is involved, not just swaks or sendmail related things. The type confusion between char* and char[] is indeed the root cause.

Any SMTP payload that causes fork_exec::wait_status to return a non-zero value to fork_exec::wait should suffice to reproduce this crash.

General steps to reproduce:

Expected behavior: Malformed MAIL/RCPT addresses that are not FQDN should be rejected, in accordance with RFC 5321.

Resulting behavior: Segmentation fault.

Impact:

Fix(es):

bruceg commented 4 weeks ago

Thank you for the extensive report, all. This is indeed a silly typo and should be fixed in 79a8a45

Note that the man page explicitly cautions not to use this in an untrusted environment. As such, while I fully agree this is a bug that needs fixing, I am not seeing how this can be used to cause a security issue.