From: Vsevolod Stakhov Date: Thu, 14 May 2026 18:50:17 +0000 (+0100) Subject: [Minor] Defensive guards in JPEG and RFC 2047 QP decoders X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=f0f11e051c4039a98b646d002b189018d6cc59d2;p=thirdparty%2Frspamd.git [Minor] Defensive guards in JPEG and RFC 2047 QP decoders 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. --- diff --git a/src/libmime/images.c b/src/libmime/images.c index 3b89e979ae..087834e4f8 100644 --- a/src/libmime/images.c +++ b/src/libmime/images.c @@ -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; diff --git a/src/libutil/str_util.c b/src/libutil/str_util.c index f59ff4dd40..ac2311ff62 100644 --- a/src/libutil/str_util.c +++ b/src/libutil/str_util.c @@ -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 {