[Bug 3191] New: Segmentation fault in string.c

Exim Bugzilla via Exim-dev <[email protected]> Thu, 22 Jan 2026 16:02:54 +0000
Newsgroups gmane.mail.exim.devel
Message-ID <[email protected]/>
https://bugs.exim.org/show_bug.cgi?id=3191

            Bug ID: 3191
           Summary: Segmentation fault in string.c
           Product: Exim
           Version: 4.99
          Hardware: x86-64
                OS: Linux
            Status: NEW
          Severity: bug
          Priority: medium
         Component: General execution
          Assignee: [email protected]
          Reporter: [email protected]
                CC: [email protected]

Created attachment 1530
  --> https://bugs.exim.org/attachment.cgi?id=1530&action=edit
File with crash input

I have found segmentation fault in string.c:1887 in string_vformat_trc()
function:

g->ptr += sprintf(gp, l_align ? "%-*.*s" : "%*.*s", width, precision, s);

The error happens when *gp pointer is NULL and is transferred to sprintf()
function.

The real reason of this error is place here (log_vwrite in log.c) on line 855:

gstring gs = { .size = LOG_BUFFER_SIZE-2, .ptr = 0, .s = log_buffer };

Since log_buffer is NULL, the gs.s member is NULL too.
Then on line 878 log_buffer is checked for NULL and memory is allocated for
log_buffer. But gs.s is still NULL:

if (!log_buffer)
{
  if (!(log_buffer = US malloc(LOG_BUFFER_SIZE)))

In string_vformat_trc() gs.s becomes *gp pointer:
gp = CS g->s + g->ptr;

This is ASAN report:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==1670813==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc
0x7bd75fc875fe bp 0x000000000032 sp 0x7ffc25d959f0 T0)
==1670813==The signal is caused by a WRITE memory access.
==1670813==Hint: address points to the zero page.
    #0 0x7bd75fc875fe in _IO_str_overflow libio/strops.c:133:28
    #1 0x7bd75fc86200 in __GI__IO_default_xsputn libio/genops.c:399:24
    #2 0x7bd75fc86200 in _IO_default_xsputn libio/genops.c:370:1
    #3 0x7bd75fc5fc10 in outstring_func stdio-common/vfprintf-internal.c:239:16
    #4 0x7bd75fc5fc10 in __vfprintf_internal
stdio-common/vfprintf-process-arg.c:427:5
    #5 0x7bd75fc7b3b2 in __vsprintf_internal libio/iovsprintf.c:96:9
    #6 0x7bd75fc7b3b2 in vsprintf libio/iovsprintf.c:105:10
    #7 0x55a7350cae46 in vsprintf
(/exim-main/src/build-Linux-x86_64/parser_harness+0x7ce46) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)
    #8 0x55a7350cc21a in sprintf
(/exim-main/src/build-Linux-x86_64/parser_harness+0x7e21a) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)
    #9 0x55a7351a7617 in string_vformat_trc
(/exim-main/src/build-Linux-x86_64/parser_harness+0x159617) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)
    #10 0x55a7351b4c8a in string_fmt_append_noextend log.c
    #11 0x55a7351b5dda in log_vwrite log.c
    #12 0x55a7351b54b8 in log_write_die
(/exim-main/src/build-Linux-x86_64/parser_harness+0x1674b8) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)
    #13 0x55a7351a7e86 in string_sprintf_trc
(/exim-main/src/build-Linux-x86_64/parser_harness+0x159e86) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)
    #14 0x55a735211b1a in parse_forward_list
(/exim-main/src/build-Linux-x86_64/parser_harness+0x1c3b1a) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)
    #15 0x55a735183ffa in main
/exim-main/src/build-Linux-x86_64/../parser_harness.c:53:13
    #16 0x7bd75fc2a249 in __libc_start_call_main
csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #17 0x7bd75fc2a304 in __libc_start_main csu/../csu/libc-start.c:360:3
    #18 0x55a7350a43c0 in _start
(/exim-main/src/build-Linux-x86_64/parser_harness+0x563c0) (BuildId:
aebaff1561b1ce6ed50579227ad593c295270eeb)

==1670813==Register values:
rax = 0x0000000000000000  rbx = 0x00007ffc25d95ff0  rcx = 0x0000000000000000 
rdx = 0x0000000000000001  
rdi = 0x00007ffc25d95ff0  rsi = 0x0000000000000032  rbp = 0x0000000000000032 
rsp = 0x00007ffc25d959f0  
 r8 = 0x000000000000ffff   r9 = 0x0000000000000073  r10 = 0x000055a735d715c0 
r11 = 0x000055a735d715f3  
r12 = 0xffffffffffffffff  r13 = 0x0000000000000000  r14 = 0x0000000000000000 
r15 = 0x000055a735d715e0  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV libio/strops.c:133:28 in _IO_str_overflow
==1670813==ABORTING

Possible fix that I can suggest:

diff --git a/src/src/log.c b/src/src/log.c
index 40bdfcd0c..60f8e8d53 100644
--- a/src/src/log.c
+++ b/src/src/log.c
@@ -876,11 +876,14 @@ if (panic_recurseflag)
 when running Exim proper, only when running utilities. */

 if (!log_buffer)
