From 636d4eef1eefe447deef134bdf8e34c979ff009e Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Fri, 3 Jun 2022 02:34:14 -0400 Subject: bcachefs: Fix memory corruption in encryption path When do_encrypt() was passed a vmalloc address and the buffer spanned more than a single page, we were encrypting/decrypting completely different pages than the ones intended. Signed-off-by: Kent Overstreet --- fs/bcachefs/checksum.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'fs/bcachefs/checksum.c') diff --git a/fs/bcachefs/checksum.c b/fs/bcachefs/checksum.c index 317efd047a46..e9a444f75b93 100644 --- a/fs/bcachefs/checksum.c +++ b/fs/bcachefs/checksum.c @@ -114,15 +114,41 @@ static inline int do_encrypt(struct crypto_sync_skcipher *tfm, struct nonce nonce, void *buf, size_t len) { - struct scatterlist sg; - - sg_init_table(&sg, 1); - sg_set_page(&sg, - is_vmalloc_addr(buf) - ? vmalloc_to_page(buf) - : virt_to_page(buf), - len, offset_in_page(buf)); - return do_encrypt_sg(tfm, nonce, &sg, len); + if (!is_vmalloc_addr(buf)) { + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_set_page(&sg, + is_vmalloc_addr(buf) + ? vmalloc_to_page(buf) + : virt_to_page(buf), + len, offset_in_page(buf)); + return do_encrypt_sg(tfm, nonce, &sg, len); + } else { + unsigned pages = buf_pages(buf, len); + struct scatterlist *sg; + size_t orig_len = len; + int ret, i; + + sg = kmalloc_array(sizeof(*sg), pages, GFP_KERNEL); + if (!sg) + return -ENOMEM; + + sg_init_table(sg, pages); + + for (i = 0; i < pages; i++) { + unsigned offset = offset_in_page(buf); + unsigned pg_len = min(len, PAGE_SIZE - offset); + + sg_set_page(sg + i, vmalloc_to_page(buf), pg_len, offset); + buf += pg_len; + len -= pg_len; + } + + ret = do_encrypt_sg(tfm, nonce, sg, orig_len); + kfree(sg); + return ret; + } } int bch2_chacha_encrypt_key(struct bch_key *key, struct nonce nonce, -- cgit v1.2.3