⬢ DragonFlyBSD Kernel Audit
DF-3167 / fix.diff
← back to finding ↓ download raw
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) {