Skip to content

Commit 36d7e76

Browse files
Paulo Alcantarasmb49
authored andcommitted
cifs: fix NULL ptr dereference in smb2_ioctl_query_info()
BugLink: https://bugs.launchpad.net/bugs/1969110 commit d6f5e35 upstream. When calling smb2_ioctl_query_info() with invalid smb_query_info::flags, a NULL ptr dereference is triggered when trying to kfree() uninitialised rqst[n].rq_iov array. This also fixes leaked paths that are created in SMB2_open_init() which required SMB2_open_free() to properly free them. Here is a small C reproducer that triggers it #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #define die(s) perror(s), exit(1) #define QUERY_INFO 0xc018cf07 int main(int argc, char *argv[]) { int fd; if (argc < 2) exit(1); fd = open(argv[1], O_RDONLY); if (fd == -1) die("open"); if (ioctl(fd, QUERY_INFO, (uint32_t[]) { 0, 0, 0, 4, 0, 0}) == -1) die("ioctl"); close(fd); return 0; } mount.cifs //srv/share /mnt -o ... gcc repro.c && ./a.out /mnt/f0 [ 1832.124468] CIFS: VFS: \\w22-dc.zelda.test\test Invalid passthru query flags: 0x4 [ 1832.125043] general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN NOPTI [ 1832.125764] KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] [ 1832.126241] CPU: 3 PID: 1133 Comm: a.out Not tainted 5.17.0-rc8 #2 [ 1832.126630] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.15.0-0-g2dd4b9b-rebuilt.opensuse.org 04/01/2014 [ 1832.127322] RIP: 0010:smb2_ioctl_query_info+0x7a3/0xe30 [cifs] [ 1832.127749] Code: 00 00 00 fc ff df 48 c1 ea 03 80 3c 02 00 0f 85 6c 05 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 74 24 28 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 cb 04 00 00 49 8b 3e e8 bb fc fa ff 48 89 da 48 [ 1832.128911] RSP: 0018:ffffc90000957b08 EFLAGS: 00010256 [ 1832.129243] RAX: dffffc0000000000 RBX: ffff888117e9b850 RCX: ffffffffa020580d [ 1832.129691] RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffffffa043a2c0 [ 1832.130137] RBP: ffff888117e9b878 R08: 0000000000000001 R09: 0000000000000003 [ 1832.130585] R10: fffffbfff4087458 R11: 0000000000000001 R12: ffff888117e9b800 [ 1832.131037] R13: 00000000ffffffea R14: 0000000000000000 R15: ffff888117e9b8a8 [ 1832.131485] FS: 00007fcee9900740(0000) GS:ffff888151a00000(0000) knlGS:0000000000000000 [ 1832.131993] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1832.132354] CR2: 00007fcee9a1ef5e CR3: 0000000114cd2000 CR4: 0000000000350ee0 [ 1832.132801] Call Trace: [ 1832.132962] <TASK> [ 1832.133104] ? smb2_query_reparse_tag+0x890/0x890 [cifs] [ 1832.133489] ? cifs_mapchar+0x460/0x460 [cifs] [ 1832.133822] ? rcu_read_lock_sched_held+0x3f/0x70 [ 1832.134125] ? cifs_strndup_to_utf16+0x15b/0x250 [cifs] [ 1832.134502] ? lock_downgrade+0x6f0/0x6f0 [ 1832.134760] ? cifs_convert_path_to_utf16+0x198/0x220 [cifs] [ 1832.135170] ? smb2_check_message+0x1080/0x1080 [cifs] [ 1832.135545] cifs_ioctl+0x1577/0x3320 [cifs] [ 1832.135864] ? lock_downgrade+0x6f0/0x6f0 [ 1832.136125] ? cifs_readdir+0x2e60/0x2e60 [cifs] [ 1832.136468] ? rcu_read_lock_sched_held+0x3f/0x70 [ 1832.136769] ? __rseq_handle_notify_resume+0x80b/0xbe0 [ 1832.137096] ? __up_read+0x192/0x710 [ 1832.137327] ? __ia32_sys_rseq+0xf0/0xf0 [ 1832.137578] ? __x64_sys_openat+0x11f/0x1d0 [ 1832.137850] __x64_sys_ioctl+0x127/0x190 [ 1832.138103] do_syscall_64+0x3b/0x90 [ 1832.138378] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 1832.138702] RIP: 0033:0x7fcee9a253df [ 1832.138937] 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 <41> 89 c0 3d 00 f0 ff ff 77 1f 48 8b 44 24 18 64 48 2b 04 25 28 00 [ 1832.140107] RSP: 002b:00007ffeba94a8a0 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 1832.140606] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fcee9a253df [ 1832.141058] RDX: 00007ffeba94a910 RSI: 00000000c018cf07 RDI: 0000000000000003 [ 1832.141503] RBP: 00007ffeba94a930 R08: 00007fcee9b24db0 R09: 00007fcee9b45c4e [ 1832.141948] R10: 00007fcee9918d40 R11: 0000000000000246 R12: 00007ffeba94aa48 [ 1832.142396] R13: 0000000000401176 R14: 0000000000403df8 R15: 00007fcee9b78000 [ 1832.142851] </TASK> [ 1832.142994] Modules linked in: cifs cifs_arc4 cifs_md4 bpf_preload [last unloaded: cifs] Cc: [email protected] Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit 39a4bf7d1a23dd172526c2fb0db480c5d5c63bd6) Signed-off-by: Paolo Pisati <[email protected]>
1 parent 066cd34 commit 36d7e76

