The pinned PFN list returned from vfio_pin_pages() is simply converted
using page_to_pfn() without protection, so direct access via memcpy()
will crash on S390 if the PFN is an IO PFN. Instead, the pages should
be touched using kmap_local_page().

Add kmap_local_page() before doing memcpy on "from".

Suggested-by: Jason Gunthorpe <j...@nvidia.com>
Signed-off-by: Nicolin Chen <nicol...@nvidia.com>
---
 drivers/s390/cio/vfio_ccw_cp.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c
index e2b01115b3ec..12cbe66721af 100644
--- a/drivers/s390/cio/vfio_ccw_cp.c
+++ b/drivers/s390/cio/vfio_ccw_cp.c
@@ -11,6 +11,7 @@
 #include <linux/ratelimit.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
+#include <linux/highmem.h>
 #include <linux/iommu.h>
 #include <linux/vfio.h>
 #include <asm/idals.h>
@@ -235,7 +236,6 @@ static long copy_from_iova(struct vfio_device *vdev, void 
*to, u64 iova,
                           unsigned long n)
 {
        struct pfn_array pa = {0};
-       u64 from;
        int i, ret;
        unsigned long l, m;
 
@@ -251,7 +251,9 @@ static long copy_from_iova(struct vfio_device *vdev, void 
*to, u64 iova,
 
        l = n;
        for (i = 0; i < pa.pa_nr; i++) {
-               from = pa.pa_pfn[i] << PAGE_SHIFT;
+               struct page *page = pfn_to_page(pa.pa_pfn[i]);
+               void *from = kmap_local_page(page);
+
                m = PAGE_SIZE;
                if (i == 0) {
                        from += iova & (PAGE_SIZE - 1);
@@ -259,7 +261,8 @@ static long copy_from_iova(struct vfio_device *vdev, void 
*to, u64 iova,
                }
 
                m = min(l, m);
-               memcpy(to + (n - l), (void *)from, m);
+               memcpy(to + (n - l), from, m);
+               kunmap_local(from);
 
                l -= m;
                if (l == 0)
-- 
2.17.1

Reply via email to