]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] mime_headers/encoding: correct lengths after in-place rewrites
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 16 May 2026 14:45:03 +0000 (15:45 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 16 May 2026 14:45:03 +0000 (15:45 +0100)
- mime_headers (message-id): after g_strstrip shifts content forward
  in-place, the pre-strip length is stale; re-acquire p and len so the
  cleanup loop does not scan past the live content and pull stale bytes
  (which the loop would otherwise turn into '?' or treat as a trailing
  '>') into MESSAGE_FIELD(task, message_id).
- mime_encoding (rspamd_charset_normalize): fix the trim-in-place math;
  the previous version copied one extra byte past `end` and wrote the
  null terminator at the unshifted offset, leaving stale trailing bytes
  in the normalized charset name.
- mime_encoding (rspamd_mime_charset_utf_enforce): use goffset for the
  inner offsets so buffers >= 2 GiB cannot truncate to int32_t and make
  p += cur_offset walk backwards into OOB writes.

src/libmime/mime_encoding.c
src/libmime/mime_headers.c

index 1766ec549fd41177fa93b6d9e10cbd1fd98e0582..aa04420a2cca90747bb69a3fe1553ec0a6569dc5 100644 (file)
@@ -244,8 +244,16 @@ rspamd_charset_normalize(char *in)
        }
 
        if (changed) {
-               memmove(in, begin, end - begin + 2);
-               *(end + 1) = '\0';
+               /*
+                * Copy the trimmed content [begin..end] (length = end-begin+1)
+                * and null-terminate at the shifted position. The previous
+                * version copied one extra byte from past `end` and wrote the
+                * terminator at the original (unshifted) offset, leaving stale
+                * bytes in the result.
+                */
+               size_t out_len = (size_t) (end - begin + 1);
+               memmove(in, begin, out_len);
+               in[out_len] = '\0';
        }
 }
 
@@ -569,10 +577,15 @@ void rspamd_mime_charset_utf_enforce(char *in, gsize len)
 
        while (p < end && len > 0 && (err_offset = rspamd_fast_utf8_validate(p, len)) > 0) {
                err_offset--; /* As it returns it 1 indexed */
-               int32_t cur_offset = err_offset;
+               /*
+                * Keep offsets as goffset (signed long) to avoid truncating to
+                * int32_t on buffers >= 2 GiB: a negative cur_offset would make
+                * `p += cur_offset` walk backwards.
+                */
+               goffset cur_offset = err_offset;
 
-               while (cur_offset < len) {
-                       int32_t tmp = cur_offset;
+               while (cur_offset < (goffset) len) {
+                       goffset tmp = cur_offset;
 
                        U8_NEXT(p, cur_offset, len, uc);
 
index 5bffb801f20f8e583900edf423aa1d2af5fb7843..587ba3516bed61ec69dc2bcbba9f9df72e4b3174 100644 (file)
@@ -86,6 +86,13 @@ rspamd_mime_header_check_special(struct rspamd_task *task,
                rh->decoded[len] = '\0'; /* Zero terminate after stripping */
                /* Strip surrounding spaces */
                rh->decoded = g_strstrip(rh->decoded);
+               /*
+                * g_strstrip may memmove content forward and/or null-terminate
+                * earlier, so re-acquire pointer/length to avoid scanning stale
+                * bytes past the live content.
+                */
+               p = rh->decoded;
+               len = strlen(p);
                end = p + len;
 
                if (*p == '<') {