Fwd: report 1/3: buffer under-read in rfc2047.c lwslen()

"Kevin J. McCarthy" <[email protected]> Thu, 18 Jun 2026 08:09:01 +0800
Newsgroups gmane.mail.mutt.devel
Message-ID <[email protected]>
----- Forwarded message from Acts1631 <[email protected]> -----

Date: Wed, 17 Jun 2026 20:15:06 +0000
From: Acts1631 <[email protected]>
To: "[email protected]" <[email protected]>
Subject: report 1/3: buffer under-read in rfc2047.c lwslen()
X-Spam-score: 0.0
X-Delivered-to: [email protected]
Message-ID: <KB1oAIhwdCCndxRC7kVDWhJwXFMiK_E4A1Rr0WtQC3oDqkYPf6QD03ytEi_v2em8t8G3YKXEM_U5PYaYGijG4BPSorlrytsZh79CIJT6t7U=@proton.me>

Hi again sir. Thanks for taking my earlier bug. I have 3 more for you to consider. Each one has a proposed patch and reproducer. Here's the first one.

The lwslen() helper calculates the length of linear whitespace at the beginning of a string. It scans until the first non-whitespace character, then evaluates *(p - 1):

   for (; p < s + n; p++)
     if (!strchr(" \t\r\n", *p))
     {
       len = (size_t)(p - s);
       break;
     }
   if (strchr("\r\n", *(p-1)))
     len = (size_t)0;

If the string starts with a non-whitespace character, the loop breaks on its first iteration with p == s and len == 0. The subsequent *(p - 1) read accesses one byte before the buffer.

This is reachable through rfc2047_decode() when ignore_linear_white_space is enabled and lwslen() is called on text following an encoded word.

Example trigger subject:
   Subject: =?utf-8?Q?foo?=bar

After decoding the encoded word, rfc2047_decode() sees the trailing "bar", sets found_encoded, and calls lwslen("bar", 3). The first character is not whitespace, so the under-read occurs in the unpatched code.

Proposed fix: Guard the final look-behind with len > 0:

   if (len > 0 && strchr("\r\n", *(p-1)))
     len = (size_t)0;

When len > 0, p has advanced beyond s, so p - 1 is guaranteed to be in bounds. When len == 0, the CR/LF check cannot change the return value and can be safely skipped.




----- End forwarded message -----

-- 
Kevin J. McCarthy
GPG Fingerprint: 8975 A9B3 3AA3 7910 385C  5308 ADEF 7684 8031 6BDA
bug1.patch (text/x-patch, 406 B)
diff --git a/rfc2047.c b/rfc2047.c
index d72ae997..2bdd8553 100644
--- a/rfc2047.c
+++ b/rfc2047.c
@@ -815,7 +815,7 @@ static size_t lwslen(const char *s, size_t n)
       len = (size_t)(p - s);
       break;
     }
-  if (strchr("\r\n", *(p-1))) /* LWS doesn't end with CRLF */
+  if (len > 0 && strchr("\r\n", *(p-1))) /* LWS doesn't end with CRLF */
     len = (size_t)0;
   return len;
 }
bug1_reproducer.py (text/x-python, 1 KB)
#!/usr/bin/env python3
"""
Reproducer for Bug 1: rfc2047.c lwslen() buffer under-read.

This emits an mbox message whose Subject has trailing non-whitespace text
after an RFC2047 encoded word:

    Subject: =?utf-8?Q?foo?=bar

In rfc2047_decode(), once an encoded word has been found,
ignore_linear_white_space makes mutt call lwslen() on the trailing "bar".
The first byte is not whitespace, so the unpatched lwslen() reads one byte
before the buffer via *(p - 1).

Example:

    python3 reproducer_lwslen.py > /tmp/lwslen.mbox
    mutt -e 'set ignore_linear_white_space' -f /tmp/lwslen.mbox

Use an AddressSanitizer build to make the one-byte under-read visible.
"""

email = """\
From [email protected] Wed Jun 17 00:00:00 2026
From: [email protected]
To: [email protected]
Subject: =?utf-8?Q?foo?=bar
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii

This message triggers lwslen() on trailing non-whitespace text when
ignore_linear_white_space is enabled.
"""


if __name__ == "__main__":
    print(email)
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEEiXWpszqjeRA4XFMIre92hIAxa9oFAmozNx0ACgkQre92hIAx
a9rVfg//W++Ja/u5lh1SWKwCHAzyr7CgRo8lEXvmqORpEGjOBODNiicCuWWKJCER
mRSsDbxWjFFY84AaQIK8b+sV+0SoiqTXPLtOVOfajY2fmeYbH80a9ZA0tZEo4yOS
P5FWy+Rx2vH0d5yqnVBSejBidVmkH4WTHC7018a3WVOn583X4bs5Ag8rSKhSQ2fq
LLBXSWLfTjKn3Bwv/Bp+v4snbXfQidj0iHEe6iSd32BGrY74I51/FNaH+gW2Ygwk
xIcaUJ9TvKS8/2p/zB/HVZsSnQfR96TdTPxLQAigMmIhzgqmngn0hjZJFSm/bZcq
UtTr4kCMRSQoTC/5JRZ8AAmnnIjDabnvpuNWG3kIv+listPJa7r27PetkfV28qvQ
HbfKezmv0DqCTwjhRXWCRtDs/ImMAVXa+gwOxbJ77rypKQi+ZZa9kb+ns5nV9qP9
wkPhrG2fKCt1DcbMfRqj/UqfXqzK2aE8+r1agrpL1w+kEx7UWHGTnokYdayi7tHg
7WU5OWPJDxJVKwlrbARXQBWvIboteZjvNUQWV+MtOgWmb2qhjAZ+qaHmP+TlXOT/
po7RTUunX+YUoLNoedy1HmFOOdzDTda3FjH1L2y2AAci9lrtSYmlGE9zJCqEQbFQ
BtMl4sjylgvn1+stg7JwAF9WF2nLcQVs2kPjtZBl34oFUDIsfto=
=9fxh
-----END PGP SIGNATURE-----