git: ca39f23347e1 - main - ufs: do not leave around empty buffers shadowing disk content

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 15 Dec 2023 22:09:19 UTC
The branch main has been updated by kib:

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

commit ca39f23347e1416a28dde13279bfe5841ad9a746
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2023-12-11 22:57:28 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2023-12-15 22:06:10 +0000

    ufs: do not leave around empty buffers shadowing disk content
    
    If the ffs_write() operation specified to overwrite the whole buffer,
    ffs tries to save the read by not validating allocated buffer. Then
    uiommove() might fail with EFAULT, in which case pages are left zeroed
    and marked valid but not read from the disk. Then vn_io_fault() logic
    retries the write after holding the user pages to avoid EFAULTs. In
    erronous case of really faulty buffer, or in contrived case of writing
    from file to itself, we are left with zeroed buffer instead of valid
    content written back to disk.
    
    Handle the situation by releasing non-cached buffer on fault, instead
    of clearing it. Note that buffers with alive dependencies cannot be
    released, but also either they cannot have valid content on the disk
    because dependency on data buffer means that it was not yet written, or
    they were reallocated by fragment extension or ffs_reallocbks(), and are
    already fully valid.
    
    Reported by:    kevans
    Discussed with: mav
    In collaboration with:  pho
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
---
 sys/ufs/ffs/ffs_vnops.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index 0a327aab155b..e9849008cde2 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -978,8 +978,15 @@ ffs_write(
 		 * validated the pages.
 		 */
 		if (error != 0 && (bp->b_flags & B_CACHE) == 0 &&
-		    fs->fs_bsize == xfersize)
-			vfs_bio_clrbuf(bp);
+		    fs->fs_bsize == xfersize) {
+			if (error == EFAULT && LIST_EMPTY(&bp->b_dep)) {
+				bp->b_flags |= B_INVAL | B_RELBUF | B_NOCACHE;
+				brelse(bp);
+				break;
+			} else {
+				vfs_bio_clrbuf(bp);
+			}
+		}
 
 		vfs_bio_set_flags(bp, ioflag);