Commit 1d9a9438 authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Mike Snitzer

dm flakey: clone pages on write bio before corrupting them

dm-flakey has an option to corrupt write bios. It corrupts the memory that
is being written. This can cause system crashes or security bugs - for
example, if the user writes a shared library code with O_DIRECT flag to a
dm-flakey device, it corrupts the memory for all users that have the
shared library mapped.

Fix this bug by cloning the bio and corrupting the clone rather than
the original.

Also drop the test for ZERO_PAGE(0) - it can't happen because we write
the cloned pages.
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
parent 5054e778
...@@ -322,11 +322,7 @@ static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) ...@@ -322,11 +322,7 @@ static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
*/ */
bio_for_each_segment(bvec, bio, iter) { bio_for_each_segment(bvec, bio, iter) {
if (bio_iter_len(bio, iter) > corrupt_bio_byte) { if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
char *segment; char *segment = bvec_kmap_local(&bvec);
struct page *page = bio_iter_page(bio, iter);
if (unlikely(page == ZERO_PAGE(0)))
break;
segment = bvec_kmap_local(&bvec);
segment[corrupt_bio_byte] = fc->corrupt_bio_value; segment[corrupt_bio_byte] = fc->corrupt_bio_value;
kunmap_local(segment); kunmap_local(segment);
DMDEBUG("Corrupting data bio=%p by writing %u to byte %u " DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
...@@ -340,6 +336,92 @@ static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) ...@@ -340,6 +336,92 @@ static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
} }
} }
static void clone_free(struct bio *clone)
{
struct folio_iter fi;
if (clone->bi_vcnt > 0) { /* bio_for_each_folio_all crashes with an empty bio */
bio_for_each_folio_all(fi, clone)
folio_put(fi.folio);
}
bio_uninit(clone);
kfree(clone);
}
static void clone_endio(struct bio *clone)
{
struct bio *bio = clone->bi_private;
bio->bi_status = clone->bi_status;
clone_free(clone);
bio_endio(bio);
}
static struct bio *clone_bio(struct dm_target *ti, struct flakey_c *fc, struct bio *bio)
{
struct bio *clone;
unsigned size, remaining_size, nr_iovecs, order;
struct bvec_iter iter = bio->bi_iter;
if (unlikely(bio->bi_iter.bi_size > UIO_MAXIOV << PAGE_SHIFT))
dm_accept_partial_bio(bio, UIO_MAXIOV << PAGE_SHIFT >> SECTOR_SHIFT);
size = bio->bi_iter.bi_size;
nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
clone = bio_kmalloc(nr_iovecs, GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN);
if (!clone)
return NULL;
bio_init(clone, fc->dev->bdev, bio->bi_inline_vecs, nr_iovecs, bio->bi_opf);
clone->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);
clone->bi_private = bio;
clone->bi_end_io = clone_endio;
remaining_size = size;
order = MAX_ORDER - 1;
while (remaining_size) {
struct page *pages;
unsigned size_to_add, to_copy;
unsigned char *virt;
unsigned remaining_order = __fls((remaining_size + PAGE_SIZE - 1) >> PAGE_SHIFT);
order = min(order, remaining_order);
retry_alloc_pages:
pages = alloc_pages(GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN | __GFP_COMP, order);
if (unlikely(!pages)) {
if (order) {
order--;
goto retry_alloc_pages;
}
clone_free(clone);
return NULL;
}
size_to_add = min((unsigned)PAGE_SIZE << order, remaining_size);
virt = page_to_virt(pages);
to_copy = size_to_add;
do {
struct bio_vec bvec = bvec_iter_bvec(bio->bi_io_vec, iter);
unsigned this_step = min(bvec.bv_len, to_copy);
void *map = bvec_kmap_local(&bvec);
memcpy(virt, map, this_step);
kunmap_local(map);
bvec_iter_advance(bio->bi_io_vec, &iter, this_step);
to_copy -= this_step;
virt += this_step;
} while (to_copy);
__bio_add_page(clone, pages, size_to_add, 0);
remaining_size -= size_to_add;
}
return clone;
}
static int flakey_map(struct dm_target *ti, struct bio *bio) static int flakey_map(struct dm_target *ti, struct bio *bio)
{ {
struct flakey_c *fc = ti->private; struct flakey_c *fc = ti->private;
...@@ -383,10 +465,14 @@ static int flakey_map(struct dm_target *ti, struct bio *bio) ...@@ -383,10 +465,14 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
/* /*
* Corrupt matching writes. * Corrupt matching writes.
*/ */
if (fc->corrupt_bio_byte) { if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) {
if (fc->corrupt_bio_rw == WRITE) { if (all_corrupt_bio_flags_match(bio, fc)) {
if (all_corrupt_bio_flags_match(bio, fc)) struct bio *clone = clone_bio(ti, fc, bio);
corrupt_bio_data(bio, fc); if (clone) {
corrupt_bio_data(clone, fc);
submit_bio(clone);
return DM_MAPIO_SUBMITTED;
}
} }
goto map_bio; goto map_bio;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment