DragonFlyBSD Kernel Audit
DF-2999 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/hammer/hammer_vnops.c
+++ b/sys/vfs/hammer/hammer_vnops.c
@@ -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);
@@ -2187,6 +2197,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,