Commit fc6c6a3c authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French
Browse files

ksmbd: fix out-of-bound read in parse_lease_state()



This bug is in parse_lease_state, and it is caused by the missing check
of `struct create_context`. When the ksmbd traverses the create_contexts,
it doesn't check if the field of `NameOffset` and `Next` is valid,
The KASAN message is following:

[    6.664323] BUG: KASAN: slab-out-of-bounds in parse_lease_state+0x7d/0x280
[    6.664738] Read of size 2 at addr ffff888005c08988 by task kworker/0:3/103
...
[    6.666644] Call Trace:
[    6.666796]  <TASK>
[    6.666933]  dump_stack_lvl+0x33/0x50
[    6.667167]  print_report+0xcc/0x620
[    6.667903]  kasan_report+0xae/0xe0
[    6.668374]  kasan_check_range+0x35/0x1b0
[    6.668621]  parse_lease_state+0x7d/0x280
[    6.668868]  smb2_open+0xbe8/0x4420
[    6.675137]  handle_ksmbd_work+0x282/0x820

Use smb2_find_context_vals() to find smb2 create request lease context.
smb2_find_context_vals validate create context fields.

Cc: stable@vger.kernel.org
Reported-by: default avatarChih-Yen Chang <cc85nod@gmail.com>
Tested-by: default avatarChih-Yen Chang <cc85nod@gmail.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent f1a41187
Loading
Loading
Loading
Loading
+24 −42
Original line number Diff line number Diff line
@@ -1415,32 +1415,18 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
 */
struct lease_ctx_info *parse_lease_state(void *open_req)
{
	char *data_offset;
	struct create_context *cc;
	unsigned int next = 0;
	char *name;
	bool found = false;
	struct smb2_create_req *req = (struct smb2_create_req *)open_req;
	struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info),
		GFP_KERNEL);
	if (!lreq)
	struct lease_ctx_info *lreq;

	cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4);
	if (IS_ERR_OR_NULL(cc))
		return NULL;

	data_offset = (char *)req + le32_to_cpu(req->CreateContextsOffset);
	cc = (struct create_context *)data_offset;
	do {
		cc = (struct create_context *)((char *)cc + next);
		name = le16_to_cpu(cc->NameOffset) + (char *)cc;
		if (le16_to_cpu(cc->NameLength) != 4 ||
		    strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) {
			next = le32_to_cpu(cc->Next);
			continue;
		}
		found = true;
		break;
	} while (next != 0);
	lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL);
	if (!lreq)
		return NULL;

	if (found) {
	if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) {
		struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;

@@ -1463,10 +1449,6 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
	return lreq;
}

	kfree(lreq);
	return NULL;
}

/**
 * smb2_find_context_vals() - find a particular context info in open request
 * @open_req:	buffer containing smb2 file open(create) request