diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2022-06-03 02:34:14 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-22 17:09:33 -0400 |
commit | 636d4eef1eefe447deef134bdf8e34c979ff009e (patch) | |
tree | 60666fd80b382c88370d9012c4fb5bfe343f4f09 /fs/bcachefs/checksum.c | |
parent | 0fbf71f80d60c077f491f0ac97000c3a0c9be3aa (diff) | |
download | lwn-636d4eef1eefe447deef134bdf8e34c979ff009e.tar.gz lwn-636d4eef1eefe447deef134bdf8e34c979ff009e.zip |
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 <kent.overstreet@gmail.com>
Diffstat (limited to 'fs/bcachefs/checksum.c')
-rw-r--r-- | fs/bcachefs/checksum.c | 44 |
1 files changed, 35 insertions, 9 deletions
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, |