block: guard bvec iteration logic
authorDmitry Monakhov <dmonakhov@openvz.org>
Thu, 29 Jun 2017 18:31:13 +0000 (11:31 -0700)
committerJens Axboe <axboe@kernel.dk>
Mon, 3 Jul 2017 22:56:26 +0000 (16:56 -0600)
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>
drivers/nvdimm/blk.c
drivers/nvdimm/btt.c
include/linux/bio.h
include/linux/bvec.h

index 1a578b2a437b27058441dd479595ecf3ede07101..345acca576b3c077b68e339437dd2e54ae76384a 100644 (file)
@@ -106,7 +106,8 @@ static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
 
                len -= cur_len;
                dev_offset += cur_len;
-               bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
+               if (!bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len))
+                       return -EIO;
        }
 
        return err;
index b5caaee78bbfa97073668c165a75346beabbd9c5..d00c10f382f0fcc93027ce46ac17caf1b9bb6563 100644 (file)
@@ -985,7 +985,8 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
 
                len -= cur_len;
                meta_nsoff += cur_len;
-               bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
+               if (!bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len))
+                       return -EIO;
        }
 
        return ret;
index b3b5f5a89a9c6160f9f618f913fc22005b63eee4..d5e8689f86b8c8c30cccc770f572820ebcdf4c8a 100644 (file)
@@ -167,8 +167,10 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
 
        if (bio_no_advance_iter(bio))
                iter->bi_size -= bytes;
-       else
+       else {
                bvec_iter_advance(bio->bi_io_vec, iter, bytes);
+               /* TODO: It is reasonable to complete bio with error here. */
+       }
 }
 
 #define __bio_for_each_segment(bvl, bio, iter, start)                  \
index 89b65b82d98f5c5e77c34f967e856dcc6028dabe..de317b4c13c105ec5d3166f9705927994b3a1136 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <linux/kernel.h>
 #include <linux/bug.h>
+#include <linux/errno.h>
 
 /*
  * was unsigned short, but we might as well be ready for > 64kB I/O pages
@@ -66,12 +67,14 @@ struct bvec_iter {
        .bv_offset      = bvec_iter_offset((bvec), (iter)),     \
 })
 
-static inline void bvec_iter_advance(const struct bio_vec *bv,
-                                    struct bvec_iter *iter,
-                                    unsigned bytes)
+static inline bool bvec_iter_advance(const struct bio_vec *bv,
+               struct bvec_iter *iter, unsigned bytes)
 {
-       WARN_ONCE(bytes > iter->bi_size,
-                 "Attempted to advance past end of bvec iter\n");
+       if (WARN_ONCE(bytes > iter->bi_size,
+                    "Attempted to advance past end of bvec iter\n")) {
+               iter->bi_size = 0;
+               return false;
+       }
 
        while (bytes) {
                unsigned iter_len = bvec_iter_len(bv, *iter);
@@ -86,6 +89,7 @@ static inline void bvec_iter_advance(const struct bio_vec *bv,
                        iter->bi_idx++;
                }
        }
+       return true;
 }
 
 #define for_each_bvec(bvl, bio_vec, iter, start)                       \