summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2017-06-29 14:31:13 -0400
committerJens Axboe <axboe@kernel.dk>2017-07-03 18:56:26 -0400
commitb1fb2c52b2d85f51f36f1661409f9aeef94265ff (patch)
tree26f345624f8f3a8adca8689916b2f9d69282051d
parent128b6f9fdd9ace9e56cb3a263b4bc269658f9c40 (diff)
block: guard bvec iteration logic
Currently if some one try to advance bvec beyond it's size we simply dump WARN_ONCE and continue to iterate beyond bvec array boundaries. This simply means that we endup dereferencing/corrupting random memory region. Sane reaction would be to propagate error back to calling context But bvec_iter_advance's calling context is not always good for error handling. For safity reason let truncate iterator size to zero which will break external iteration loop which prevent us from unpredictable memory range corruption. And even it caller ignores an error, it will corrupt it's own bvecs, not others. This patch does: - Return error back to caller with hope that it will react on this - Truncate iterator size Code was added long time ago here 4550dd6c, luckily no one hit it in real life :) Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Reviewed-by: Ming Lei <ming.lei@redhat.com> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> [hch: switch to true/false returns instead of errno values] Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--drivers/nvdimm/blk.c3
-rw-r--r--drivers/nvdimm/btt.c3
-rw-r--r--include/linux/bio.h4
-rw-r--r--include/linux/bvec.h14
4 files changed, 16 insertions, 8 deletions
diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 1a578b2a437b..345acca576b3 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -106,7 +106,8 @@ static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
106 106
107 len -= cur_len; 107 len -= cur_len;
108 dev_offset += cur_len; 108 dev_offset += cur_len;
109 bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len); 109 if (!bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len))
110 return -EIO;
110 } 111 }
111 112
112 return err; 113 return err;
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index b5caaee78bbf..d00c10f382f0 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -985,7 +985,8 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
985 985
986 len -= cur_len; 986 len -= cur_len;
987 meta_nsoff += cur_len; 987 meta_nsoff += cur_len;
988 bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len); 988 if (!bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len))
989 return -EIO;
989 } 990 }
990 991
991 return ret; 992 return ret;
diff --git a/include/linux/bio.h b/include/linux/bio.h
index b3b5f5a89a9c..d5e8689f86b8 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -167,8 +167,10 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
167 167
168 if (bio_no_advance_iter(bio)) 168 if (bio_no_advance_iter(bio))
169 iter->bi_size -= bytes; 169 iter->bi_size -= bytes;
170 else 170 else {
171 bvec_iter_advance(bio->bi_io_vec, iter, bytes); 171 bvec_iter_advance(bio->bi_io_vec, iter, bytes);
172 /* TODO: It is reasonable to complete bio with error here. */
173 }
172} 174}
173 175
174#define __bio_for_each_segment(bvl, bio, iter, start) \ 176#define __bio_for_each_segment(bvl, bio, iter, start) \
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 89b65b82d98f..de317b4c13c1 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -22,6 +22,7 @@
22 22
23#include <linux/kernel.h> 23#include <linux/kernel.h>
24#include <linux/bug.h> 24#include <linux/bug.h>
25#include <linux/errno.h>
25 26
26/* 27/*
27 * was unsigned short, but we might as well be ready for > 64kB I/O pages 28 * was unsigned short, but we might as well be ready for > 64kB I/O pages
@@ -66,12 +67,14 @@ struct bvec_iter {
66 .bv_offset = bvec_iter_offset((bvec), (iter)), \ 67 .bv_offset = bvec_iter_offset((bvec), (iter)), \
67}) 68})
68 69
69static inline void bvec_iter_advance(const struct bio_vec *bv, 70static inline bool bvec_iter_advance(const struct bio_vec *bv,
70 struct bvec_iter *iter, 71 struct bvec_iter *iter, unsigned bytes)
71 unsigned bytes)
72{ 72{
73 WARN_ONCE(bytes > iter->bi_size, 73 if (WARN_ONCE(bytes > iter->bi_size,
74 "Attempted to advance past end of bvec iter\n"); 74 "Attempted to advance past end of bvec iter\n")) {
75 iter->bi_size = 0;
76 return false;
77 }
75 78
76 while (bytes) { 79 while (bytes) {
77 unsigned iter_len = bvec_iter_len(bv, *iter); 80 unsigned iter_len = bvec_iter_len(bv, *iter);
@@ -86,6 +89,7 @@ static inline void bvec_iter_advance(const struct bio_vec *bv,
86 iter->bi_idx++; 89 iter->bi_idx++;
87 } 90 }
88 } 91 }
92 return true;
89} 93}
90 94
91#define for_each_bvec(bvl, bio_vec, iter, start) \ 95#define for_each_bvec(bvl, bio_vec, iter, start) \