From be584b115707be7a7c32d1c2c6a44be4978a2acd Mon Sep 17 00:00:00 2001 From: Horst Birthelmer Date: Thu, 11 Jun 2026 08:42:11 +0200 Subject: [PATCH] fuse: don't leak AOP_TRUNCATED_PAGE from fuse_write_begin() fuse_do_readpage() may return AOP_TRUNCATED_PAGE (a positive value) when the daemon fails its DLM lock acquisition with -EAGAIN during an in-flight invalidation. fuse_read_folio() is prepared for that, but fuse_write_begin() forwarded it to generic_perform_write(), which only treats negative returns as errors and went on to use the never-initialized page pointer: the user copy is silently fixed up as a 0-byte short copy and fuse_write_end() then oopses in unlock_page(NULL). Retry the page grab and read inside fuse_write_begin() instead, mirroring the read-side retry done by filemap_fault(). Fixes: 8ecf11820538 ("fuse: Allow read_folio to retry page fault and read operations") Signed-off-by: Horst Birthelmer --- fs/fuse/file.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 56303193e8e07b..55ab5e9cf61715 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2252,10 +2252,12 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, struct fuse_conn *fc = get_fuse_conn(file_inode(file)); struct page *page; loff_t fsize; - int err = -ENOMEM; + int err; WARN_ON(!fc->writeback_cache); +retry: + err = -ENOMEM; page = grab_cache_page_write_begin(mapping, index); if (!page) goto error; @@ -2285,6 +2287,13 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, cleanup: unlock_page(page); put_page(page); + /* + * ->write_begin has no AOP_TRUNCATED_PAGE contract; a positive + * return would pass the "status < 0" check in + * generic_perform_write() and crash on an unset *pagep. + */ + if (err == AOP_TRUNCATED_PAGE) + goto retry; error: return err; }