Skip to content

Commit 0a247f3

Browse files
committed
cifs: fix oops during encryption
jira VULN-154755 cve CVE-2022-50341 commit-author Paulo Alcantara <[email protected]> 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) <[email protected]> Cc: [email protected] Signed-off-by: Steve French <[email protected]> (cherry picked from commit f7f291e) Signed-off-by: Brett Mastbergen <[email protected]> # Conflicts: # fs/cifs/cifsglob.h
1 parent 1cca91e commit 0a247f3

File tree

4 files changed

+140
-79
lines changed

4 files changed

+140
-79
lines changed

fs/cifs/cifsglob.h

Lines changed: 68 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 <linux/utsname.h>
@@ -2117,4 +2119,70 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
21172119
return sizeof(ses->workstation_name);
21182120
}
21192121

2122+
static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst,
2123+
int num_rqst,
2124+
const u8 *sig)
2125+
{
2126+
unsigned int len, skip;
2127+
unsigned int nents = 0;
2128+
unsigned long addr;
2129+
int i, j;
2130+
2131+
/* Assumes the first rqst has a transform header as the first iov.
2132+
* I.e.
2133+
* rqst[0].rq_iov[0] is transform header
2134+
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
2135+
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
2136+
*/
2137+
for (i = 0; i < num_rqst; i++) {
2138+
/*
2139+
* The first rqst has a transform header where the
2140+
* first 20 bytes are not part of the encrypted blob.
2141+
*/
2142+
for (j = 0; j < rqst[i].rq_nvec; j++) {
2143+
struct kvec *iov = &rqst[i].rq_iov[j];
2144+
2145+
skip = (i == 0) && (j == 0) ? 20 : 0;
2146+
addr = (unsigned long)iov->iov_base + skip;
2147+
if (unlikely(is_vmalloc_addr((void *)addr))) {
2148+
len = iov->iov_len - skip;
2149+
nents += DIV_ROUND_UP(offset_in_page(addr) + len,
2150+
PAGE_SIZE);
2151+
} else {
2152+
nents++;
2153+
}
2154+
}
2155+
nents += rqst[i].rq_npages;
2156+
}
2157+
nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
2158+
return nents;
2159+
}
2160+
2161+
/* We can not use the normal sg_set_buf() as we will sometimes pass a
2162+
* stack object as buf.
2163+
*/
2164+
static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg,
2165+
const void *buf,
2166+
unsigned int buflen)
2167+
{
2168+
unsigned long addr = (unsigned long)buf;
2169+
unsigned int off = offset_in_page(addr);
2170+
2171+
addr &= PAGE_MASK;
2172+
if (unlikely(is_vmalloc_addr((void *)addr))) {
2173+
do {
2174+
unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);
2175+
2176+
sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off);
2177+
2178+
off = 0;
2179+
addr += PAGE_SIZE;
2180+
buflen -= len;
2181+
} while (buflen);
2182+
} else {
2183+
sg_set_page(sg++, virt_to_page(addr), buflen, off);
2184+
}
2185+
return sg;
2186+
}
2187+
21202188
#endif /* _CIFS_GLOB_H */

fs/cifs/cifsproto.h

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

609-
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
610-
unsigned int *len, unsigned int *offset);
609+
void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page,
610+
unsigned int *len, unsigned int *offset);
611611
struct cifs_chan *
612612
cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server);
613613
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
@@ -1132,8 +1132,8 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
11321132
* @len: Where to store the length for this page:
11331133
* @offset: Where to store the offset for this page
11341134
*/
1135-
void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
1136-
unsigned int *len, unsigned int *offset)
1135+
void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page,
1136+
unsigned int *len, unsigned int *offset)
11371137
{
11381138
*len = rqst->rq_pagesz;
11391139
*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
@@ -4197,69 +4197,82 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
41974197
memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8);
41984198
}
41994199

4200-
/* We can not use the normal sg_set_buf() as we will sometimes pass a
4201-
* stack object as buf.
4202-
*/
4203-
static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
4204-
unsigned int buflen)
4200+
static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst,
4201+
int num_rqst, const u8 *sig, u8 **iv,
4202+
struct aead_request **req, struct scatterlist **sgl,
4203+
unsigned int *num_sgs)
42054204
{
4206-
void *addr;
4207-
/*
4208-
* VMAP_STACK (at least) puts stack into the vmalloc address space
4209-
*/
4210-
if (is_vmalloc_addr(buf))
4211-
addr = vmalloc_to_page(buf);
4212-
else
4213-
addr = virt_to_page(buf);
4214-
sg_set_page(sg, addr, buflen, offset_in_page(buf));
4205+
unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
4206+
unsigned int iv_size = crypto_aead_ivsize(tfm);
4207+
unsigned int len;
4208+
u8 *p;
4209+
4210+
*num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig);
4211+
4212+
len = iv_size;
4213+
len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
4214+
len = ALIGN(len, crypto_tfm_ctx_alignment());
4215+
len += req_size;
4216+
len = ALIGN(len, __alignof__(struct scatterlist));
4217+
len += *num_sgs * sizeof(**sgl);
4218+
4219+
p = kmalloc(len, GFP_ATOMIC);
4220+
if (!p)
4221+
return NULL;
4222+
4223+
*iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1);
4224+
*req = (struct aead_request *)PTR_ALIGN(*iv + iv_size,
4225+
crypto_tfm_ctx_alignment());
4226+
*sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size,
4227+
__alignof__(struct scatterlist));
4228+
return p;
42154229
}
42164230