File tree

1 file changed

+65
-59
lines changed

1 file changed

+65
-59
lines changed

fs/cifs/smb2ops.c

Lines changed: 65 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,7 @@ smb2_ioctl_query_info(const unsigned int xid,
16311631
unsigned int size[2];
16321632
void *data[2];
16331633
int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR;
1634+
void (*free_req1_func)(struct smb_rqst *r);
16341635

16351636
vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
16361637
if (vars == NULL)
@@ -1640,17 +1641,18 @@ smb2_ioctl_query_info(const unsigned int xid,
16401641

16411642
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
16421643

1643-
if (copy_from_user(&qi, arg, sizeof(struct smb_query_info)))
1644-
goto e_fault;
1645-
1644+
if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) {
1645+
rc = -EFAULT;
1646+
goto free_vars;
1647+
}
16461648
if (qi.output_buffer_length > 1024) {
1647-
kfree(vars);
1648-
return -EINVAL;
1649+
rc = -EINVAL;
1650+
goto free_vars;
16491651
}
16501652

16511653
if (!ses || !server) {
1652-
kfree(vars);
1653-
return -EIO;
1654+
rc = -EIO;
1655+
goto free_vars;
16541656
}
16551657

16561658
if (smb3_encryption_required(tcon))
@@ -1659,8 +1661,8 @@ smb2_ioctl_query_info(const unsigned int xid,
16591661
if (qi.output_buffer_length) {
16601662
buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length);
16611663
if (IS_ERR(buffer)) {
1662-
kfree(vars);
1663-
return PTR_ERR(buffer);
1664+
rc = PTR_ERR(buffer);
1665+
goto free_vars;
16641666
}
16651667
}
16661668

@@ -1699,48 +1701,45 @@ smb2_ioctl_query_info(const unsigned int xid,
16991701
rc = SMB2_open_init(tcon, server,
17001702
&rqst[0], &oplock, &oparms, path);
17011703
if (rc)
1702-
goto iqinf_exit;
1704+
goto free_output_buffer;
17031705
smb2_set_next_command(tcon, &rqst[0]);
17041706

17051707
/* Query */
17061708
if (qi.flags & PASSTHRU_FSCTL) {
17071709
/* Can eventually relax perm check since server enforces too */
1708-
if (!capable(CAP_SYS_ADMIN))
1710+
if (!capable(CAP_SYS_ADMIN)) {
17091711
rc = -EPERM;
1710-
else {
1711-
rqst[1].rq_iov = &vars->io_iov[0];
1712-
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
1713-
1714-
rc = SMB2_ioctl_init(tcon, server,
1715-
&rqst[1],
1716-
COMPOUND_FID, COMPOUND_FID,
1717-
qi.info_type, true, buffer,
1718-
qi.output_buffer_length,
1719-
CIFSMaxBufSize -
1720-
MAX_SMB2_CREATE_RESPONSE_SIZE -
1721-
MAX_SMB2_CLOSE_RESPONSE_SIZE);
1712+
goto free_open_req;
17221713
}
1714+
rqst[1].rq_iov = &vars->io_iov[0];
1715+
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
1716+
1717+
rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID,
1718+
qi.info_type, true, buffer, qi.output_buffer_length,
1719+
CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE -
1720+
MAX_SMB2_CLOSE_RESPONSE_SIZE);
1721+
free_req1_func = SMB2_ioctl_free;
17231722
} else if (qi.flags == PASSTHRU_SET_INFO) {
17241723
/* Can eventually relax perm check since server enforces too */
1725-
if (!capable(CAP_SYS_ADMIN))
1724+
if (!capable(CAP_SYS_ADMIN)) {
17261725
rc = -EPERM;
1727-
else if (qi.output_buffer_length < 8)
1726+
goto free_open_req;
1727+
}
1728+
if (qi.output_buffer_length < 8) {
17281729
rc = -EINVAL;
1729-
else {
1730-
rqst[1].rq_iov = &vars->si_iov[0];
1731-
rqst[1].rq_nvec = 1;
1732-
1733-
/* MS-FSCC 2.4.13 FileEndOfFileInformation */
1734-
size[0] = 8;
1735-
data[0] = buffer;
1736-
1737-
rc = SMB2_set_info_init(tcon, server,
1738-
&rqst[1],
1739-
COMPOUND_FID, COMPOUND_FID,
1740-
current->tgid,
1741-
FILE_END_OF_FILE_INFORMATION,
1742-
SMB2_O_INFO_FILE, 0, data, size);
1730+
goto free_open_req;
17431731
}
1732+
rqst[1].rq_iov = &vars->si_iov[0];
1733+
rqst[1].rq_nvec = 1;
1734+
1735+
/* MS-FSCC 2.4.13 FileEndOfFileInformation */
1736+
size[0] = 8;
1737+
data[0] = buffer;
1738+
1739+
rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID,
1740+
current->tgid, FILE_END_OF_FILE_INFORMATION,
1741+
SMB2_O_INFO_FILE, 0, data, size);
1742+
free_req1_func = SMB2_set_info_free;
17441743
} else if (qi.flags == PASSTHRU_QUERY_INFO) {
17451744
rqst[1].rq_iov = &vars->qi_iov[0];
17461745
rqst[1].rq_nvec = 1;
@@ -1751,14 +1750,15 @@ smb2_ioctl_query_info(const unsigned int xid,
17511750
qi.info_type, qi.additional_information,
17521751
qi.input_buffer_length,
17531752
qi.output_buffer_length, buffer);
1753+
free_req1_func = SMB2_query_info_free;
17541754
} else { /* unknown flags */
17551755
cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n",
17561756
qi.flags);
17571757
rc = -EINVAL;
17581758
}
17591759

17601760
if (rc)
1761-
goto iqinf_exit;
1761+
goto free_open_req;
17621762
smb2_set_next_command(tcon, &rqst[1]);
17631763
smb2_set_related(&rqst[1]);
17641764

@@ -1769,14 +1769,14 @@ smb2_ioctl_query_info(const unsigned int xid,
17691769
rc = SMB2_close_init(tcon, server,
17701770
&rqst[2], COMPOUND_FID, COMPOUND_FID, false);
17711771
if (rc)
1772-
goto iqinf_exit;
1772+
goto free_req_1;
17731773
smb2_set_related(&rqst[2]);
17741774

17751775
rc = compound_send_recv(xid, ses, server,
17761776
flags, 3, rqst,
17771777
resp_buftype, rsp_iov);
17781778
if (rc)
1779-
goto iqinf_exit;
1779+
goto out;
17801780

17811781
/* No need to bump num_remote_opens since handle immediately closed */
17821782
if (qi.flags & PASSTHRU_FSCTL) {
@@ -1786,47 +1786,53 @@ smb2_ioctl_query_info(const unsigned int xid,
17861786
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
17871787
if (qi.input_buffer_length > 0 &&
17881788
le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
1789-
> rsp_iov[1].iov_len)
1790-
goto e_fault;
1789+
> rsp_iov[1].iov_len) {
1790+
rc = -EFAULT;
1791+
goto out;
1792+
}
17911793

17921794
if (copy_to_user(&pqi->input_buffer_length,
17931795
&qi.input_buffer_length,
1794-
sizeof(qi.input_buffer_length)))
1795-
goto e_fault;
1796+
sizeof(qi.input_buffer_length))) {
1797+
rc = -EFAULT;
1798+
goto out;
1799+
}
17961800

17971801
if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info),
17981802
(const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset),
17991803
qi.input_buffer_length))
1800-
goto e_fault;
1804+
rc = -EFAULT;
18011805
} else {
18021806
pqi = (struct smb_query_info __user *)arg;
18031807
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
18041808
if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length)
18051809
qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
18061810
if (copy_to_user(&pqi->input_buffer_length,
18071811
&qi.input_buffer_length,
1808-
sizeof(qi.input_buffer_length)))
1809-
goto e_fault;
1812+
sizeof(qi.input_buffer_length))) {
1813+
rc = -EFAULT;
1814+
goto out;
1815+
}
18101816

18111817
if (copy_to_user(pqi + 1, qi_rsp->Buffer,
18121818
qi.input_buffer_length))
1813-
goto e_fault;
1819+
rc = -EFAULT;
18141820
}
18151821

1816-
iqinf_exit:
1817-
cifs_small_buf_release(rqst[0].rq_iov[0].iov_base);
1818-
cifs_small_buf_release(rqst[1].rq_iov[0].iov_base);
1819-
cifs_small_buf_release(rqst[2].rq_iov[0].iov_base);
1822+
out:
18201823
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
18211824
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
18221825
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
1823-
kfree(vars);
1826+
SMB2_close_free(&rqst[2]);
1827+
free_req_1:
1828+
free_req1_func(&rqst[1]);
1829+
free_open_req:
1830+
SMB2_open_free(&rqst[0]);
1831+
free_output_buffer:
18241832
kfree(buffer);
1833+
free_vars:
1834+
kfree(vars);
18251835
return rc;
1826-
1827-
e_fault:
1828-
rc = -EFAULT;
1829-
goto iqinf_exit;
18301836
}
18311837

18321838
static ssize_t

0 commit comments

Comments
 (0)