dm crypt: use bio_add_page

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>

authored by Milan Broz and committed by Alasdair G Kergon 91e10625 91212507

+11 -13
+11 -13
drivers/md/dm-crypt.c
··· 398 struct bio *clone; 399 unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; 400 gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; 401 - unsigned int i; 402 403 clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); 404 if (!clone) ··· 408 clone_init(io, clone); 409 410 for (i = 0; i < nr_iovecs; i++) { 411 - struct bio_vec *bv = bio_iovec_idx(clone, i); 412 - 413 - bv->bv_page = mempool_alloc(cc->page_pool, gfp_mask); 414 - if (!bv->bv_page) 415 break; 416 417 /* ··· 420 if (i == (MIN_BIO_PAGES - 1)) 421 gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT; 422 423 - bv->bv_offset = 0; 424 - if (size > PAGE_SIZE) 425 - bv->bv_len = PAGE_SIZE; 426 - else 427 - bv->bv_len = size; 428 429 - clone->bi_size += bv->bv_len; 430 - clone->bi_vcnt++; 431 - size -= bv->bv_len; 432 } 433 434 if (!clone->bi_size) {
··· 398 struct bio *clone; 399 unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; 400 gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; 401 + unsigned i, len; 402 + struct page *page; 403 404 clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); 405 if (!clone) ··· 407 clone_init(io, clone); 408 409 for (i = 0; i < nr_iovecs; i++) { 410 + page = mempool_alloc(cc->page_pool, gfp_mask); 411 + if (!page) 412 break; 413 414 /* ··· 421 if (i == (MIN_BIO_PAGES - 1)) 422 gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT; 423 424 + len = (size > PAGE_SIZE) ? PAGE_SIZE : size; 425 426 + if (!bio_add_page(clone, page, len, 0)) { 427 + mempool_free(page, cc->page_pool); 428 + break; 429 + } 430 + 431 + size -= len; 432 } 433 434 if (!clone->bi_size) {