DragonFlyBSD Kernel Audit
DF-0805 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer2/hammer2_strategy.c b/sys/vfs/hammer2/hammer2_strategy.c
index 1d477f1d76dba9e617634a2f9150cb89b22f76a3..725dd733de25a9f62a22fd9aa61cec3b3eb4058a 100644
--- a/sys/vfs/hammer2/hammer2_strategy.c
+++ b/sys/vfs/hammer2/hammer2_strategy.c
@@ -196,7 +196,27 @@
 
 	KKASSERT(bp->b_bufsize <= HAMMER2_PBUFSIZE);
 	compressed_size = *(const int *)data;
-	KKASSERT((uint32_t)compressed_size <= bytes - sizeof(int));
+
+	/*
+	 * Validate the on-media compressed_size BEFORE handing it to
+	 * LZ4_decompress_safe() as inputSize.  LZ4 trusts inputSize and
+	 * uses it as the source bound (iend = ip + inputSize at
+	 * hammer2_lz4.c:391); an attacker-controlled value read directly
+	 * from disk in a corrupted/malicious image would cause the decoder
+	 * to read past the chain dio buffer into adjacent kernel memory.
+	 * The previous KKASSERT-only guard was compiled out on non-
+	 * INVARIANTS kernels (sys/sys/systm.h:118).
+	 */
+	if (compressed_size < 0 ||
+	    (u_int)compressed_size > bytes - sizeof(int)) {
+		kprintf("HAMMER2 LZ4: bad compressed_size %d (bytes=%u)\n",
+			compressed_size, bytes);
+		bp->b_error = EIO;
+		bp->b_flags |= B_ERROR;
+		bp->b_resid = bp->b_bufsize;
+		bzero(bp->b_data, bp->b_bufsize);
+		return;
+	}
 
 	compressed_buffer = objcache_get(cache_buffer_read, M_INTWAIT);
 	result = LZ4_decompress_safe(__DECONST(char *, &data[sizeof(int)]),