Skip to content

Commit

Permalink
dm crypt: use bio_add_page
Browse files Browse the repository at this point in the history
Fix possible max_phys_segments violation in cloned dm-crypt bio.

In write operation dm-crypt needs to allocate new bio request
and run crypto operation on this clone. Cloned request has always
the same size, but number of physical segments can be increased
and violate max_phys_segments restriction.

This can lead to data corruption and serious hardware malfunction.
This was observed when using XFS over dm-crypt and at least
two HBA controller drivers (arcmsr, cciss) recently.

Fix it by using bio_add_page() call (which tests for other
restrictions too) instead of constructing own biovec.

All versions of dm-crypt are affected by this bug.

Cc: stable@kernel.org
Cc:  dm-crypt@saout.de
Signed-off-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
  • Loading branch information
Milan Broz authored and kergon committed Dec 20, 2007
1 parent 9121250 commit 91e1062
Showing 1 changed file with 11 additions and 13 deletions.
24 changes: 11 additions & 13 deletions drivers/md/dm-crypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size)
struct bio *clone;
unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM;
unsigned int i;
unsigned i, len;
struct page *page;

clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs);
if (!clone)
Expand All @@ -407,10 +408,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size)
clone_init(io, clone);

for (i = 0; i < nr_iovecs; i++) {
struct bio_vec *bv = bio_iovec_idx(clone, i);

bv->bv_page = mempool_alloc(cc->page_pool, gfp_mask);
if (!bv->bv_page)
page = mempool_alloc(cc->page_pool, gfp_mask);
if (!page)
break;

/*
Expand All @@ -421,15 +420,14 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size)
if (i == (MIN_BIO_PAGES - 1))
gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT;

bv->bv_offset = 0;
if (size > PAGE_SIZE)
bv->bv_len = PAGE_SIZE;
else
bv->bv_len = size;
len = (size > PAGE_SIZE) ? PAGE_SIZE : size;

if (!bio_add_page(clone, page, len, 0)) {
mempool_free(page, cc->page_pool);
break;
}

clone->bi_size += bv->bv_len;
clone->bi_vcnt++;
size -= bv->bv_len;
size -= len;
}

if (!clone->bi_size) {
Expand Down

0 comments on commit 91e1062

Please sign in to comment.