From: Vsevolod Stakhov Date: Sun, 31 May 2026 13:45:56 +0000 (+0100) Subject: [Fix] archives: bounds guards for RAR/ZIP/7-zip parsing X-Git-Tag: 4.1.0~12 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=ea4e060c5e7c0c462f649858fa4c252c0a2455a3;p=thirdparty%2Frspamd.git [Fix] archives: bounds guards for RAR/ZIP/7-zip parsing Several archive parsers could read slightly past the input buffer on crafted (attacker-controlled) attachments: - RAR v4 file header: fname_len was validated against the remaining buffer, but p then advanced past the attrs and optional HIGH_PACK_SIZE/HIGH_UNP_SIZE fields (4-12 bytes) before the filename was read, allowing an over-read of up to 12 bytes. Re-validate fname_len at the point of use. - 7-zip: rspamd_7zip_read_next_section, _read_digest and _read_bits dereferenced *p before any bounds check; a section/type byte landing on the last byte of the buffer (e.g. a trailing kCRC or kHeader) led to a one-byte over-read. Guard p < end before the dereference. rspamd_7zip_read_archive_props guarded only p != NULL; also require p < end. - ZIP central-directory extra-field loop advanced p by an attacker-controlled hlen without checking it against the remaining extra-field length, producing a past-the-end pointer. Clamp the advance and stop on a truncated field. All reads, no writes; impact is a potential crash on malformed input. --- diff --git a/src/libmime/archives.c b/src/libmime/archives.c index c0b69b8391..e9ffa3cac1 100644 --- a/src/libmime/archives.c +++ b/src/libmime/archives.c @@ -349,6 +349,10 @@ rspamd_archive_process_zip(struct rspamd_task *task, f->flags |= RSPAMD_ARCHIVE_FILE_ENCRYPTED; } + if (hlen + sizeof(uint16_t) * 2 > (gsize) (extra + extra_len - p)) { + /* Truncated or malformed extra field */ + break; + } p += hlen + sizeof(uint16_t) * 2; } @@ -544,6 +548,17 @@ rspamd_archive_process_rar_v4(struct rspamd_task *task, const unsigned char *sta uncomp_sz += tmp; } + /* + * p advanced past the attrs and optional HIGH_*_SIZE fields + * after fname_len was validated above, so re-check it against + * the remaining buffer before reading the filename. + */ + if (fname_len > (gsize) (end - p)) { + msg_debug_archive("rar archive is invalid (truncated filename)"); + + return; + } + f = g_malloc0(sizeof(*f)); if (flags & 0x200) { @@ -1008,6 +1023,10 @@ rspamd_7zip_read_bits(struct rspamd_task *task, for (i = 0; i < nbits; i++) { if (mask == 0) { + if (p >= end) { + return NULL; + } + avail = *p; SZ_SKIP_BYTES(1); mask = 0x80; @@ -1032,6 +1051,10 @@ rspamd_7zip_read_digest(struct rspamd_task *task, uint64_t num_streams, unsigned int *pdigest_read) { + if (p >= end) { + return NULL; + } + unsigned char all_defined = *p; uint64_t i; unsigned int num_defined = 0; @@ -1526,7 +1549,7 @@ rspamd_7zip_read_archive_props(struct rspamd_task *task, * } */ - if (p != NULL) { + if (p != NULL && p < end) { proptype = *p; SZ_SKIP_BYTES(1); @@ -1702,6 +1725,10 @@ rspamd_7zip_read_next_section(struct rspamd_task *task, struct rspamd_archive *arch, struct rspamd_mime_part *part) { + if (p >= end) { + return NULL; + } + unsigned char t = *p; SZ_SKIP_BYTES(1);