]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iomap: avoid potential null folio->mapping deref during error reporting
authorJoanne Koong <joannelkoong@gmail.com>
Thu, 4 Jun 2026 01:18:58 +0000 (18:18 -0700)
committerChristian Brauner <brauner@kernel.org>
Thu, 4 Jun 2026 08:02:56 +0000 (10:02 +0200)
When a buffered read fails, iomap_finish_folio_read() reports the error
with fserror_report_io(folio->mapping->host, ...). This is called after
ifs->read_bytes_pending has been decremented by the bytes attempted to
be read.

For a folio split across multiple read completions, the folio is only
guaranteed to stay locked while read_bytes_pending > 0. Once
iomap_finish_folio_read() decrements read_bytes_pending, another
in-flight read can complete and end the read on the folio, which unlocks
it. This allows truncate logic to run and detach the folio (set
folio->mapping to NULL). The error reporting path then can dereference a
NULL folio->mapping. As reported by Sam Sun, this is the race that can
occur:

CPU0: failed completion      CPU1: final completion     CPU2: truncate
-----------------------      ----------------------     --------------
read_bytes_pending -= len
finished = false
/* preempted before
   fserror_report_io() */
     read_bytes_pending -= len
     finished = true
     folio_end_read()
truncate clears
folio->mapping
fserror_report_io(
  folio->mapping->host, ...)
      ^ NULL deref

Fix this by reporting the error first before decrementing
ifs->read_bytes_pending.

Fixes: a9d573ee88af ("iomap: report file I/O errors to the VFS")
Cc: stable@vger.kernel.org
Reported-by: Sam Sun <samsun1006219@gmail.com>
Closes: https://lore.kernel.org/linux-fsdevel/CAEkJfYPhWdd59RKmuNLJg-bkypHz7xiOwaWyNVu3A8CUqQCnvg@mail.gmail.com/
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Link: https://patch.msgid.link/20260604011858.2297561-1-joannelkoong@gmail.com
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/iomap/buffered-io.c

index d7b648421a70fa52fe70a379990458d67647332e..d55b936e69862856331a272f8a44b4a985513d05 100644 (file)
@@ -400,6 +400,11 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,
        bool uptodate = !error;
        bool finished = true;
 
+       if (error)
+               fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ,
+                                 folio_pos(folio) + off, len, error,
+                                 GFP_ATOMIC);
+
        if (ifs) {
                unsigned long flags;
 
@@ -411,11 +416,6 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,
                spin_unlock_irqrestore(&ifs->state_lock, flags);
        }
 
-       if (error)
-               fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ,
-                                 folio_pos(folio) + off, len, error,
-                                 GFP_ATOMIC);
-
        if (finished)
                folio_end_read(folio, uptodate);
 }