DF-0812 / run.log
=== DF-0812 harness: unvalidated redo_data_bytes -> OOB ===
sizeof(hammer_fifo_head) = 16
sizeof(hammer_fifo_redo) = 56 (head + payload fields)
sizeof(hammer_fifo_tail) = 8
redo->redo_data_bytes offset within redo = 32
(redo+1) = first byte after the 56-byte redo struct = payload[0]
--- legitimate REDO record (pre-patch) ---
hdr_size = 96
redo_data_bytes = 32 (legitimate, <= capacity 32)
payload bytes = 32 @ offset 56 in buffer
CRC covers = 96 bytes (the whole record)
CRC does NOT cover= bytes past offset 96 in this 16KB buffer
--- UNDO path validation (recover.c:1053-1060) ---
bytes = hdr_size - sizeof(*undo) - sizeof(tail) = 32
if (bytes < 0 || undo_data_bytes < 0 || undo_data_bytes > bytes) return EIO;
-> UNDO would REJECT redo_data_bytes > 32
=== ATTACK: forge redo_data_bytes = 4096 (capacity is 32) ===
vn_rdwr source = (redo+1) = buf+56
vn_rdwr length = redo_data_bytes = 4096
in-record bytes = 40 (CRC-covered, legitimate payload space)
OOB bytes = 4056 (past the record, into adjacent heap)
First 64 OOB bytes (kernel heap, written to recovered file):
AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
These are 0xAA fill bytes = uninitialized/adjacent kernel heap.
In a real kernel they would be whatever data follows the
REDO record in the hammer_buffer or the adjacent slab object.
=== PANIC variant: redo_data_bytes = 0x7FFFFFFF ===
vn_rdwr length = 2147483647 (2 GB - 1)
(redo+1) + 0x7FFFFFFF walks ~2 GB of kernel virtual space
-> bcopy hits an unmapped page -> Fatal trap 12 page fault
=== The fix: validate redo_data_bytes before vn_rdwr ===
if (redo->redo_data_bytes < 0 ||
redo->redo_data_bytes > hdr_size - sizeof(*redo) - sizeof(tail))
break; /* mirror recover.c:1053-1060 */
With this check, forged redo_data_bytes=4096 > capacity=32 -> rejected.
=== harness complete: OOB extent proven deterministically ===