diff --git a/sys/vfs/hammer/hammer_reblock.c b/sys/vfs/hammer/hammer_reblock.c --- a/sys/vfs/hammer/hammer_reblock.c +++ b/sys/vfs/hammer/hammer_reblock.c @@ -461,6 +461,31 @@ int error; void *ndata; + /* + * Sanity-check the on-disk record before trusting data_len as a + * copy length and data_offset as a copy source. A legitimately + * written record never crosses a 16KB buffer boundary (regular + * large-data records are written one per 16KB buffer and small + * records are allocated within a single buffer), so anything + * else is corruption. + * + * An out-of-range or buffer-crossing (data_offset, data_len) + * pair makes the source extraction and/or the destination copy + * run past the end of the DOALIGN16K(data_len)-sized kernel + * buffers (kernel memory corruption), and data_len values above + * HAMMER_BUFSIZE also make hammer_bread_ext() getblk() buffers + * that overlap other cached buffers on the device. + */ + if (elm->leaf.data_len <= 0 || + elm->leaf.data_len > HAMMER_BUFSIZE || + ((elm->leaf.data_offset & HAMMER_BUFMASK64) + + (hammer_off_t)elm->leaf.data_len) > HAMMER_BUFSIZE) { + hkprintf("reblock %016jx/%d: illegal data range\n", + (intmax_t)elm->leaf.data_offset, + elm->leaf.data_len); + return (EINVAL); + } + error = hammer_btree_extract_data(cursor); if (error) return (error); diff --git a/sys/vfs/hammer/hammer_blockmap.c b/sys/vfs/hammer/hammer_blockmap.c --- a/sys/vfs/hammer/hammer_blockmap.c +++ b/sys/vfs/hammer/hammer_blockmap.c @@ -173,7 +173,22 @@ /* * The allocation request may not cross a buffer boundary. Special * large allocations must not cross a big-block boundary. + * + * Large allocations must also be 16KB-buffer aligned. The caller + * (hammer_alloc_data()->hammer_bread_ext()) obtains a single + * DOALIGN16K(bytes)-sized buffer plus the intra-buffer offset of + * the allocation; if the allocation is not buffer-aligned any + * bytes-sized access to the returned pointer runs past the end of + * that buffer (kernel memory corruption). Regular large-data + * writes are always buffer-aligned reservations, so this only + * affects defensive handling of reblock/mirror allocations. */ + if (bytes > HAMMER_BUFSIZE && + (next_offset & HAMMER_BUFMASK64) != 0) { + next_offset = (next_offset + HAMMER_BUFMASK64) & + ~HAMMER_BUFMASK64; + goto again; + } tmp_offset = next_offset + bytes - 1; if (bytes <= HAMMER_BUFSIZE) { if ((next_offset ^ tmp_offset) & ~HAMMER_BUFMASK64) {