diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-10-11 12:00:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-10-11 12:00:22 -0400 |
commit | ce3861819a5bcb0762c0b627e43ced1683227807 (patch) | |
tree | 36c37e9882b4ea9aa60a847f2a2110b342ea545c | |
parent | a957fd420ca8774f1a6708c64a867f056e67c46e (diff) | |
parent | 1cfd0ddd82232804e03f3023f6a58b50dfef0574 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
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
-rw-r--r-- | block/bio.c | 26 | ||||
-rw-r--r-- | fs/direct-io.c | 3 |
2 files changed, 21 insertions, 8 deletions
diff --git a/block/bio.c b/block/bio.c index b38e962fa83e..101c2a9b5481 100644 --- a/block/bio.c +++ b/block/bio.c | |||
@@ -1239,8 +1239,8 @@ struct bio *bio_copy_user_iov(struct request_queue *q, | |||
1239 | */ | 1239 | */ |
1240 | bmd->is_our_pages = map_data ? 0 : 1; | 1240 | bmd->is_our_pages = map_data ? 0 : 1; |
1241 | memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs); | 1241 | memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs); |
1242 | iov_iter_init(&bmd->iter, iter->type, bmd->iov, | 1242 | bmd->iter = *iter; |
1243 | iter->nr_segs, iter->count); | 1243 | bmd->iter.iov = bmd->iov; |
1244 | 1244 | ||
1245 | ret = -ENOMEM; | 1245 | ret = -ENOMEM; |
1246 | bio = bio_kmalloc(gfp_mask, nr_pages); | 1246 | bio = bio_kmalloc(gfp_mask, nr_pages); |
@@ -1331,6 +1331,7 @@ struct bio *bio_map_user_iov(struct request_queue *q, | |||
1331 | int ret, offset; | 1331 | int ret, offset; |
1332 | struct iov_iter i; | 1332 | struct iov_iter i; |
1333 | struct iovec iov; | 1333 | struct iovec iov; |
1334 | struct bio_vec *bvec; | ||
1334 | 1335 | ||
1335 | iov_for_each(iov, i, *iter) { | 1336 | iov_for_each(iov, i, *iter) { |
1336 | unsigned long uaddr = (unsigned long) iov.iov_base; | 1337 | unsigned long uaddr = (unsigned long) iov.iov_base; |
@@ -1375,7 +1376,12 @@ struct bio *bio_map_user_iov(struct request_queue *q, | |||
1375 | ret = get_user_pages_fast(uaddr, local_nr_pages, | 1376 | ret = get_user_pages_fast(uaddr, local_nr_pages, |
1376 | (iter->type & WRITE) != WRITE, | 1377 | (iter->type & WRITE) != WRITE, |
1377 | &pages[cur_page]); | 1378 | &pages[cur_page]); |
1378 | if (ret < local_nr_pages) { | 1379 | if (unlikely(ret < local_nr_pages)) { |
1380 | for (j = cur_page; j < page_limit; j++) { | ||
1381 | if (!pages[j]) | ||
1382 | break; | ||
1383 | put_page(pages[j]); | ||
1384 | } | ||
1379 | ret = -EFAULT; | 1385 | ret = -EFAULT; |
1380 | goto out_unmap; | 1386 | goto out_unmap; |
1381 | } | 1387 | } |
@@ -1383,6 +1389,7 @@ struct bio *bio_map_user_iov(struct request_queue *q, | |||
1383 | offset = offset_in_page(uaddr); | 1389 | offset = offset_in_page(uaddr); |
1384 | for (j = cur_page; j < page_limit; j++) { | 1390 | for (j = cur_page; j < page_limit; j++) { |
1385 | unsigned int bytes = PAGE_SIZE - offset; | 1391 | unsigned int bytes = PAGE_SIZE - offset; |
1392 | unsigned short prev_bi_vcnt = bio->bi_vcnt; | ||
1386 | 1393 | ||
1387 | if (len <= 0) | 1394 | if (len <= 0) |
1388 | break; | 1395 | break; |
@@ -1397,6 +1404,13 @@ struct bio *bio_map_user_iov(struct request_queue *q, | |||
1397 | bytes) | 1404 | bytes) |
1398 | break; | 1405 | break; |
1399 | 1406 | ||
1407 | /* | ||
1408 | * check if vector was merged with previous | ||
1409 | * drop page reference if needed | ||
1410 | */ | ||
1411 | if (bio->bi_vcnt == prev_bi_vcnt) | ||
1412 | put_page(pages[j]); | ||
1413 | |||
1400 | len -= bytes; | 1414 | len -= bytes; |
1401 | offset = 0; | 1415 | offset = 0; |
1402 | } | 1416 | } |
@@ -1423,10 +1437,8 @@ struct bio *bio_map_user_iov(struct request_queue *q, | |||
1423 | return bio; | 1437 | return bio; |
1424 | 1438 | ||
1425 | out_unmap: | 1439 | out_unmap: |
1426 | for (j = 0; j < nr_pages; j++) { | 1440 | bio_for_each_segment_all(bvec, bio, j) { |
1427 | if (!pages[j]) | 1441 | put_page(bvec->bv_page); |
1428 | break; | ||
1429 | put_page(pages[j]); | ||
1430 | } | 1442 | } |
1431 | out: | 1443 | out: |
1432 | kfree(pages); | 1444 | kfree(pages); |
diff --git a/fs/direct-io.c b/fs/direct-io.c index 62cf812ed0e5..96415c65bbdc 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c | |||
@@ -866,7 +866,8 @@ out: | |||
866 | */ | 866 | */ |
867 | if (sdio->boundary) { | 867 | if (sdio->boundary) { |
868 | ret = dio_send_cur_page(dio, sdio, map_bh); | 868 | ret = dio_send_cur_page(dio, sdio, map_bh); |
869 | dio_bio_submit(dio, sdio); | 869 | if (sdio->bio) |
870 | dio_bio_submit(dio, sdio); | ||
870 | put_page(sdio->cur_page); | 871 | put_page(sdio->cur_page); |
871 | sdio->cur_page = NULL; | 872 | sdio->cur_page = NULL; |
872 | } | 873 | } |