From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60341265CC2 for ; Tue, 9 Jun 2026 04:17:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780978656; cv=none; b=e4x5/O+sxfZHRsTEoFkdUMt+u/PlSkxHoD65uNe5s74wAXTamWXjtxC7Ip5uQtkMYG4DU9Hf8XA6ZS6CI5NVgvBgTy/aH7FwuNiRRas28Y7jos2reb04Weuu1ZBjsnK8aEN382UqnIGjU078avd2HsbUgDC/Qco+d3j1f9ItxoY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780978656; c=relaxed/simple; bh=U4Np9Ii9wtElKPYq5/Att3d025QkxteJXmh4QQc94mc=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=GzRZzFut+wgHNmU+M3DLSrae0LzD9DpwMI+50X3T8w0RBrPJzGaXaue46EQu4vmawoMkxWTSBbSccWWl9Mrlxq5GzuQSu10TIrpC1j9juC1dxonFJCVhrVWIT0aJpDhd5C0IkB8suyG+91JKx6rllBrzZY5hBv/K13yPPi7H9h0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ZcJTYbNM; arc=none smtp.client-ip=209.85.214.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZcJTYbNM" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-2bf2e8ccca1so35755325ad.0 for ; Mon, 08 Jun 2026 21:17:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780978653; x=1781583453; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=q406pH5lUADnbt61DxG138tUfOLYKP8K5yUgAEXHh5w=; b=ZcJTYbNME++VZEHgAwsJFlkKFtxJL0POKkbjHAqrsTEoIVJBHcpx3rP/p6FitVUQKs ATiWIQ04W2ufoan+N+l0+1ZRvwOjuStV64VzcNViTES1eL9tf+4YQKi6LKoq8R8xh08C i5abNhwZUzJsvbg26lGOaU+qH1OkaSlFrR1AoBh0UO8f4KTIF+4oDqBygB9NyS4RRRaH +7olvecBlprSD8fu2pdWS/Ry/TmMF4N1P9vp/T6fcYVIXhRQYCUVn+hzPAB+weBHJdy0 jGq464bYWJ2nD4GViEWTjBr2/aBzxOQCG74EyIXjtH8qNbyEijd4yfw2CiccgZIPsxUe DqOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780978653; x=1781583453; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=q406pH5lUADnbt61DxG138tUfOLYKP8K5yUgAEXHh5w=; b=l07cCdE9wocO3vh7//lKBr4lgH0NziOwYoJcLjZrpU+IzsHu2jc4UVXyIBXscdRxhS ErjZgau9zEDSYbeuh3qmnZ3S/ZAM8uIW+mPw9SHGnA+1xZebYegvUpeLb6e6MZiipYBM p4OUTUx5fRB1s3deMqS4zcph6SqK5CdS8xCHGcEud6AtaijEote6izXPK4Jf5qr1NFlO eEV4BdvnNrZ8thuF6D+/VHKtbVPKRob45UjH1a8q2TSHVbG2FMBp3gRnnz8kFDppNQ7n iG8/rmh2Pwf1JSdt0vSp4sxH/B4bVmNbLweGwh+kfZSukyq24zP4OT7Bfn/rYuCcmfrl 4wOA== X-Forwarded-Encrypted: i=1; AFNElJ9Uh2D9+KGe9ONBQA3G8vqfV/6HKvTFaDsdO/FN0sZLNqJY+b8rYVZB6/xR0iZc777zZaMt+ePVM9YsHRA=@vger.kernel.org X-Gm-Message-State: AOJu0Ywt1CBg+E4AfLxRYtG/w9bBNVX6lNYF+xKlNU4ODP02miFnTW1+ UlYWJnDhM3uDmk1TB3svVHSz72ubpcDqwywBOTr3Puv4KR1183hZor6va0wfVNhDZVRrVw== X-Gm-Gg: Acq92OEODtdO65CfTO3CCJ+93EywcJMlqTqiwpGZVKfM1+8bhpUATy31e7SNyybjKYJ 7wJmOzsL2esX8Co1gmDfMDKG/6u/9hx+0hmxQ3rT2mUJ1e8eLZr4myR3sUImfoQvSF69Ml8pzmG DyVfZPCNwChJdq216gi+4WAcDOt5QcKY/rXkgQ0ea35NAXa69e0CCw5EPrGKWyY70HxGkLsu+vm 7nyANyHYOzItrJ4Rm/FzQMxU7RKrTKT8x/2/5hQHhJr4lcUhX4guIlUB84HgP1qldA2TZxE8uSW +rrIW6TqaEVnxhiOWED+hchNRTriUxW3fCFBaKDUaVMeryd27A2eNTEdfnPN8G8R/VAoYrh2hCl dqQs5RbNINrEPaw+016ilYg4UmoDYwovpO7jxezPhzq04hbygx3ciC4yW2yozVGR2njbqtP4Y9p ANsilFAxOhTn3ALcjexbbqH8y+dWJL/pvdbMMV/oES6cXhFmH2ln+NYroD8y7/aM/l5dIW9/wKs gvDHArJuX6N9LI7qiZQN6kZmULLOxIDilho+TdBkKt5Vtyxxm4wb++rlXg+7xXZ9fRGelgPA8eP n60U3hbQPxuucMu9E3hEhzPuNzEYGvKkyhdIt4EKXVtfpYBKHA== X-Received: by 2002:a17:903:2450:b0:2c1:27a3:6859 with SMTP id d9443c01a7336-2c1e79e22dcmr216110905ad.7.1780978652716; Mon, 08 Jun 2026 21:17:32 -0700 (PDT) Received: from cs-1047136853211-default.asia-southeast1-b.c.d33bddc1d573818c7-tp.internal (192.152.126.34.bc.googleusercontent.com. [34.126.152.192]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c249aeb62csm74641005ad.68.2026.06.08.21.17.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jun 2026 21:17:32 -0700 (PDT) From: Aditya Prakash Srivastava To: Theodore Ts'o , Andreas Dilger Cc: Jan Kara , Baokun Li , Ojaswin Mujoo , Ritesh Harjani , Zhang Yi , sashiko-reviews@lists.linux.dev, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, Aditya Prakash Srivastava , syzbot+0c89d865531d053abb2d@syzkaller.appspotmail.com Subject: [PATCH v2] ext4: fix kernel BUG in ext4_write_inline_data_end Date: Tue, 9 Jun 2026 04:17:03 +0000 Message-ID: <20260609041704.1709-1-aditya.ansh182@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit When the data=journal mount option is used, the ext4_journalled_write_end() function incorrectly calls ext4_write_inline_data_end() without checking if the EXT4_STATE_MAY_INLINE_DATA flag is still set on the inode. If a previous attempt to convert the inline data to an extent failed (e.g. due to ENOSPC), the EXT4_STATE_MAY_INLINE_DATA flag is cleared, but the EXT4_INODE_INLINE_DATA flag remains set. In this scenario, the next call to ext4_write_begin() will not prepare the inline data xattr for writing, but ext4_journalled_write_end() will incorrectly attempt to write to it, triggering a BUG_ON(pos + len > EXT4_I(inode)->i_inline_size) in ext4_write_inline_data() since i_inline_size was not expanded. Additionally, a concurrent ext4_page_mkwrite() can execute ext4_convert_inline_data() between ext4_write_begin() and ext4_write_end() since it only takes xattr_sem. This concurrent path converts the inline data to an extent and clears both the EXT4_INODE_INLINE_DATA and EXT4_STATE_MAY_INLINE_DATA flags. Since the block buffers were not allocated in ext4_write_begin(), falling through to the block fallback path causes a NULL pointer dereference because folio_buffers(folio) is NULL. Fix this by ensuring that the write_end functions (including ext4_write_end() and ext4_da_do_write_end()) safely return 0 to trigger a VFS retry if they detect folio_buffers(folio) is NULL after falling through from the inline data check. Returning 0 signals generic_perform_write() to revert the write iterator and retry the entire write operation, which will then properly allocate block buffers. Reported-by: syzbot+0c89d865531d053abb2d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0c89d865531d053abb2d Fixes: 3fdcfb668fd7 ("ext4: add journalled write support for inline data") Signed-off-by: Aditya Prakash Srivastava --- v2: - Address TOCTOU race condition (reported by Sashiko AI review): A concurrent ext4_page_mkwrite() can execute ext4_convert_inline_data() which takes xattr_sem and converts the inline data to extents, clearing both EXT4_INODE_INLINE_DATA and EXT4_STATE_MAY_INLINE_DATA flags between ext4_write_begin() and write_end(). - Safely return 0 from write_end functions if folio_buffers(folio) is NULL to trigger a VFS retry. This prevents a NULL pointer dereference in ext4_journalled_zero_new_buffers() and ext4_walk_page_buffers(). - Add check for ext4_write_end() and ext4_da_do_write_end(). fs/ext4/inode.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c2c2d6ac7f3d..d6c934b2ae31 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1455,6 +1455,13 @@ static int ext4_write_end(const struct kiocb *iocb, return ext4_write_inline_data_end(inode, pos, len, copied, folio); + if (unlikely(!folio_buffers(folio))) { + folio_unlock(folio); + folio_put(folio); + ext4_journal_stop(handle); + return 0; + } + copied = block_write_end(pos, len, copied, folio); /* * it's important to update i_size while still holding folio lock: @@ -1560,10 +1567,18 @@ static int ext4_journalled_write_end(const struct kiocb *iocb, BUG_ON(!ext4_handle_valid(handle)); - if (ext4_has_inline_data(inode)) + if (ext4_has_inline_data(inode) && + ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) return ext4_write_inline_data_end(inode, pos, len, copied, folio); + if (unlikely(!folio_buffers(folio))) { + folio_unlock(folio); + folio_put(folio); + ext4_journal_stop(handle); + return 0; + } + if (unlikely(copied < len) && !folio_test_uptodate(folio)) { copied = 0; ext4_journalled_zero_new_buffers(handle, inode, folio, @@ -3231,7 +3246,7 @@ static int ext4_da_do_write_end(struct address_space *mapping, if (unlikely(!folio_buffers(folio))) { folio_unlock(folio); folio_put(folio); - return -EIO; + return 0; } /* * block_write_end() will mark the inode as dirty with I_DIRTY_PAGES -- 2.47.3