mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 05:32:00 +00:00
smb: client: fix OOB in SMB2_query_info_init()
A small CIFS buffer (448 bytes) isn't big enough to hold
SMB2_QUERY_INFO request along with user's input data from
CIFS_QUERY_INFO ioctl. That is, if the user passed an input buffer >
344 bytes, the client will memcpy() off the end of @req->Buffer in
SMB2_query_info_init() thus causing the following KASAN splat:
BUG: KASAN: slab-out-of-bounds in SMB2_query_info_init+0x242/0x250 [cifs]
Write of size 1023 at addr ffff88801308c5a8 by task a.out/1240
CPU: 1 PID: 1240 Comm: a.out Not tainted 6.7.0-rc4 #5
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x4a/0x80
print_report+0xcf/0x650
? srso_alias_return_thunk+0x5/0xfbef5
? srso_alias_return_thunk+0x5/0xfbef5
? srso_alias_return_thunk+0x5/0xfbef5
? __phys_addr+0x46/0x90
kasan_report+0xd8/0x110
? SMB2_query_info_init+0x242/0x250 [cifs]
? SMB2_query_info_init+0x242/0x250 [cifs]
kasan_check_range+0x105/0x1b0
__asan_memcpy+0x3c/0x60
SMB2_query_info_init+0x242/0x250 [cifs]
? __pfx_SMB2_query_info_init+0x10/0x10 [cifs]
? srso_alias_return_thunk+0x5/0xfbef5
? smb_rqst_len+0xa6/0xc0 [cifs]
smb2_ioctl_query_info+0x4f4/0x9a0 [cifs]
? __pfx_smb2_ioctl_query_info+0x10/0x10 [cifs]
? __pfx_cifsConvertToUTF16+0x10/0x10 [cifs]
? kasan_set_track+0x25/0x30
? srso_alias_return_thunk+0x5/0xfbef5
? __kasan_kmalloc+0x8f/0xa0
? srso_alias_return_thunk+0x5/0xfbef5
? cifs_strndup_to_utf16+0x12d/0x1a0 [cifs]
? __build_path_from_dentry_optional_prefix+0x19d/0x2d0 [cifs]
? __pfx_smb2_ioctl_query_info+0x10/0x10 [cifs]
cifs_ioctl+0x11c7/0x1de0 [cifs]
? __pfx_cifs_ioctl+0x10/0x10 [cifs]
? srso_alias_return_thunk+0x5/0xfbef5
? rcu_is_watching+0x23/0x50
? srso_alias_return_thunk+0x5/0xfbef5
? __rseq_handle_notify_resume+0x6cd/0x850
? __pfx___schedule+0x10/0x10
? blkcg_iostat_update+0x250/0x290
? srso_alias_return_thunk+0x5/0xfbef5
? ksys_write+0xe9/0x170
__x64_sys_ioctl+0xc9/0x100
do_syscall_64+0x47/0xf0
entry_SYSCALL_64_after_hwframe+0x6f/0x77
RIP: 0033:0x7f893dde49cf
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48
89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89>
c2 3d 00 f0 ff ff 77 18 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ffc03ff4160 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007ffc03ff4378 RCX: 00007f893dde49cf
RDX: 00007ffc03ff41d0 RSI: 00000000c018cf07 RDI: 0000000000000003
RBP: 00007ffc03ff4260 R08: 0000000000000410 R09: 0000000000000001
R10: 00007f893dce7300 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffc03ff4388 R14: 00007f893df15000 R15: 0000000000406de0
</TASK>
Fix this by increasing size of SMB2_QUERY_INFO request buffers and
validating input length to prevent other callers from overflowing @req
in SMB2_query_info_init() as well.
Fixes: f5b05d622a
("cifs: add IOCTL for QUERY_INFO passthrough to userspace")
Cc: stable@vger.kernel.org
Reported-by: Robert Morris <rtm@csail.mit.edu>
Signed-off-by: Paulo Alcantara <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
a8f68b1115
commit
33eae65c6f
@ -471,10 +471,15 @@ static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
void **request_buf, unsigned int *total_len)
|
||||
{
|
||||
/* BB eventually switch this to SMB2 specific small buf size */
|
||||
if (smb2_command == SMB2_SET_INFO)
|
||||
switch (smb2_command) {
|
||||
case SMB2_SET_INFO:
|
||||
case SMB2_QUERY_INFO:
|
||||
*request_buf = cifs_buf_get();
|
||||
else
|
||||
break;
|
||||
default:
|
||||
*request_buf = cifs_small_buf_get();
|
||||
break;
|
||||
}
|
||||
if (*request_buf == NULL) {
|
||||
/* BB should we add a retry in here if not a writepage? */
|
||||
return -ENOMEM;
|
||||
@ -3587,8 +3592,13 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
|
||||
struct smb2_query_info_req *req;
|
||||
struct kvec *iov = rqst->rq_iov;
|
||||
unsigned int total_len;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) ||
|
||||
len > CIFSMaxBufSize))
|
||||
return -EINVAL;
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server,
|
||||
(void **) &req, &total_len);
|
||||
if (rc)
|
||||
@ -3610,7 +3620,7 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
|
||||
|
||||
iov[0].iov_base = (char *)req;
|
||||
/* 1 for Buffer */
|
||||
iov[0].iov_len = total_len - 1 + input_len;
|
||||
iov[0].iov_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3618,7 +3628,7 @@ void
|
||||
SMB2_query_info_free(struct smb_rqst *rqst)
|
||||
{
|
||||
if (rqst && rqst->rq_iov)
|
||||
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
|
||||
cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */
|
||||
}
|
||||
|
||||
static int
|
||||
@ -5493,6 +5503,11 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void free_qfs_info_req(struct kvec *iov)
|
||||
{
|
||||
cifs_buf_release(iov->iov_base);
|
||||
}
|
||||
|
||||
int
|
||||
SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
|
||||
@ -5524,7 +5539,7 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = cifs_send_recv(xid, ses, server,
|
||||
&rqst, &resp_buftype, flags, &rsp_iov);
|
||||
cifs_small_buf_release(iov.iov_base);
|
||||
free_qfs_info_req(&iov);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto posix_qfsinf_exit;
|
||||
@ -5575,7 +5590,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = cifs_send_recv(xid, ses, server,
|
||||
&rqst, &resp_buftype, flags, &rsp_iov);
|
||||
cifs_small_buf_release(iov.iov_base);
|
||||
free_qfs_info_req(&iov);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto qfsinf_exit;
|
||||
@ -5642,7 +5657,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = cifs_send_recv(xid, ses, server,
|
||||
&rqst, &resp_buftype, flags, &rsp_iov);
|
||||
cifs_small_buf_release(iov.iov_base);
|
||||
free_qfs_info_req(&iov);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto qfsattr_exit;
|
||||
|
Loading…
Reference in New Issue
Block a user