diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2014-08-28 11:09:31 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2014-08-28 14:24:09 -0400 |
commit | d49ec52ff6ddcda178fc2476a109cf1bd1fa19ed (patch) | |
tree | 789fa01103e1afc9d7a565e57f1e24eaf18db718 /drivers/md/dm-crypt.c | |
parent | 52addcf9d6669fa439387610bc65c92fa0980cef (diff) |
dm crypt: fix access beyond the end of allocated space
The DM crypt target accesses memory beyond allocated space resulting in
a crash on 32 bit x86 systems.
This bug is very old (it dates back to 2.6.25 commit 3a7f6c990ad04 "dm
crypt: use async crypto"). However, this bug was masked by the fact
that kmalloc rounds the size up to the next power of two. This bug
wasn't exposed until 3.17-rc1 commit 298a9fa08a ("dm crypt: use per-bio
data"). By switching to using per-bio data there was no longer any
padding beyond the end of a dm-crypt allocated memory block.
To minimize allocation overhead dm-crypt puts several structures into one
block allocated with kmalloc. The block holds struct ablkcipher_request,
cipher-specific scratch pad (crypto_ablkcipher_reqsize(any_tfm(cc))),
struct dm_crypt_request and an initialization vector.
The variable dmreq_start is set to offset of struct dm_crypt_request
within this memory block. dm-crypt allocates the block with this size:
cc->dmreq_start + sizeof(struct dm_crypt_request) + cc->iv_size.
When accessing the initialization vector, dm-crypt uses the function
iv_of_dmreq, which performs this calculation: ALIGN((unsigned long)(dmreq
+ 1), crypto_ablkcipher_alignmask(any_tfm(cc)) + 1).
dm-crypt allocated "cc->iv_size" bytes beyond the end of dm_crypt_request
structure. However, when dm-crypt accesses the initialization vector, it
takes a pointer to the end of dm_crypt_request, aligns it, and then uses
it as the initialization vector. If the end of dm_crypt_request is not
aligned on a crypto_ablkcipher_alignmask(any_tfm(cc)) boundary the
alignment causes the initialization vector to point beyond the allocated
space.
Fix this bug by calculating the variable iv_size_padding and adding it
to the allocated size.
Also correct the alignment of dm_crypt_request. struct dm_crypt_request
is specific to dm-crypt (it isn't used by the crypto subsystem at all),
so it is aligned on __alignof__(struct dm_crypt_request).
Also align per_bio_data_size on ARCH_KMALLOC_MINALIGN, so that it is
aligned as if the block was allocated with kmalloc.
Reported-by: Krzysztof Kolasa <kkolasa@winsoft.pl>
Tested-by: Milan Broz <gmazyland@gmail.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md/dm-crypt.c')
-rw-r--r-- | drivers/md/dm-crypt.c | 25 |
1 files changed, 19 insertions, 6 deletions
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 2785007e0e46..cd15e0801228 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c | |||
@@ -1688,6 +1688,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
1688 | unsigned int key_size, opt_params; | 1688 | unsigned int key_size, opt_params; |
1689 | unsigned long long tmpll; | 1689 | unsigned long long tmpll; |
1690 | int ret; | 1690 | int ret; |
1691 | size_t iv_size_padding; | ||
1691 | struct dm_arg_set as; | 1692 | struct dm_arg_set as; |
1692 | const char *opt_string; | 1693 | const char *opt_string; |
1693 | char dummy; | 1694 | char dummy; |
@@ -1724,20 +1725,32 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
1724 | 1725 | ||
1725 | cc->dmreq_start = sizeof(struct ablkcipher_request); | 1726 | cc->dmreq_start = sizeof(struct ablkcipher_request); |
1726 | cc->dmreq_start += crypto_ablkcipher_reqsize(any_tfm(cc)); | 1727 | cc->dmreq_start += crypto_ablkcipher_reqsize(any_tfm(cc)); |
1727 | cc->dmreq_start = ALIGN(cc->dmreq_start, crypto_tfm_ctx_alignment()); | 1728 | cc->dmreq_start = ALIGN(cc->dmreq_start, __alignof__(struct dm_crypt_request)); |
1728 | cc->dmreq_start += crypto_ablkcipher_alignmask(any_tfm(cc)) & | 1729 | |
1729 | ~(crypto_tfm_ctx_alignment() - 1); | 1730 | if (crypto_ablkcipher_alignmask(any_tfm(cc)) < CRYPTO_MINALIGN) { |
1731 | /* Allocate the padding exactly */ | ||
1732 | iv_size_padding = -(cc->dmreq_start + sizeof(struct dm_crypt_request)) | ||
1733 | & crypto_ablkcipher_alignmask(any_tfm(cc)); | ||
1734 | } else { | ||
1735 | /* | ||
1736 | * If the cipher requires greater alignment than kmalloc | ||
1737 | * alignment, we don't know the exact position of the | ||
1738 | * initialization vector. We must assume worst case. | ||
1739 | */ | ||
1740 | iv_size_padding = crypto_ablkcipher_alignmask(any_tfm(cc)); | ||
1741 | } | ||
1730 | 1742 | ||
1731 | cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start + | 1743 | cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start + |
1732 | sizeof(struct dm_crypt_request) + cc->iv_size); | 1744 | sizeof(struct dm_crypt_request) + iv_size_padding + cc->iv_size); |
1733 | if (!cc->req_pool) { | 1745 | if (!cc->req_pool) { |
1734 | ti->error = "Cannot allocate crypt request mempool"; | 1746 | ti->error = "Cannot allocate crypt request mempool"; |
1735 | goto bad; | 1747 | goto bad; |
1736 | } | 1748 | } |
1737 | 1749 | ||
1738 | cc->per_bio_data_size = ti->per_bio_data_size = | 1750 | cc->per_bio_data_size = ti->per_bio_data_size = |
1739 | sizeof(struct dm_crypt_io) + cc->dmreq_start + | 1751 | ALIGN(sizeof(struct dm_crypt_io) + cc->dmreq_start + |
1740 | sizeof(struct dm_crypt_request) + cc->iv_size; | 1752 | sizeof(struct dm_crypt_request) + iv_size_padding + cc->iv_size, |
1753 | ARCH_KMALLOC_MINALIGN); | ||
1741 | 1754 | ||
1742 | cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0); | 1755 | cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0); |
1743 | if (!cc->page_pool) { | 1756 | if (!cc->page_pool) { |