⬢ DragonFlyBSD Kernel Audit
DF-5817 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/hammer2/hammer2_vnops.c	2026-06-29 12:51:19.000000000 +0000
+++ b/sys/vfs/hammer2/hammer2_vnops.c	2026-10-13 05:02:22.968295490 +0000
@@ -828,7 +828,6 @@
 hammer2_vop_write(struct vop_write_args *ap)
 {
 	hammer2_inode_t *ip;
-	thread_t td;
 	struct vnode *vp;
 	struct uio *uio;
 	int error;
@@ -864,16 +863,6 @@
 	seqcount = ioflag >> IO_SEQSHIFT;
 
 	/*
-	 * Check resource limit
-	 */
-	if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc &&
-	    uio->uio_offset + uio->uio_resid >
-	     td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
-		lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
-		return (EFBIG);
-	}
-
-	/*
 	 * The transaction interlocks against flush initiations
 	 * (note: but will run concurrently with the actual flush).
 	 *
@@ -1019,6 +1008,38 @@
 	old_eof = ip->meta.size;
 
 	/*
+	 * If the file is flagged append-only (chflags uappnd/sappnd),
+	 * writes must occur at EOF.  The descriptor-level O_APPEND
+	 * enforcement in nlookup() only applies to opens that happen
+	 * while the flag is set; it cannot retroactively fix already-
+	 * open descriptors.  (Matches ffs_write: ufs_readwrite.c)
+	 */
+	if ((ip->meta.uflags & APPEND) && uio->uio_offset != ip->meta.size) {
+		hammer2_mtx_unlock(&ip->lock);
+		hammer2_mtx_unlock(&ip->truncate_lock);
+		return (EPERM);
+	}
+
+	/*
+	 * Check resource limit.  This must be done AFTER the IO_APPEND
+	 * offset override above, otherwise append writes bypass the
+	 * file-size rlimit entirely (no EFBIG, no SIGXFSZ).
+	 * (Matches ffs_write ordering: ufs_readwrite.c)
+	 */
+	if (uio->uio_resid > 0) {
+		thread_t td = uio->uio_td;
+
+		if (td != NULL && td->td_proc &&
+		    uio->uio_offset + uio->uio_resid >
+		     td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
+			hammer2_mtx_unlock(&ip->lock);
+			hammer2_mtx_unlock(&ip->truncate_lock);
+			lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
+			return (EFBIG);
+		}
+	}
+
+	/*
 	 * Extend the file if necessary.  If the write fails at some point
 	 * we will truncate it back down to cover as much as we were able
 	 * to write.