]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Minor] Defensive guards in JPEG and RFC 2047 QP decoders
authorVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 14 May 2026 18:50:17 +0000 (19:50 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 14 May 2026 18:53:28 +0000 (19:53 +0100)
process_jpg_image(): bail out early when the input is shorter than the
minimum needed to safely access the SOF fields referenced as p[4..7].
Pointer-arithmetic associativity already makes the existing
`end = p + data->len - 8` benign on standard targets (the loop simply
doesn't execute for tiny buffers), but the explicit precondition makes
the intent obvious and is robust against future refactors.

rspamd_decode_qp2047_buf(): when an encoded-word ends with a bare `=`
that has no following hex digits, emit a literal `=` instead of reading
one byte past the input. Two paths could reach the OOB read - the
direct `*p == '='` block and the else-branch's `goto decode` after
memcspn finds a trailing `=` - both are now guarded. In production the
read landed inside the surrounding header-value buffer (mempool
allocated, null-terminated), so this is cosmetic, but it silences
fuzzer/ASAN noise on direct-call test harnesses.

src/libmime/images.c
src/libutil/str_util.c

index 3b89e979ae5d4cbe028849aca90977571e2c648b..087834e4f8ef7217324a56ca25387a452afd96d8 100644 (file)
@@ -144,6 +144,14 @@ process_jpg_image(rspamd_mempool_t *pool, rspamd_ftok_t *data)
        uint16_t h, w;
        struct rspamd_image *img;
 
+       /*
+        * Need room for the 2-byte SOI plus a marker (2 bytes) plus the
+        * SOF fields (8 bytes) referenced as p[4..7] below.
+        */
+       if (data->len < 12) {
+               return NULL;
+       }
+
        img = rspamd_mempool_alloc0(pool, sizeof(struct rspamd_image));
        img->type = IMAGE_TYPE_JPG;
        img->data = data;
index f59ff4dd407cf8ed3f523eeb96e5dfcd0c14137c..ac2311ff6258b01b2ae648480b3e4eedfff64383 100644 (file)
@@ -3102,10 +3102,11 @@ rspamd_decode_qp2047_buf(const char *in, gsize inlen,
                        remain--;
 
                        if (remain == 0) {
+                               /* Last '=' character without following hex digits */
                                if (end - o > 0) {
-                                       *o++ = *p;
-                                       break;
+                                       *o++ = '=';
                                }
+                               break;
                        }
                decode:
                        /* Decode character after '=' */
@@ -3170,6 +3171,14 @@ rspamd_decode_qp2047_buf(const char *in, gsize inlen,
                                                p++;
                                                /* Skip comparison, as we know that we have found match */
                                                remain--;
+
+                                               if (remain == 0) {
+                                                       /* Trailing '=' without hex digits */
+                                                       if (end - o > 0) {
+                                                               *o++ = '=';
+                                                       }
+                                                       break;
+                                               }
                                                goto decode;
                                        }
                                        else {