DF-2658 / fix.diff
--- 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; |