[PATCH 2/7] lib/iov_iter: add NULL check on folioq->prev in iov_iter_folioq_revert()

Josh Law posted 7 patches 3 weeks, 3 days ago
[PATCH 2/7] lib/iov_iter: add NULL check on folioq->prev in iov_iter_folioq_revert()
Posted by Josh Law 3 weeks, 3 days ago
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