Skip to content

Commit b1fb2c5

Browse files
Dmitry Monakhovaxboe
authored andcommitted
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 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]>
1 parent 128b6f9 commit b1fb2c5

File tree

4 files changed

+16
-8
lines changed

4 files changed

+16
-8
lines changed

drivers/nvdimm/blk.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
106106

107107
len -= cur_len;
108108
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;
110111
}
111112

112113
return err;

drivers/nvdimm/btt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,8 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
985985

986986
len -= cur_len;
987987
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;
989990
}
990991

991992
return ret;

include/linux/bio.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
167167

168168
if (bio_no_advance_iter(bio))
169169
iter->bi_size -= bytes;
170-
else
170+
else {
171171
bvec_iter_advance(bio->bi_io_vec, iter, bytes);
172+
/* TODO: It is reasonable to complete bio with error here. */
173+
}
172174
}
173175

174176
#define __bio_for_each_segment(bvl, bio, iter, start) \

include/linux/bvec.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <linux/kernel.h>
2424
#include <linux/bug.h>
25+
#include <linux/errno.h>
2526

2627
/*
2728
* was unsigned short, but we might as well be ready for > 64kB I/O pages
@@ -66,12 +67,14 @@ struct bvec_iter {
6667
.bv_offset = bvec_iter_offset((bvec), (iter)), \
6768
})
6869

69-
static inline void bvec_iter_advance(const struct bio_vec *bv,
70-
struct bvec_iter *iter,
71-
unsigned bytes)
70+
static inline bool bvec_iter_advance(const struct bio_vec *bv,
71+
struct bvec_iter *iter, unsigned bytes)
7272
{
73-
WARN_ONCE(bytes > iter->bi_size,
74-
"Attempted to advance past end of bvec iter\n");
73+
if (WARN_ONCE(bytes > iter->bi_size,
74+
"Attempted to advance past end of bvec iter\n")) {
75+
iter->bi_size = 0;
76+
return false;
77+
}
7578

7679
while (bytes) {
7780
unsigned iter_len = bvec_iter_len(bv, *iter);
@@ -86,6 +89,7 @@ static inline void bvec_iter_advance(const struct bio_vec *bv,
8689
iter->bi_idx++;
8790
}
8891
}
92+
return true;
8993
}
9094

9195
#define for_each_bvec(bvl, bio_vec, iter, start) \

0 commit comments

Comments
 (0)