]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
libext2fs: fix data corruption when writing to inline data files
authorDarrick J. Wong <djwong@kernel.org>
Thu, 17 Jul 2025 14:59:50 +0000 (07:59 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 31 Jul 2025 14:41:55 +0000 (10:41 -0400)
Fix various bugs in ext2fs_file_write_inline_data:

 - "count = nbytes - pos" makes no sense since nbytes is already a
   length value and results in short writes if pos > 0.
 - Pass the correct file size to ext2fs_inline_data_set because count
   will not be the file size if pos > 0.
 - Simplify the decision to increase the file size.
 - Don't let a huge write corrupt memory beyond file->buf.
 - Zero the buffer between isize and pos if we're doing a sparse write
   past EOF.

Cc: linux-ext4@vger.kernel.org # v1.43
Fixes: 54e880b870f7fe ("libext2fs: handle inline data in read/write function")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Link: https://lore.kernel.org/r/20250717145950.GJ2672022@frogsfrogsfrogs
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
lib/ext2fs/fileio.c

index 900002c529568235df4f4cc121446e30cfd09f40..3a36e9e7fff43b71e02e4e1bdc9dd770a9ad2bfa 100644 (file)
@@ -349,31 +349,42 @@ ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
                              unsigned int nbytes, unsigned int *written)
 {
        ext2_filsys fs;
+       uint64_t old_isize = EXT2_I_SIZE(&file->inode);
+       uint64_t new_isize = old_isize;
        errcode_t retval;
-       unsigned int count = 0;
        size_t size;
 
+       if (file->pos + nbytes > old_isize)
+               new_isize = file->pos + nbytes;
+
        fs = file->fs;
        retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
                                        file->buf, &size);
        if (retval)
                return retval;
 
-       if (file->pos < size) {
-               count = nbytes - file->pos;
-               memcpy(file->buf + file->pos, buf, count);
+       /*
+        * Only try to set new inline data if it won't go past the end of
+        * @file->buf; if there's not enough space in the ondisk inode, we'll
+        * jump out to the expand code.
+        */
+       if (new_isize < fs->blocksize) {
+               if (file->pos > old_isize)
+                       memset(file->buf + old_isize, 0, file->pos - old_isize);
+
+               memcpy(file->buf + file->pos, buf, nbytes);
 
                retval = ext2fs_inline_data_set(fs, file->ino, &file->inode,
-                                               file->buf, count);
+                                               file->buf, new_isize);
                if (retval == EXT2_ET_INLINE_DATA_NO_SPACE)
                        goto expand;
                if (retval)
                        return retval;
 
-               file->pos += count;
+               file->pos += nbytes;
 
                /* Update inode size */
-               if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
+               if (old_isize < new_isize) {
                        errcode_t       rc;
 
                        rc = ext2fs_file_set_size2(file, file->pos);
@@ -382,7 +393,7 @@ ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
                }
 
                if (written)
-                       *written = count;
+                       *written = nbytes;
                return 0;
        }