DF-3000 / probe.diff
--- a/sys/vfs/hammer/hammer_vnops.c 2026-09-05 01:20:18.240323232 +0000 +++ b2/sys/vfs/hammer/hammer_vnops.c 2026-09-05 01:41:14.164464786 +0000 @@ -566,6 +566,16 @@ return (EFBIG); } + /* + * DF-2999: also reject a final offset that would overflow the + * signed block round-up in nvextendbuf()/nvnode_pager_setsize() + * (same 2^63 wrap as the setattr path below). + */ + if (uio->uio_resid > 0 && base_offset > OFF_MAX - HAMMER_XBUFSIZE) { + hammer_done_transaction(&trans); + return (EFBIG); + } + if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc && base_offset > td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) { hammer_done_transaction(&trans); @@ -690,9 +700,13 @@ bp = getblk(ap->a_vp, base_offset, blksize, GETBLK_BHEAVY, 0); if ((bp->b_flags & B_CACHE) == 0) { + kprintf("DF3000-PROBE: branch fired vp=%p bp=%p bcount=%d\n", + ap->a_vp, bp, bp->b_bcount); bqrelse(bp); error = bread(ap->a_vp, base_offset, blksize, &bp); + kprintf("DF3000-PROBE: after bread err=%d lockinuse=%d\n", + error, BUF_LOCKINUSE(bp)); } } else if (offset == 0 && uio->uio_resid >= blksize) { /* @@ -2187,6 +2201,21 @@ hammer_start_transaction(&trans, hmp); error = 0; + /* + * DF-2999: Reject file lengths within HAMMER_XBUFSIZE of 2^63 + * (and negative lengths). nvtruncbuf()/nvnode_pager_setsize() + * round the truncation point up to the next 64K block in signed + * 64-bit arithmetic; for such lengths the round-up wraps to + * INT64_MIN and the page-unmap loop in nvnode_pager_setsize() + * iterates ~2^63 times while holding the vnode token and the + * vm_object lock (permanent, unkillable kernel livelock). + */ + if (vap->va_size != (off_t)VNOVAL && + (vap->va_size < 0 || vap->va_size > OFF_MAX - HAMMER_XBUFSIZE)) { + error = (vap->va_size < 0) ? EINVAL : EFBIG; + goto done; + } + if (vap->va_flags != VNOVAL) { flags = ip->ino_data.uflags; error = vop_helper_setattr_flags(&flags, vap->va_flags, |