Skip to content

Commit

Permalink
block: guard bvec iteration logic
Browse files Browse the repository at this point in the history
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 4550dd6, luckily no one hit it
in real life :)

Signed-off-by: Dmitry Monakhov <[email protected]>
Reviewed-by: Ming Lei <[email protected]>
Reviewed-by: Martin K. Petersen <[email protected]>
[hch: switch to true/false returns instead of errno values]
Signed-off-by: Christoph Hellwig <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
Dmitry Monakhov authored and axboe committed Jul 3, 2017
1 parent 128b6f9 commit b1fb2c5
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 8 deletions.
3 changes: 2 additions & 1 deletion drivers/nvdimm/blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion drivers/nvdimm/btt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion include/linux/bio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
14 changes: 9 additions & 5 deletions include/linux/bvec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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) \
Expand Down

0 comments on commit b1fb2c5

Please sign in to comment.