diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 20f5d301d32d..8de950c63dc3 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -338,6 +338,23 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) extraction_flags, &offset); if (unlikely(ret < 0)) goto free_bvec; + if (unlikely(ret != bytes)) { + /* + * Not all pages could be pinned. This can happen when + * pin_user_pages_fast() returns fewer pages than requested. + * All pages must be pinned to copy user data, so unpin + * whatever we got and fail. + */ + int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); + int i; + + for (i = 0; i < npinned; i++) + unpin_user_page(pages[i]); + if (pages != stack_pages) + kvfree(pages); + ret = -EFAULT; + goto free_bvec; + } nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, &is_p2p);