+{
   if (!(log_buffer = US malloc(LOG_BUFFER_SIZE)))
     {
     fprintf(stderr, "exim: failed to get store for log buffer\n");
     exim_exit(EXIT_FAILURE);
     }
+  gs.s = log_buffer;
+}

 /* If we haven't already done so, inspect the setting of log_file_path to
 determine whether to log to files and/or to syslog. Bits in logging_mode

After that I fix this bug I have found another bug in log_vwrite() in
log.c:1038:

if (f.really_exim && message_id[0])

Error happens because message_id is NULL.

ASAN report:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==1675885==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc
0x634208befe4c bp 0x634208c8b4c8 sp 0x7ffee6d8f9a0 T0)
==1675885==The signal is caused by a READ memory access.
==1675885==Hint: address points to the zero page.
    #0 0x634208befe4c in log_vwrite log.c
    #1 0x634208bef4b8 in log_write_die
(/exim-main/src/build-Linux-x86_64/parser_harness+0x1674b8) (BuildId:
ed25fae69d25a7f65e7fdfc4b2fb7f0923e1afe5)
    #2 0x634208be1e86 in string_sprintf_trc
(/exim-main/src/build-Linux-x86_64/parser_harness+0x159e86) (BuildId:
ed25fae69d25a7f65e7fdfc4b2fb7f0923e1afe5)
    #3 0x634208c4bb23 in parse_forward_list
(/exim-main/src/build-Linux-x86_64/parser_harness+0x1c3b23) (BuildId:
ed25fae69d25a7f65e7fdfc4b2fb7f0923e1afe5)
    #4 0x634208bbdffa in main
/exim-main/src/build-Linux-x86_64/../parser_harness.c:53:13
    #5 0x7cdcd5991249 in __libc_start_call_main
csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #6 0x7cdcd5991304 in __libc_start_main csu/../csu/libc-start.c:360:3
    #7 0x634208ade3c0 in _start
(/exim-main/src/build-Linux-x86_64/parser_harness+0x563c0) (BuildId:
ed25fae69d25a7f65e7fdfc4b2fb7f0923e1afe5)

==1675885==Register values:
rax = 0x00007ffee6d8fa60  rbx = 0x0000000000000007  rcx = 0x0000000000000020 
rdx = 0x0000000000000000  
rdi = 0x0000525000002900  rsi = 0x0000000000000014  rbp = 0x0000634208c8b4c8 
rsp = 0x00007ffee6d8f9a0  
 r8 = 0x00000a4a00000522   r9 = 0x00000a4a7fff8520  r10 = 0x0000000000000000 
r11 = 0x00006342097ab5f3  
r12 = 0x0000000000000000  r13 = 0x00007ffee6d8fab8  r14 = 0x0000000000000000 
r15 = 0x0000000000000002  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV log.c in log_vwrite
==1675885==ABORTING

The possible fix:

diff --git a/src/src/log.c b/src/src/log.c
index 40bdfcd0c..60f8e8d53 100644
--- a/src/src/log.c
+++ b/src/src/log.c
@@ -1032,7 +1035,7 @@ if (LOGGING(pid))
   if (!syslog_pid) pid_position[1] = gstring_length(g);        /*  … and end+1
of the PID */
   }

-if (f.really_exim && message_id[0])
+if (f.really_exim && message_id && message_id[0])
   string_fmt_append_noextend(g, "%s ", message_id);

 if (flags & LOG_CONFIG)

This is the test harness:
#ifndef __AFL_FUZZ_TESTCASE_LEN
    ssize_t fuzz_len;
    unsigned char fuzz_buf[1024000];
    #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
    #define __AFL_FUZZ_TESTCASE_LEN fuzz_len
    #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0
? 1 : 0)
    #define __AFL_FUZZ_INIT() void sync(void);
    #define __AFL_INIT() sync()
#endif

#ifndef MAX_INPUT
#define MAX_INPUT (1 << 20)
#endif


__AFL_FUZZ_INIT();

int main(void)
{
    store_init();
    __AFL_INIT();
    while (__AFL_LOOP(1000)) {
        size_t len = __AFL_FUZZ_TESTCASE_LEN;
        unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;

        if (len == 0 || !buf ) {
            return 0;
        }

        char *buff = (char *)malloc(len + 1);
        memcpy(buff, buf, len);
        buff[len] = '\0';
        uschar *errmess;
        address_item *anchor = NULL;
        parse_forward_list(buf, -1, &anchor, &errmess, US"incoming.domain",
NULL, NULL);
        free(buff);
    }
    return 0;
}

Build by clang with -fsanitize=address option.

Execute ./src/build-Linux-x86_64/parser_harness < crash.txt

-- 
You are receiving this mail because:
You are on the CC list for the bug.

-- 
## subscription configuration (requires account):
##   https://lists.exim.org/mailman3/postorius/lists/exim-dev.lists.exim.org/
## unsubscribe (doesn't require an account):
##   [email protected]
## Exim details at https://www.exim.org/
## Please use the Wiki with this list - https://code.exim.org/exim/wiki/wiki