lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251210081558.2714709-1-libaokun@huaweicloud.com>
Date: Wed, 10 Dec 2025 16:15:58 +0800
From: libaokun@...weicloud.com
To: linux-ext4@...r.kernel.org
Cc: tytso@....edu,
	adilger.kernel@...ger.ca,
	jack@...e.cz,
	yangerkun@...wei.com,
	libaokun1@...wei.com,
	libaokun@...weicloud.com
Subject: [PATCH e2fsprogs] lib/quota: fix checksum mismatch on uninitialized PRJQUOTA inode

From: Baokun Li <libaokun1@...wei.com>

In quota_inode_init_new(), we attempt to read and truncate an existing
quota inode before proceeding with its initialization.

This read operation verifies the inode's checksum. This works fine for
USRQUOTA and GRPQUOTA inodes because write_reserved_inodes() is always
called during ext4 image creation to set appropriate checksums for these
reserved inodes.

However, the PRJQUOTA inode is not reserved, and its corresponding inode
table block may not have been zeroed, potentially containing stale data.
Consequently, reading this inode can fail due to a checksum mismatch.

This can be reproduced by running the following sequence:

  dd if=/dev/random of=$DISK bs=1M count=128
  mkfs.ext4 -F -q -b 1024 $DISK 5G
  tune2fs -O quota,project $DISK

Which results in the following error output:

 tune2fs 1.47.3 (8-Jul-2025)
 [ERROR] quotaio.c:279:quota_inode_init_new: ex2fs_read_inode failed
 [ERROR] quotaio.c:341:quota_file_create: init_new_quota_inode failed
 tune2fs: Inode checksum does not match inode while writing quota file (2)

While running `kvm-xfstests -c ext4/1k -C 1 generic/383`, the test itself
does not fail, but checksum verification failures are reported even
without fault injection, which led to discovering this issue.

To fix this, we stop attempting to read the quota inode that is about
to be initialized inside quota_inode_init_new(). Instead, the logic
to attempt truncation of an existing quota inode is moved to be handled
inside quota_file_create().

Fixes: 080e09b4 ("Add project quota support")
Signed-off-by: Baokun Li <libaokun1@...wei.com>
---
 lib/support/quotaio.c | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
index f5f2c7f7..827df85b 100644
--- a/lib/support/quotaio.c
+++ b/lib/support/quotaio.c
@@ -274,18 +274,6 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
 	errcode_t err = 0;
 	time_t now;
 
-	err = ext2fs_read_inode(fs, ino, &inode);
-	if (err) {
-		log_err("ex2fs_read_inode failed");
-		return err;
-	}
-
-	if (EXT2_I_SIZE(&inode)) {
-		err = quota_inode_truncate(fs, ino);
-		if (err)
-			return err;
-	}
-
 	memset(&inode, 0, sizeof(struct ext2_inode));
 	ext2fs_iblk_set(fs, &inode, 0);
 	now = fs->now ? fs->now : time(0);
@@ -319,6 +307,10 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
 	if (fmt == -1)
 		fmt = QFMT_VFS_V1;
 
+	err = ext2fs_read_bitmaps(fs);
+	if (err)
+		goto out_err;
+
 	h->qh_qf.fs = fs;
 	qf_inum = quota_type2inum(qtype, fs->super);
 	if (qf_inum == 0 && qtype == PRJQUOTA) {
@@ -330,15 +322,19 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
 		ext2fs_mark_ib_dirty(fs);
 	} else if (qf_inum == 0) {
 		return EXT2_ET_BAD_INODE_NUM;
+	} else {
+		err = quota_inode_truncate(fs, qf_inum);
+		if (err) {
+			log_err("quota_inode_truncate failed, ino=%u, type=%d",
+				qf_inum, qtype);
+			return err;
+		}
 	}
 
-	err = ext2fs_read_bitmaps(fs);
-	if (err)
-		goto out_err;
-
 	err = quota_inode_init_new(fs, qf_inum);
 	if (err) {
-		log_err("init_new_quota_inode failed");
+		log_err("init_new_quota_inode failed, ino=%u, type=%d",
+			qf_inum, qtype);
 		goto out_err;
 	}
 	h->qh_qf.ino = qf_inum;
-- 
2.46.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