From bdbf01fdc8f2d34825f5c8a6663d7a77aa7132b3 Mon Sep 17 00:00:00 2001 From: RageLtMan Date: Wed, 13 Dec 2023 16:33:38 -0500 Subject: [PATCH] Upstream OSS fix for highmem violations by kmap During testing of the most recent grsec patchset utilizing strict kmap checks, zfs_uiomove_bvec_impl was found to be capable of kmap-ing more than PAGE_SIZE. Upstream x64 allows this in that it does not crash outright, x86 likely doesn't. This commit addresses the zfs_uiomove_bvec_impl condition, but there may be other such violations such as zfs_copy_bvec directly below in zfs_uio.c which may require a similar approach forcing page-sized kmap calls within a loop. Tests and a test-driven audit would be helpful to find all of these conditions as they are somewhat arbitrarily triggered and the same codepath can work correctly or crash being > PAGE_SIZE. Credits: Open Source Security (Spender, probably Pipacs or Minipli too) Testing: Internal VM tests with ZFS send/recv which used to hard-crash the machine are now completing successfully. Notes: The crash condition didn't occur in the first set of ztests run during evaluation of the 6.6 patchset. --- module/os/linux/zfs/zfs_uio.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/module/os/linux/zfs/zfs_uio.c b/module/os/linux/zfs/zfs_uio.c index c2ed67c438c6..c7a9e0c94465 100644 --- a/module/os/linux/zfs/zfs_uio.c +++ b/module/os/linux/zfs/zfs_uio.c @@ -134,15 +134,16 @@ zfs_uiomove_bvec_impl(void *p, size_t n, zfs_uio_rw_t rw, zfs_uio_t *uio) while (n && uio->uio_resid) { void *paddr; - cnt = MIN(bv->bv_len - skip, n); + size_t offset = bv->bv_offset + skip; + cnt = MIN(PAGE_SIZE - (offset & ~PAGE_MASK), MIN(bv->bv_len - skip, n)); - paddr = zfs_kmap_atomic(bv->bv_page); + paddr = zfs_kmap_atomic(bv->bv_page + (offset >> PAGE_SHIFT)); if (rw == UIO_READ) { /* Copy from buffer 'p' to the bvec data */ - memcpy(paddr + bv->bv_offset + skip, p, cnt); + memcpy(paddr + (offset & ~PAGE_MASK), p, cnt); } else { /* Copy from bvec data to buffer 'p' */ - memcpy(p, paddr + bv->bv_offset + skip, cnt); + memcpy(p, paddr + (offset & ~PAGE_MASK), cnt); } zfs_kunmap_atomic(paddr); @@ -171,10 +172,10 @@ zfs_copy_bvec(void *p, size_t skip, size_t cnt, zfs_uio_rw_t rw, paddr = zfs_kmap_atomic(bv->bv_page); if (rw == UIO_READ) { /* Copy from buffer 'p' to the bvec data */ - memcpy(paddr + bv->bv_offset + skip, p, cnt); + memcpy(paddr + bv->bv_offset + skip, p, cnt); } else { /* Copy from bvec data to buffer 'p' */ - memcpy(p, paddr + bv->bv_offset + skip, cnt); + memcpy(p, paddr + bv->bv_offset + skip, cnt); } zfs_kunmap_atomic(paddr); }