⬢ DragonFlyBSD Kernel Audit
DF-2658 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/hammer2/hammer2_synchro.c	2026-10-15 07:16:23.700535111 +0000
+++ b/sys/vfs/hammer2/hammer2_synchro.c	2026-10-15 07:16:39.628330073 +0000
@@ -920,6 +936,7 @@
 		     int isroot)
 {
 	uint8_t otype;
+	size_t bytes;
 	int nradix;
 	int error;
 
@@ -937,8 +954,18 @@
 		const hammer2_media_data_t *data;
 
 		if (chain->bytes != focus->bytes) {
-			/* XXX what if compressed? */
-			nradix = hammer2_getradix(chain->bytes);
+			/*
+			 * Resize to the FOCUS's physical size.  Compressed
+			 * chains can have a non-power-of-2 physical size,
+			 * so round the radix up to at least focus->bytes.
+			 * (Using chain->bytes -- the OLD size -- computed a
+			 * radix equal to the current allocation, making
+			 * hammer2_chain_resize() a guaranteed no-op and the
+			 * bcopy()s below run with mismatched sizes.)
+			 */
+			nradix = hammer2_getradix(focus->bytes);
+			if ((size_t)1 << nradix < focus->bytes)
+				++nradix;
 			error = hammer2_chain_resize(chain, mtid, 0, nradix, 0);
 			if (error)
 				goto failed;
@@ -1038,15 +1065,44 @@
 			}
 			/* fall through */
 		case HAMMER2_BREF_TYPE_DATA:
-			bcopy(data, chain->data, chain->bytes);
+			/*
+			 * Copy from the focus's data buffer, bounded by the
+			 * focus's physical size.  After the resize above
+			 * chain->bytes == focus->bytes for power-of-2 foci;
+			 * for a non-power-of-2 (compressed) focus the chain
+			 * was rounded up, so zero the remainder.  Never read
+			 * past the end of the focus's buffer.
+			 */
+			bytes = focus->bytes;
+			if (bytes > chain->bytes)
+				bytes = chain->bytes;
+			bcopy(data, chain->data, bytes);
+			if (bytes < chain->bytes)
+				bzero((char *)chain->data + bytes,
+				      chain->bytes - bytes);
 			hammer2_chain_setcheck(chain, chain->data);
 			break;
 		case HAMMER2_BREF_TYPE_DIRENT:
 			/*
 			 * Directory entries embed data in the blockref.
+			 *
+			 * An embedded focus (bytes == 0) has no data buffer;
+			 * clear the local allocation instead of copying
+			 * from a NULL gdata pointer.
 			 */
 			if (chain->bytes) {
-				bcopy(data, chain->data, chain->bytes);
+				if (focus->bytes) {
+					bytes = focus->bytes;
+					if (bytes > chain->bytes)
+						bytes = chain->bytes;
+					bcopy(data, chain->data, bytes);
+					if (bytes < chain->bytes)
+						bzero((char *)chain->data +
+						      bytes,
+						      chain->bytes - bytes);
+				} else {
+					bzero(chain->data, chain->bytes);
+				}
 				hammer2_chain_setcheck(chain, chain->data);
 			} else {
 				chain->bref.check = focus->bref.check;