Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ce386181 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull vfs fixes from Al Viro:
 "Fairly old DIO bug caught by Andreas (3.10+) and several slightly
  younger blk_rq_map_user_iov() bugs, both on map and copy codepaths
  (Vitaly and me)"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  bio_copy_user_iov(): don't ignore ->iov_offset
  more bio_map_user_iov() leak fixes
  fix unbalanced page refcounting in bio_map_user_iov
  direct-io: Prevent NULL pointer access in submit_page_section
parents a957fd42 1cfd0ddd
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -1239,8 +1239,8 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
	 */
	bmd->is_our_pages = map_data ? 0 : 1;
	memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs);
	iov_iter_init(&bmd->iter, iter->type, bmd->iov,
			iter->nr_segs, iter->count);
	bmd->iter = *iter;
	bmd->iter.iov = bmd->iov;

	ret = -ENOMEM;
	bio = bio_kmalloc(gfp_mask, nr_pages);
@@ -1331,6 +1331,7 @@ struct bio *bio_map_user_iov(struct request_queue *q,
	int ret, offset;
	struct iov_iter i;
	struct iovec iov;
	struct bio_vec *bvec;

	iov_for_each(iov, i, *iter) {
		unsigned long uaddr = (unsigned long) iov.iov_base;
@@ -1375,7 +1376,12 @@ struct bio *bio_map_user_iov(struct request_queue *q,
		ret = get_user_pages_fast(uaddr, local_nr_pages,
				(iter->type & WRITE) != WRITE,
				&pages[cur_page]);
		if (ret < local_nr_pages) {
		if (unlikely(ret < local_nr_pages)) {
			for (j = cur_page; j < page_limit; j++) {
				if (!pages[j])
					break;
				put_page(pages[j]);
			}
			ret = -EFAULT;
			goto out_unmap;
		}
@@ -1383,6 +1389,7 @@ struct bio *bio_map_user_iov(struct request_queue *q,
		offset = offset_in_page(uaddr);
		for (j = cur_page; j < page_limit; j++) {
			unsigned int bytes = PAGE_SIZE - offset;
			unsigned short prev_bi_vcnt = bio->bi_vcnt;

			if (len <= 0)
				break;
@@ -1397,6 +1404,13 @@ struct bio *bio_map_user_iov(struct request_queue *q,
					    bytes)
				break;

			/*
			 * check if vector was merged with previous
			 * drop page reference if needed
			 */
			if (bio->bi_vcnt == prev_bi_vcnt)
				put_page(pages[j]);

			len -= bytes;
			offset = 0;
		}
@@ -1423,10 +1437,8 @@ struct bio *bio_map_user_iov(struct request_queue *q,
	return bio;

 out_unmap:
	for (j = 0; j < nr_pages; j++) {
		if (!pages[j])
			break;
		put_page(pages[j]);
	bio_for_each_segment_all(bvec, bio, j) {
		put_page(bvec->bv_page);
	}
 out:
	kfree(pages);
+2 −1
Original line number Diff line number Diff line
@@ -866,6 +866,7 @@ submit_page_section(struct dio *dio, struct dio_submit *sdio, struct page *page,
	 */
	if (sdio->boundary) {
		ret = dio_send_cur_page(dio, sdio, map_bh);
		if (sdio->bio)
			dio_bio_submit(dio, sdio);
		put_page(sdio->cur_page);
		sdio->cur_page = NULL;