public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: syzbot <syzbot+ed8bc247f231c1a48e21@syzkaller.appspotmail.com>
To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com
Subject: Forwarded: [PATCH] ext4: fix NULL page dereference in ext4_bio_write_folio() with large folios
Date: Sat, 21 Mar 2026 01:36:29 -0700	[thread overview]
Message-ID: <69be588d.050a0220.3bf4de.0046.GAE@google.com> (raw)
In-Reply-To: <69bdcdcd.050a0220.3bf4de.0030.GAE@google.com>

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] ext4: fix NULL page dereference in ext4_bio_write_folio() with large folios
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master


When blocksize < PAGE_SIZE, a folio can span multiple pages with
multiple buffer heads. ext4_bio_write_folio() encrypted the entire
folio once with offset=0 via fscrypt_encrypt_pagecache_blocks(),
which always returns a single bounce page covering only the first
page of the folio.

When the write loop iterated over buffer heads beyond the first page,
bio_add_folio() calculated nr = bh_offset(bh) / PAGE_SIZE which was
non-zero for bh's on subsequent pages. folio_page(io_folio, nr) then
went out of bounds on the single page bounce folio, returning a NULL
or garbage page pointer, causing a NULL pointer dereference in
bvec_set_page().

Fix this by moving the encryption inside the write loop and encrypting
per buffer head using the correct offset within the folio via
offset_in_folio(folio, bh->b_data). Each buffer head now gets its own
bounce page at index 0, so folio_page(io_folio, 0) is always valid.

The existing retry logic for -ENOMEM is preserved.

Reported-by: syzbot+ed8bc247f231c1a48e21@syzkaller.appspotmail.com
Signed-off-by: Deepanshu kartikey <Kartikey406@gmail.com>
---
 fs/ext4/page-io.c | 87 +++++++++++++++++++++++------------------------
 1 file changed, 43 insertions(+), 44 deletions(-)

diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index a8c95eee91b7..d7114171cd52 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -537,56 +537,55 @@ int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio,
 	 * (e.g. holes) to be unnecessarily encrypted, but this is rare and
 	 * can't happen in the common case of blocksize == PAGE_SIZE.
 	 */
-	if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
-		gfp_t gfp_flags = GFP_NOFS;
-		unsigned int enc_bytes = round_up(len, i_blocksize(inode));
-		struct page *bounce_page;
-
-		/*
-		 * Since bounce page allocation uses a mempool, we can only use
-		 * a waiting mask (i.e. request guaranteed allocation) on the
-		 * first page of the bio.  Otherwise it can deadlock.
-		 */
-		if (io->io_bio)
-			gfp_flags = GFP_NOWAIT;
-	retry_encrypt:
-		bounce_page = fscrypt_encrypt_pagecache_blocks(folio,
-					enc_bytes, 0, gfp_flags);
-		if (IS_ERR(bounce_page)) {
-			ret = PTR_ERR(bounce_page);
-			if (ret == -ENOMEM &&
-			    (io->io_bio || wbc->sync_mode == WB_SYNC_ALL)) {
-				gfp_t new_gfp_flags = GFP_NOFS;
-				if (io->io_bio)
-					ext4_io_submit(io);
-				else
-					new_gfp_flags |= __GFP_NOFAIL;
-				memalloc_retry_wait(gfp_flags);
-				gfp_flags = new_gfp_flags;
-				goto retry_encrypt;
-			}
-
-			printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret);
-			folio_redirty_for_writepage(wbc, folio);
-			do {
-				if (buffer_async_write(bh)) {
-					clear_buffer_async_write(bh);
-					set_buffer_dirty(bh);
-				}
-				bh = bh->b_this_page;
-			} while (bh != head);
-
-			return ret;
-		}
-		io_folio = page_folio(bounce_page);
-	}
-
 	__folio_start_writeback(folio, keep_towrite);
 
 	/* Now submit buffers to write */
 	do {
 		if (!buffer_async_write(bh))
 			continue;
+		if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
+			gfp_t gfp_flags = GFP_NOFS;
+			struct page *bounce_page;
+			/*
+			 * Since bounce page allocation uses a mempool, we can
+			 * only use a waiting mask (i.e. request guaranteed
+			 * allocation) on the first page of the bio.
+			 * Otherwise it can deadlock.
+			 */
+			if (io->io_bio)
+				gfp_flags = GFP_NOWAIT;
+		retry_encrypt:
+			bounce_page = fscrypt_encrypt_pagecache_blocks(folio,
+						bh->b_size,
+						offset_in_folio(folio, bh->b_data),
+						gfp_flags);
+			if (IS_ERR(bounce_page)) {
+				ret = PTR_ERR(bounce_page);
+				if (ret == -ENOMEM &&
+				    (io->io_bio || wbc->sync_mode == WB_SYNC_ALL)) {
+					gfp_t new_gfp_flags = GFP_NOFS;
+					if (io->io_bio)
+						ext4_io_submit(io);
+					else
+						new_gfp_flags |= __GFP_NOFAIL;
+					memalloc_retry_wait(gfp_flags);
+					gfp_flags = new_gfp_flags;
+					goto retry_encrypt;
+				}
+				printk_ratelimited(KERN_ERR "%s: ret = %d\n",
+						   __func__, ret);
+				folio_redirty_for_writepage(wbc, folio);
+				do {
+					if (buffer_async_write(bh)) {
+						clear_buffer_async_write(bh);
+						set_buffer_dirty(bh);
+					}
+					bh = bh->b_this_page;
+				} while (bh != head);
+				return ret;
+			}
+			io_folio = page_folio(bounce_page);
+		}
 		io_submit_add_bh(io, inode, folio, io_folio, bh);
 	} while ((bh = bh->b_this_page) != head);
 
-- 
2.43.0


  reply	other threads:[~2026-03-21  8:36 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-20 22:44 [syzbot] [block?] general protection fault in bio_add_page syzbot
2026-03-21  8:36 ` syzbot [this message]
2026-03-21 12:15 ` Forwarded: [PATCH] ext4: fix general protection fault in bio_add_page for encrypted large folios syzbot
2026-03-22  2:14 ` Forwarded: [PATCH] ext4: fix null-ptr-deref in bio_add_folio syzbot
2026-03-22  4:41 ` Forwarded: [PATCH] blktrace: reject buf_size smaller than struct blk_io_trace syzbot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=69be588d.050a0220.3bf4de.0046.GAE@google.com \
    --to=syzbot+ed8bc247f231c1a48e21@syzkaller.appspotmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=syzkaller-bugs@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox