Skip to content

Commit abfdd13

Browse files
committed
cifs: fix oops during encryption
jira VULN-154755 cve CVE-2022-50341 commit-author Paulo Alcantara <pc@cjr.nz> commit f7f291e upstream-diff Used 5.15 LT bf0543b but still had some conflicts due to context changes 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> (cherry picked from commit f7f291e) Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com> (cherry picked from commit f6286d5) Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
1 parent 92b4795 commit abfdd13

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 <linux/utsname.h>
@@ -25,6 +27,7 @@
2527
#include <uapi/linux/cifs/cifs_mount.h>
2628
#include "../smbfs_common/smb2pdu.h"
2729
#include "smb2pdu.h"
30+
#include "smb2glob.h"
2831

2932
#define SMB_PATH_MAX 260
3033
#define CIFS_PORT 445
@@ -2117,4 +2120,70 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
21172120
return sizeof(ses->workstation_name);
21182121
}
21192122

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

0 commit comments

Comments
 (0)