4217-
/* Assumes the first rqst has a transform header as the first iov.
4218-
* I.e.
4219-
* rqst[0].rq_iov[0] is transform header
4220-
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
4221-
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
4222-
*/
4223-
static struct scatterlist *
4224-
init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
4231+
static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst,
4232+
int num_rqst, const u8 *sig, u8 **iv,
4233+
struct aead_request **req, struct scatterlist **sgl)
42254234
{
4226-
unsigned int sg_len;
4235+
unsigned int off, len, skip;
42274236
struct scatterlist *sg;
4228-
unsigned int i;
4229-
unsigned int j;
4230-
unsigned int idx = 0;
4231-
int skip;
4232-
4233-
sg_len = 1;
4234-
for (i = 0; i < num_rqst; i++)
4235-
sg_len += rqst[i].rq_nvec + rqst[i].rq_npages;
4237+
unsigned int num_sgs;
4238+
unsigned long addr;
4239+
int i, j;
4240+
void *p;
42364241

4237-
sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
4238-
if (!sg)
4242+
p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs);
4243+
if (!p)
42394244
return NULL;
42404245

4241-
sg_init_table(sg, sg_len);
4246+
sg_init_table(*sgl, num_sgs);
4247+
sg = *sgl;
4248+
4249+
/* Assumes the first rqst has a transform header as the first iov.
4250+
* I.e.
4251+
* rqst[0].rq_iov[0] is transform header
4252+
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
4253+
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
4254+
*/
42424255
for (i = 0; i < num_rqst; i++) {
4256+
/*
4257+
* The first rqst has a transform header where the
4258+
* first 20 bytes are not part of the encrypted blob.
4259+
*/
42434260
for (j = 0; j < rqst[i].rq_nvec; j++) {
4244-
/*
4245-
* The first rqst has a transform header where the
4246-
* first 20 bytes are not part of the encrypted blob
4247-
*/
4248-
skip = (i == 0) && (j == 0) ? 20 : 0;
4249-
smb2_sg_set_buf(&sg[idx++],
4250-
rqst[i].rq_iov[j].iov_base + skip,
4251-
rqst[i].rq_iov[j].iov_len - skip);
4252-
}
4261+
struct kvec *iov = &rqst[i].rq_iov[j];
42534262

4263+
skip = (i == 0) && (j == 0) ? 20 : 0;
4264+
addr = (unsigned long)iov->iov_base + skip;
4265+
len = iov->iov_len - skip;
4266+
sg = cifs_sg_set_buf(sg, (void *)addr, len);
4267+
}
42544268
for (j = 0; j < rqst[i].rq_npages; j++) {
4255-
unsigned int len, offset;
4256-
4257-
rqst_page_get_length(&rqst[i], j, &len, &offset);
4258-
sg_set_page(&sg[idx++], rqst[i].rq_pages[j], len, offset);
4269+
rqst_page_get_length(&rqst[i], j, &len, &off);
4270+
sg_set_page(sg++, rqst[i].rq_pages[j], len, off);
42594271
}
42604272
}
4261-
smb2_sg_set_buf(&sg[idx], sign, SMB2_SIGNATURE_SIZE);
4262-
return sg;
4273+
cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE);
4274+
4275+
return p;
42634276
}
42644277

42654278
static int
@@ -4305,11 +4318,11 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
43054318
u8 sign[SMB2_SIGNATURE_SIZE] = {};
43064319
u8 key[SMB3_ENC_DEC_KEY_SIZE];
43074320
struct aead_request *req;
4308-
char *iv;
4309-
unsigned int iv_len;
4321+
u8 *iv;
43104322
DECLARE_CRYPTO_WAIT(wait);
43114323
struct crypto_aead *tfm;
43124324
unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
4325+
void *creq;
43134326

43144327
rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key);
43154328
if (rc) {
@@ -4344,32 +4357,15 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
43444357
return rc;
43454358
}
43464359

4347-
req = aead_request_alloc(tfm, GFP_KERNEL);
4348-
if (!req) {
4349-
cifs_server_dbg(VFS, "%s: Failed to alloc aead request\n", __func__);
4360+
creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg);
4361+
if (unlikely(!creq))
43504362
return -ENOMEM;
4351-
}
43524363

43534364
if (!enc) {
43544365
memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
43554366
crypt_len += SMB2_SIGNATURE_SIZE;
43564367
}
43574368

4358-
sg = init_sg(num_rqst, rqst, sign);
4359-
if (!sg) {
4360-
cifs_server_dbg(VFS, "%s: Failed to init sg\n", __func__);
4361-
rc = -ENOMEM;
4362-
goto free_req;
4363-
}
4364-
4365-
iv_len = crypto_aead_ivsize(tfm);
4366-
iv = kzalloc(iv_len, GFP_KERNEL);
4367-
if (!iv) {
4368-
cifs_server_dbg(VFS, "%s: Failed to alloc iv\n", __func__);
4369-
rc = -ENOMEM;
4370-
goto free_sg;
4371-
}
4372-
43734369
if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) ||
43744370
(server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
43754371
memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE);
@@ -4378,6 +4374,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
43784374
memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE);
43794375
}
43804376

4377+
aead_request_set_tfm(req, tfm);
43814378
aead_request_set_crypt(req, sg, sg, crypt_len, iv);
43824379
aead_request_set_ad(req, assoc_data_len);
43834380

@@ -4390,11 +4387,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
43904387
if (!rc && enc)
43914388
memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
43924389

4393-
kfree_sensitive(iv);
4394-
free_sg:
4395-
kfree_sensitive(sg);
4396-
free_req:
4397-
kfree_sensitive(req);
4390+
kfree_sensitive(creq);
43984391
return rc;
43994392
}
44004393

0 commit comments

Comments
 (0)