From 5e271995d1e7b5e6b3d9972914b5d8f4138125f3 Mon Sep 17 00:00:00 2001 From: bspengler-oss <94915855+bspengler-oss@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:40:41 -0500 Subject: [PATCH] Fix interaction of abd_iter_map()/abd_iter_unmap() with HIGHMEM HIGHMEM kmap interfaces operate on only a single page at a time yet ZFS hadn't accounted for this, resulting in crashes and potential memory corruption on HIGHMEM (typically 32-bit) systems. This was caught by PaX's KERNSEAL feature as it makes use of HIGHMEM functionality on x64. On typical 64-bit systems, this issue wouldn't have been observed, as the map interfaces simply fall back to returning an address in lowmem where the contiguous pages can be accessed directly. Joint work with the PaX Team, tested by Mark van Dijk Reviewed-by: RageLtMan Reviewed-by: Rob Norris Reviewed-by: Brian Behlendorf Signed-off-by: bspengler-oss <94915855+bspengler-oss@users.noreply.github.com> Closes #15668 Closes #18030 --- module/os/linux/zfs/abd_os.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/module/os/linux/zfs/abd_os.c b/module/os/linux/zfs/abd_os.c index 18f2426fb..37d9007a6 100644 --- a/module/os/linux/zfs/abd_os.c +++ b/module/os/linux/zfs/abd_os.c @@ -915,7 +915,14 @@ abd_iter_map(struct abd_iter *aiter) aiter->iter_mapsize = MIN(aiter->iter_sg->length - offset, aiter->iter_abd->abd_size - aiter->iter_pos); - paddr = zfs_kmap_local(sg_page(aiter->iter_sg)); + struct page *page = sg_page(aiter->iter_sg); + if (PageHighMem(page)) { + page = nth_page(page, offset / PAGE_SIZE); + offset &= PAGE_SIZE - 1; + aiter->iter_mapsize = MIN(aiter->iter_mapsize, + PAGE_SIZE - offset); + } + paddr = zfs_kmap_local(page); } aiter->iter_mapaddr = (char *)paddr + offset; @@ -933,8 +940,14 @@ abd_iter_unmap(struct abd_iter *aiter) return; if (!abd_is_linear(aiter->iter_abd)) { + size_t offset = aiter->iter_offset; + + struct page *page = sg_page(aiter->iter_sg); + if (PageHighMem(page)) + offset &= PAGE_SIZE - 1; + /* LINTED E_FUNC_SET_NOT_USED */ - zfs_kunmap_local(aiter->iter_mapaddr - aiter->iter_offset); + zfs_kunmap_local(aiter->iter_mapaddr - offset); } ASSERT3P(aiter->iter_mapaddr, !=, NULL);