DragonFlyBSD Kernel Audit
DF-2594 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer/hammer_recover.c b/sys/vfs/hammer/hammer_recover.c
--- a/sys/vfs/hammer/hammer_recover.c
+++ b/sys/vfs/hammer/hammer_recover.c
@@ -1221,10 +1221,37 @@
 	struct hammer_rterm rtval;
 	hammer_rterm_t rterm;
 	hammer_rterm_entry_t rte;
+	int payload;
 
 	if (redo->head.hdr_type != HAMMER_HEAD_TYPE_REDO)
 		return(0);
 
+	/*
+	 * Validate the REDO record before trusting any of its fields.
+	 * The record must be large enough to hold the redo struct and the
+	 * tail, and for REDO_WRITE the redo_data_bytes payload must fit
+	 * within the declared record size.  Without this an attacker-crafted
+	 * image can pass the FIFO CRC / signature checks (which only enforce
+	 * a 24-byte minimum) and supply an inflated redo_data_bytes that is
+	 * handed directly to vn_rdwr() in hammer_recover_redo_exec(), causing
+	 * an out-of-bounds kernel-memory read on mount.  Mirrors the UNDO
+	 * validation in hammer_recover_undo().
+	 */
+	if (redo->head.hdr_size < sizeof(*redo) + sizeof(struct hammer_fifo_tail)) {
+		hkprintf("Corrupt REDO record, hdr_size %d < %zu\n",
+			redo->head.hdr_size,
+			sizeof(*redo) + sizeof(struct hammer_fifo_tail));
+		return(EIO);
+	}
+	payload = redo->head.hdr_size - sizeof(*redo) -
+		  sizeof(struct hammer_fifo_tail);
+	if (redo->redo_flags == HAMMER_REDO_WRITE &&
+	    (redo->redo_data_bytes < 0 || redo->redo_data_bytes > payload)) {
+		hkprintf("Corrupt REDO record, redo_data_bytes %d/%d\n",
+			redo->redo_data_bytes, payload);
+		return(EIO);
+	}
+
 	switch(redo->redo_flags) {
 	case HAMMER_REDO_WRITE:
 	case HAMMER_REDO_TRUNC: