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: