Skip to content

Commit bf0543b

Browse files
Paulo Alcantaragregkh
authored andcommitted
cifs: fix oops during encryption
[ Upstream commit f7f291e ] When running xfstests against Azure the following oops occurred on an arm64 system Unable to handle kernel write to read-only memory at virtual address ffff0001221cf000 Mem abort info: ESR = 0x9600004f EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x0f: level 3 permission fault Data abort info: ISV = 0, ISS = 0x0000004f CM = 0, WnR = 1 swapper pgtable: 4k pages, 48-bit VAs, pgdp=00000000294f3000 [ffff0001221cf000] pgd=18000001ffff8003, p4d=18000001ffff8003, pud=18000001ff82e003, pmd=18000001ff71d003, pte=00600001221cf787 Internal error: Oops: 9600004f [#1] PREEMPT SMP ... pstate: 80000005 (Nzcv daif -PAN -UAO -TCO BTYPE=--) pc : __memcpy+0x40/0x230 lr : scatterwalk_copychunks+0xe0/0x200 sp : ffff800014e92de0 x29: ffff800014e92de0 x28: ffff000114f9de80 x27: 0000000000000008 x26: 0000000000000008 x25: ffff800014e92e78 x24: 0000000000000008 x23: 0000000000000001 x22: 0000040000000000 x21: ffff000000000000 x20: 0000000000000001 x19: ffff0001037c4488 x18: 0000000000000014 x17: 235e1c0d6efa9661 x16: a435f9576b6edd6c x15: 0000000000000058 x14: 0000000000000001 x13: 0000000000000008 x12: ffff000114f2e590 x11: ffffffffffffffff x10: 0000040000000000 x9 : ffff8000105c3580 x8 : 2e9413b10000001a x7 : 534b4410fb86b005 x6 : 534b4410fb86b005 x5 : ffff0001221cf008 x4 : ffff0001037c4490 x3 : 0000000000000001 x2 : 0000000000000008 x1 : ffff0001037c4488 x0 : ffff0001221cf000 Call trace: __memcpy+0x40/0x230 scatterwalk_map_and_copy+0x98/0x100 crypto_ccm_encrypt+0x150/0x180 crypto_aead_encrypt+0x2c/0x40 crypt_message+0x750/0x880 smb3_init_transform_rq+0x298/0x340 smb_send_rqst.part.11+0xd8/0x180 smb_send_rqst+0x3c/0x100 compound_send_recv+0x534/0xbc0 smb2_query_info_compound+0x32c/0x440 smb2_set_ea+0x438/0x4c0 cifs_xattr_set+0x5d4/0x7c0 This is because in scatterwalk_copychunks(), we attempted to write to a buffer (@sign) that was allocated in the stack (vmalloc area) by crypt_message() and thus accessing its remaining 8 (x2) bytes ended up crossing a page boundary. To simply fix it, we could just pass @sign kmalloc'd from crypt_message() and then we're done. Luckily, we don't seem to pass any other vmalloc'd buffers in smb_rqst::rq_iov... Instead, let's map the correct pages and offsets from vmalloc buffers as well in cifs_sg_set_buf() and then avoiding such oopses. Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz> Cc: stable@vger.kernel.org Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 56f6de3 commit bf0543b

File tree

4 files changed

+141
-79
lines changed

4 files changed

+141
-79
lines changed

fs/cifs/cifsglob.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <linux/in6.h>
1414
#include <linux/inet.h>
1515
#include <linux/slab.h>
16+
#include <linux/scatterlist.h>
17+
#include <linux/mm.h>
1618
#include <linux/mempool.h>
1719
#include <linux/workqueue.h>
1820
#include "cifs_fs_sb.h"
@@ -21,6 +23,7 @@
2123
#include <linux/scatterlist.h>
2224
#include <uapi/linux/cifs/cifs_mount.h>
2325
#include "smb2pdu.h"
26+
#include "smb2glob.h"
2427

2528
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
2629

@@ -1972,4 +1975,70 @@ static inline bool cifs_is_referral_server(struct cifs_tcon *tcon,
19721975
return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER));
19731976
}
19741977

1978+
static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst,
1979+
int num_rqst,
1980+
const u8 *sig)
1981+
{
1982+
unsigned int len, skip;
1983+
unsigned int nents = 0;
1984+
unsigned long addr;
1985+
int i, j;
1986+
1987+
/* Assumes the first rqst has a transform header as the first iov.
1988+
* I.e.
1989+
* rqst[0].rq_iov[0] is transform header
1990+
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
1991+
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
1992+
*/
1993+
for (i = 0; i < num_rqst; i++) {
1994+
/*
1995+
* The first rqst has a transform header where the
1996+
* first 20 bytes are not part of the encrypted blob.
1997+
*/
1998+
for (j = 0; j < rqst[i].rq_nvec; j++) {
1999+
struct kvec *iov = &rqst[i].rq_iov[j];
2000+
2001+
skip = (i == 0) && (j == 0) ? 20 : 0;
2002+
addr = (unsigned long)iov->iov_base + skip;
2003+
if (unlikely(is_vmalloc_addr((void *)addr))) {
2004+
len = iov->iov_len - skip;
2005+
nents += DIV_ROUND_UP(offset_in_page(addr) + len,
2006+
PAGE_SIZE);
2007+
} else {
2008+
nents++;
2009+
}
2010+
}
2011+
nents += rqst[i].rq_npages;
2012+
}
2013+
nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
2014+
return nents;
2015+
}
2016+
2017+
/* We can not use the normal sg_set_buf() as we will sometimes pass a
2018+
* stack object as buf.
2019+
*/
2020+
static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg,
2021+
const void *buf,
2022+
unsigned int buflen)
2023+
{
2024+
unsigned long addr = (unsigned long)buf;
2025+
unsigned int off = offset_in_page(addr);
2026+
2027+
addr &= PAGE_MASK;
2028+
if (unlikely(is_vmalloc_addr((void *)addr))) {
2029+
do {
2030+
unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);
2031+
2032+
sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off);
2033+
2034+
off = 0;
2035+
addr += PAGE_SIZE;
2036+
buflen -= len;
2037+
} while (buflen);
2038+
} else {
2039+
sg_set_page(sg++, virt_to_page(addr), buflen, off);
2040+
}
2041+
return sg;
2042+
}
2043+
19752044
#endif /* _CIFS_GLOB_H */

fs/cifs/cifsproto.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,8 @@ int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
590590
struct sdesc **sdesc);
591591
void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
592592

593-
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
594-
unsigned int *len, unsigned int *offset);
593+
void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page,
594+
unsigned int *len, unsigned int *offset);
595595
struct cifs_chan *
596596
cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server);
597597
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);

fs/cifs/misc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,8 +1134,8 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
11341134
* @len: Where to store the length for this page:
11351135
* @offset: Where to store the offset for this page
11361136
*/
1137-
void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
1138-
unsigned int *len, unsigned int *offset)
1137+
void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page,
1138+
unsigned int *len, unsigned int *offset)
11391139
{
11401140
*len = rqst->rq_pagesz;
11411141
*offset = (page == 0) ? rqst->rq_offset : 0;

fs/cifs/smb2ops.c

Lines changed: 68 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4416,69 +4416,82 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
44164416
memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8);
44174417
}
44184418

4419-
/* We can not use the normal sg_set_buf() as we will sometimes pass a
4420-
* stack object as buf.
4421-
*/
4422-
static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
4423-
unsigned int buflen)
4419+
static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst,
4420+
int num_rqst, const u8 *sig, u8 **iv,
4421+
struct aead_request **req, struct scatterlist **sgl,
4422+
unsigned int *num_sgs)
44244423
{
4425-
void *addr;
4426-
/*
4427-
* VMAP_STACK (at least) puts stack into the vmalloc address space
4428-
*/
4429-
if (is_vmalloc_addr(buf))
4430-
addr = vmalloc_to_page(buf);
4431-
else
4432-
addr = virt_to_page(buf);
4433-
sg_set_page(sg, addr, buflen, offset_in_page(buf));
4424+
unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
4425+
unsigned int iv_size = crypto_aead_ivsize(tfm);
4426+
unsigned int len;
4427+
u8 *p;
4428+
4429+
*num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig);
4430+
4431+
len = iv_size;
4432+
len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
4433+
len = ALIGN(len, crypto_tfm_ctx_alignment());
4434+
len += req_size;
4435+
len = ALIGN(len, __alignof__(struct scatterlist));
4436+
len += *num_sgs * sizeof(**sgl);
4437+
4438+
p = kmalloc(len, GFP_ATOMIC);
4439+
if (!p)
4440+
return NULL;
4441+
4442+
*iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1);
4443+
*req = (struct aead_request *)PTR_ALIGN(*iv + iv_size,
4444+
crypto_tfm_ctx_alignment());
4445+
*sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size,
4446+
__alignof__(struct scatterlist));
4447+
return p;
44344448
}
44354449

4436-
/* Assumes the first rqst has a transform header as the first iov.
4437-
* I.e.
4438-
* rqst[0].rq_iov[0] is transform header
4439-
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
4440-
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
4441-
*/
4442-
static struct scatterlist *
4443-
init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
4450+
static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst,
4451+
int num_rqst, const u8 *sig, u8 **iv,
4452+
struct aead_request **req, struct scatterlist **sgl)
44444453
{
4445-
unsigned int sg_len;
4454+
unsigned int off, len, skip;
44464455
struct scatterlist *sg;
4447-
unsigned int i;
4448-
unsigned int j;
4449-
unsigned int idx = 0;
4450-
int skip;
4451-
4452-
sg_len = 1;
4453-
for (i = 0; i < num_rqst; i++)
4454-
sg_len += rqst[i].rq_nvec + rqst[i].rq_npages;
4456+
unsigned int num_sgs;
4457+
unsigned long addr;
4458+
int i, j;
4459+
void *p;
44554460

4456-
sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
4457-
if (!sg)
4461+
p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs);
4462+
if (!p)
44584463
return NULL;
44594464

4460-
sg_init_table(sg, sg_len);
4465+
sg_init_table(*sgl, num_sgs);
4466+
sg = *sgl;
4467+
4468+
/* Assumes the first rqst has a transform header as the first iov.
4469+
* I.e.
4470+
* rqst[0].rq_iov[0] is transform header
4471+
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
4472+
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
4473+
*/
44614474
for (i = 0; i < num_rqst; i++) {
4475+
/*
4476+
* The first rqst has a transform header where the
4477+
* first 20 bytes are not part of the encrypted blob.
4478+
*/
44624479
for (j = 0; j < rqst[i].rq_nvec; j++) {
4463-
/*
4464-
* The first rqst has a transform header where the
4465-
* first 20 bytes are not part of the encrypted blob
4466-
*/
4467-
skip = (i == 0) && (j == 0) ? 20 : 0;
4468-
smb2_sg_set_buf(&sg[idx++],
4469-
rqst[i].rq_iov[j].iov_base + skip,
4470-
rqst[i].rq_iov[j].iov_len - skip);
4471-
}
4480+
struct kvec *iov = &rqst[i].rq_iov[j];
44724481

4482+
skip = (i == 0) && (j == 0) ? 20 : 0;
4483+
addr = (unsigned long)iov->iov_base + skip;
4484+
len = iov->iov_len - skip;
4485+
sg = cifs_sg_set_buf(sg, (void *)addr, len);
4486+
}
44734487
for (j = 0; j < rqst[i].rq_npages; j++) {
4474-
unsigned int len, offset;
4475-
4476-
rqst_page_get_length(&rqst[i], j, &len, &offset);
4477-
sg_set_page(&sg[idx++], rqst[i].rq_pages[j], len, offset);
4488+
rqst_page_get_length(&rqst[i], j, &len, &off);
4489+
sg_set_page(sg++, rqst[i].rq_pages[j], len, off);
44784490
}
44794491
}
4480-
smb2_sg_set_buf(&sg[idx], sign, SMB2_SIGNATURE_SIZE);
4481-
return sg;
4492+
cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE);
4493+
4494+
return p;
44824495
}
44834496

44844497
static int
@@ -4522,11 +4535,11 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
45224535
u8 sign[SMB2_SIGNATURE_SIZE] = {};
45234536
u8 key[SMB3_ENC_DEC_KEY_SIZE];
45244537
struct aead_request *req;
4525-
char *iv;
4526-
unsigned int iv_len;
4538+
u8 *iv;
45274539
DECLARE_CRYPTO_WAIT(wait);
45284540
struct crypto_aead *tfm;
45294541
unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
4542+
void *creq;
45304543

45314544
rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key);
45324545
if (rc) {
@@ -4561,32 +4574,15 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
45614574
return rc;
45624575
}
45634576

4564-
req = aead_request_alloc(tfm, GFP_KERNEL);
4565-
if (!req) {
4566-
cifs_server_dbg(VFS, "%s: Failed to alloc aead request\n", __func__);
4577+
creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg);
4578+
if (unlikely(!creq))
45674579
return -ENOMEM;
4568-
}
45694580

45704581
if (!enc) {
45714582
memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
45724583
crypt_len += SMB2_SIGNATURE_SIZE;
45734584
}
45744585

4575-
sg = init_sg(num_rqst, rqst, sign);
4576-
if (!sg) {
4577-
cifs_server_dbg(VFS, "%s: Failed to init sg\n", __func__);
4578-
rc = -ENOMEM;
4579-
goto free_req;
4580-
}
4581-
4582-
iv_len = crypto_aead_ivsize(tfm);
4583-
iv = kzalloc(iv_len, GFP_KERNEL);
4584-
if (!iv) {
4585-
cifs_server_dbg(VFS, "%s: Failed to alloc iv\n", __func__);
4586-
rc = -ENOMEM;
4587-
goto free_sg;
4588-
}
4589-
45904586
if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) ||
45914587
(server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
45924588
memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE);
@@ -4595,6 +4591,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
45954591
memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE);
45964592
}
45974593

4594+
aead_request_set_tfm(req, tfm);
45984595
aead_request_set_crypt(req, sg, sg, crypt_len, iv);
45994596
aead_request_set_ad(req, assoc_data_len);
46004597

@@ -4607,11 +4604,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
46074604
if (!rc && enc)
46084605
memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
46094606

4610-
kfree(iv);
4611-
free_sg:
4612-
kfree(sg);
4613-
free_req:
4614-
kfree(req);
4607+
kfree_sensitive(creq);
46154608
return rc;
46164609
}
46174610

0 commit comments

Comments
 (0)