When a network filesystem using folio_queue-backed iterators (e.g.
ceph or NFS with folioq read paths) hits a short read and calls
iov_iter_revert() to wind the iterator back, the revert walks backwards
through the folio_queue linked list via folioq->prev. If the unroll
amount exceeds the data preceding the current position — possible when
a splice or sendfile operation miscalculates the revert distance after
a partial transfer — the walk reaches the head of the queue where
prev is NULL, and the subsequent folioq_nr_slots(NULL) dereferences it.
This was found through code review examining the revert paths: the
bvec and iovec revert loops have the same class of unbounded backward
walk, but the folioq case is the easiest to reach in practice because
folio_queue chains have an explicit NULL-terminated prev pointer.
Add a NULL check and early return when folioq->prev is NULL to prevent
the oops.
Signed-off-by: Josh Law <objecting@objecting.org>
---
lib/iov_iter.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 852f9ed40e5c..325669b103ed 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -600,6 +600,8 @@ static void iov_iter_folioq_revert(struct iov_iter *i, size_t unroll)
size_t fsize;
if (slot == 0) {
+ if (!folioq->prev)
+ return;
folioq = folioq->prev;
slot = folioq_nr_slots(folioq);
}
--
2.34.1