git: 8742817ba62e - main - FFS extattr: fix handling of the tail

Konstantin Belousov kib at FreeBSD.org
Tue Mar 2 00:19:55 UTC 2021


The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=8742817ba62ec604156c139727155d36f5fbad06

commit 8742817ba62ec604156c139727155d36f5fbad06
Author:     Konstantin Belousov <kib at FreeBSD.org>
AuthorDate: 2021-03-01 15:24:11 +0000
Commit:     Konstantin Belousov <kib at FreeBSD.org>
CommitDate: 2021-03-02 00:19:34 +0000

    FFS extattr: fix handling of the tail
    
    There are three issues with change that stopped truncating ea area before
    write, and resulted in possible zero tail in the ea area:
    - Truncate to zero checked i_ea_len after the reference was dropped,
      making the last drop effectively truncate to zero length always.
    - Loop to fill uio for zeroing specified too large length, that triggered
      assert in normal situation.
    - Integrity check could trip over the tail, instead we must allow
      partial header or header with zero length, and clamp ea image in
      memory at it.
    
    Reported by:    arichardson
    Tested by:      arichardson, pho
    Sponsored by:   The FreeBSD Foundation
    MFC after:      3 days
    Fixup:  5e198e7646a27412c0541719f7bf1bbc0bd89223
    Differential Revision:  https://reviews.freebsd.org/D28999
---
 sys/ufs/ffs/ffs_vnops.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index 050a6f66be17..dc638595eb7b 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -1346,13 +1346,20 @@ ffs_rdextattr(u_char **p, struct vnode *vp, struct thread *td)
 	/* Validate disk xattrfile contents. */
 	for (eap = (void *)eae, eaend = (void *)(eae + easize); eap < eaend;
 	    eap = eapnext) {
+		/* Detect zeroed out tail */
+		if (eap->ea_length < sizeof(*eap) || eap->ea_length == 0) {
+			easize = (const u_char *)eap - eae;
+			break;
+		}
+			
 		eapnext = EXTATTR_NEXT(eap);
-		/* Bogusly short entry or bogusly long entry. */
-		if (eap->ea_length < sizeof(*eap) || eapnext > eaend) {
+		/* Bogusly long entry. */
+		if (eapnext > eaend) {
 			free(eae, M_TEMP);
 			return (EINTEGRITY);
 		}
 	}
+	ip->i_ea_len = easize;
 	*p = eae;
 	return (0);
 }
@@ -1407,7 +1414,6 @@ ffs_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td)
 		ffs_unlock_ea(vp);
 		return (error);
 	}
-	ip->i_ea_len = dp->di_extsize;
 	ip->i_ea_error = 0;
 	ip->i_ea_refs++;
 	ffs_unlock_ea(vp);
@@ -1426,6 +1432,7 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
 	struct ufs2_dinode *dp;
 	size_t ea_len, tlen;
 	int error, i, lcnt;
+	bool truncate;
 
 	ip = VTOI(vp);
 
@@ -1436,6 +1443,7 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
 	}
 	dp = ip->i_din2;
 	error = ip->i_ea_error;
+	truncate = false;
 	if (commit && error == 0) {
 		ASSERT_VOP_ELOCKED(vp, "ffs_close_ea commit");
 		if (cred == NOCRED)
@@ -1452,12 +1460,12 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
 
 		liovec[0].iov_base = ip->i_ea_area;
 		liovec[0].iov_len = ip->i_ea_len;
-		for (i = 1, tlen = ea_len; i < lcnt; i++) {
+		for (i = 1, tlen = ea_len - ip->i_ea_len; i < lcnt; i++) {
 			liovec[i].iov_base = __DECONST(void *, zero_region);
 			liovec[i].iov_len = MIN(ZERO_REGION_SIZE, tlen);
 			tlen -= liovec[i].iov_len;
 		}
-		MPASS(tlen == ip->i_ea_len);
+		MPASS(tlen == 0);
 
 		luio.uio_iov = liovec;
 		luio.uio_offset = 0;
@@ -1466,6 +1474,8 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
 		luio.uio_rw = UIO_WRITE;
 		luio.uio_td = td;
 		error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred);
+		if (error == 0 && ip->i_ea_len == 0)
+			truncate = true;
 	}
 	if (--ip->i_ea_refs == 0) {
 		free(ip->i_ea_area, M_TEMP);
@@ -1475,7 +1485,7 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
 	}
 	ffs_unlock_ea(vp);
 
-	if (commit && error == 0 && ip->i_ea_len == 0)
+	if (truncate)
 		ffs_truncate(vp, 0, IO_EXT, cred);
 	return (error);
 }


More information about the dev-commits-src-main mailing list