Skip to content

Commit e65a0dc

Browse files
dhowellsbrauner
authored andcommitted
iov_iter: Fix iov_iter_get_pages*() for folio_queue
p9_get_mapped_pages() uses iov_iter_get_pages_alloc2() to extract pages from an iterator when performing a zero-copy request and under some circumstances, this crashes with odd page errors[1], for example, I see: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xbcf0 flags: 0x2000000000000000(zone=1) ... page dumped because: VM_BUG_ON_FOLIO(((unsigned int) folio_ref_count(folio) + 127u <= 127u)) ------------[ cut here ]------------ kernel BUG at include/linux/mm.h:1444! This is because, unlike in iov_iter_extract_folioq_pages(), the iter_folioq_get_pages() helper function doesn't skip the current folio when iov_offset points to the end of it, but rather extracts the next page beyond the end of the folio and adds it to the list. Reading will then clobber the contents of this page, leading to system corruption, and if the page is not in use, put_page() may try to clean up the unused page. This can be worked around by copying the iterator before each extraction[2] and using iov_iter_advance() on the original as the advance function steps over the page we're at the end of. Fix this by skipping the page extraction if we're at the end of the folio. This was reproduced in the ktest environment[3] by forcing 9p to use the fscache caching mode and then reading a file through 9p. Fixes: db0aa2e ("mm: Define struct folio_queue and ITER_FOLIOQ to handle a sequence of folios") Reported-by: Antony Antony <[email protected]> Closes: https://lore.kernel.org/r/[email protected]/ Signed-off-by: David Howells <[email protected]> cc: Eric Van Hensbergen <[email protected]> cc: Latchesar Ionkov <[email protected]> cc: Dominique Martinet <[email protected]> cc: Christian Schoenebeck <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] Link: https://lore.kernel.org/r/[email protected]/ [1] Link: https://lore.kernel.org/r/[email protected]/ [2] Link: https://github.com/koverstreet/ktest.git [3] Tested-by: Antony Antony <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 247d65f commit e65a0dc

File tree

1 file changed

+12
-9
lines changed

1 file changed

+12
-9
lines changed

lib/iov_iter.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,15 +1021,18 @@ static ssize_t iter_folioq_get_pages(struct iov_iter *iter,
10211021
size_t offset = iov_offset, fsize = folioq_folio_size(folioq, slot);
10221022
size_t part = PAGE_SIZE - offset % PAGE_SIZE;
10231023

1024-
part = umin(part, umin(maxsize - extracted, fsize - offset));
1025-
count -= part;
1026-
iov_offset += part;
1027-
extracted += part;
1028-
1029-
*pages = folio_page(folio, offset / PAGE_SIZE);
1030-
get_page(*pages);
1031-
pages++;
1032-
maxpages--;
1024+
if (offset < fsize) {
1025+
part = umin(part, umin(maxsize - extracted, fsize - offset));
1026+
count -= part;
1027+
iov_offset += part;
1028+
extracted += part;
1029+
1030+
*pages = folio_page(folio, offset / PAGE_SIZE);
1031+
get_page(*pages);
1032+
pages++;
1033+
maxpages--;
1034+
}
1035+
10331036
if (maxpages == 0 || extracted >= maxsize)
10341037
break;
10351038

0 commit comments

Comments
 (0)