]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages()
authorDavid Howells <dhowells@redhat.com>
Tue, 12 May 2026 12:33:56 +0000 (13:33 +0100)
committerChristian Brauner <brauner@kernel.org>
Tue, 12 May 2026 12:42:32 +0000 (14:42 +0200)
netfs_unlock_abandoned_read_pages(rreq) accesses the index of the folios it
is wanting to unlock and compares that to rreq->no_unlock_folio so that it
doesn't unlock a folio being read for netfs_perform_write() or
netfs_write_begin().

However, given that netfs_unlock_abandoned_read_pages() is called _after_
NETFS_RREQ_IN_PROGRESS is cleared, the one folio that it's not allowed to
dereference is the one specified by ->no_unlock_folio as ownership
immediately reverts to the caller.

Fix this by storing the folio pointer instead and using that rather than
the index.  Also fix netfs_unlock_read_folio() where the same applies.

Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading")
Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://patch.msgid.link/20260512123404.719402-20-dhowells@redhat.com
cc: Paulo Alcantara <pc@manguebit.org>
cc: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
cc: Matthew Wilcox <willy@infradead.org>
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/netfs/buffered_read.c
fs/netfs/read_collect.c
fs/netfs/read_retry.c
include/linux/netfs.h

index 004d426c02b41708a309ee03bb97104a3e51fa39..83d0b8153e96e7440b9dae883a2f3ec34571881d 100644 (file)
@@ -670,7 +670,7 @@ retry:
                ret = PTR_ERR(rreq);
                goto error;
        }
-       rreq->no_unlock_folio   = folio->index;
+       rreq->no_unlock_folio   = folio;
        __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
 
        ret = netfs_begin_cache_read(rreq, ctx);
@@ -736,7 +736,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
                goto error;
        }
 
-       rreq->no_unlock_folio = folio->index;
+       rreq->no_unlock_folio = folio;
        __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
        ret = netfs_begin_cache_read(rreq, ctx);
        if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
index 3c9b847885c2a8a46d99033fb671f8e3c3f5dfa1..23660a590124642fb7c1bbcac18a86db45c7c2ad 100644 (file)
@@ -83,7 +83,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq,
        }
 
 just_unlock:
-       if (folio->index == rreq->no_unlock_folio &&
+       if (folio == rreq->no_unlock_folio &&
            test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) {
                _debug("no unlock");
        } else {
index e10eb5a0733260435d480b53af3522acb4faca32..f59a70f3a086b4607172ed2365194f23fc9451c1 100644 (file)
@@ -292,7 +292,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq)
                        struct folio *folio = folioq_folio(p, slot);
 
                        if (folio && !folioq_is_marked2(p, slot)) {
-                               if (folio->index == rreq->no_unlock_folio &&
+                               if (folio == rreq->no_unlock_folio &&
                                    test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO,
                                             &rreq->flags)) {
                                        _debug("no unlock");
index 4fd1d796ad73bd3f89966bbf25163fbb2ad733ef..243c0f737938836135ecd23e54968d610b6e2cfb 100644 (file)
@@ -252,7 +252,7 @@ struct netfs_io_request {
        unsigned long long      collected_to;   /* Point we've collected to */
        unsigned long long      cleaned_to;     /* Position we've cleaned folios to */
        unsigned long long      abandon_to;     /* Position to abandon folios to */
-       pgoff_t                 no_unlock_folio; /* Don't unlock this folio after read */
+       const struct folio      *no_unlock_folio; /* Don't unlock this folio after read */
        unsigned int            direct_bv_count; /* Number of elements in direct_bv[] */
        unsigned int            debug_id;
        unsigned int            rsize;          /* Maximum read size (0 for none) */