Skip to content

Commit d527f51

Browse files
z00467499Steve French
authored and
Steve French
committed
cifs: Fix UAF in cifs_demultiplex_thread()
There is a UAF when xfstests on cifs: BUG: KASAN: use-after-free in smb2_is_network_name_deleted+0x27/0x160 Read of size 4 at addr ffff88810103fc08 by task cifsd/923 CPU: 1 PID: 923 Comm: cifsd Not tainted 6.1.0-rc4+ #45 ... Call Trace: <TASK> dump_stack_lvl+0x34/0x44 print_report+0x171/0x472 kasan_report+0xad/0x130 kasan_check_range+0x145/0x1a0 smb2_is_network_name_deleted+0x27/0x160 cifs_demultiplex_thread.cold+0x172/0x5a4 kthread+0x165/0x1a0 ret_from_fork+0x1f/0x30 </TASK> Allocated by task 923: kasan_save_stack+0x1e/0x40 kasan_set_track+0x21/0x30 __kasan_slab_alloc+0x54/0x60 kmem_cache_alloc+0x147/0x320 mempool_alloc+0xe1/0x260 cifs_small_buf_get+0x24/0x60 allocate_buffers+0xa1/0x1c0 cifs_demultiplex_thread+0x199/0x10d0 kthread+0x165/0x1a0 ret_from_fork+0x1f/0x30 Freed by task 921: kasan_save_stack+0x1e/0x40 kasan_set_track+0x21/0x30 kasan_save_free_info+0x2a/0x40 ____kasan_slab_free+0x143/0x1b0 kmem_cache_free+0xe3/0x4d0 cifs_small_buf_release+0x29/0x90 SMB2_negotiate+0x8b7/0x1c60 smb2_negotiate+0x51/0x70 cifs_negotiate_protocol+0xf0/0x160 cifs_get_smb_ses+0x5fa/0x13c0 mount_get_conns+0x7a/0x750 cifs_mount+0x103/0xd00 cifs_smb3_do_mount+0x1dd/0xcb0 smb3_get_tree+0x1d5/0x300 vfs_get_tree+0x41/0xf0 path_mount+0x9b3/0xdd0 __x64_sys_mount+0x190/0x1d0 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 The UAF is because: mount(pid: 921) | cifsd(pid: 923) -------------------------------|------------------------------- | cifs_demultiplex_thread SMB2_negotiate | cifs_send_recv | compound_send_recv | smb_send_rqst | wait_for_response | wait_event_state [1] | | standard_receive3 | cifs_handle_standard | handle_mid | mid->resp_buf = buf; [2] | dequeue_mid [3] KILL the process [4] | resp_iov[i].iov_base = buf | free_rsp_buf [5] | | is_network_name_deleted [6] | callback 1. After send request to server, wait the response until mid->mid_state != SUBMITTED; 2. Receive response from server, and set it to mid; 3. Set the mid state to RECEIVED; 4. Kill the process, the mid state already RECEIVED, get 0; 5. Handle and release the negotiate response; 6. UAF. It can be easily reproduce with add some delay in [3] - [6]. Only sync call has the problem since async call's callback is executed in cifsd process. Add an extra state to mark the mid state to READY before wakeup the waitter, then it can get the resp safely. Fixes: ec637e3 ("[CIFS] Avoid extra large buffer allocation (and memcpy) in cifs_readpages") Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 2da338f commit d527f51

File tree

2 files changed

+24
-11
lines changed

2 files changed

+24
-11
lines changed

fs/smb/client/cifsglob.h

+1
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,7 @@ static inline bool is_retryable_error(int error)
18071807
#define MID_RETRY_NEEDED 8 /* session closed while this request out */
18081808
#define MID_RESPONSE_MALFORMED 0x10
18091809
#define MID_SHUTDOWN 0x20
1810+
#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
18101811

18111812
/* Flags */
18121813
#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */

fs/smb/client/transport.c

+23-11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
void
3636
cifs_wake_up_task(struct mid_q_entry *mid)
3737
{
38+
if (mid->mid_state == MID_RESPONSE_RECEIVED)
39+
mid->mid_state = MID_RESPONSE_READY;
3840
wake_up_process(mid->callback_data);
3941
}
4042

@@ -87,7 +89,8 @@ static void __release_mid(struct kref *refcount)
8789
struct TCP_Server_Info *server = midEntry->server;
8890

8991
if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
90-
midEntry->mid_state == MID_RESPONSE_RECEIVED &&
92+
(midEntry->mid_state == MID_RESPONSE_RECEIVED ||
93+
midEntry->mid_state == MID_RESPONSE_READY) &&
9194
server->ops->handle_cancelled_mid)
9295
server->ops->handle_cancelled_mid(midEntry, server);
9396

@@ -737,7 +740,8 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
737740
int error;
738741

739742
error = wait_event_state(server->response_q,
740-
midQ->mid_state != MID_REQUEST_SUBMITTED,
743+
midQ->mid_state != MID_REQUEST_SUBMITTED &&
744+
midQ->mid_state != MID_RESPONSE_RECEIVED,
741745
(TASK_KILLABLE|TASK_FREEZABLE_UNSAFE));
742746
if (error < 0)
743747
return -ERESTARTSYS;
@@ -890,7 +894,7 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
890894

891895
spin_lock(&server->mid_lock);
892896
switch (mid->mid_state) {
893-
case MID_RESPONSE_RECEIVED:
897+
case MID_RESPONSE_READY:
894898
spin_unlock(&server->mid_lock);
895899
return rc;
896900
case MID_RETRY_NEEDED:
@@ -989,6 +993,9 @@ cifs_compound_callback(struct mid_q_entry *mid)
989993
credits.instance = server->reconnect_instance;
990994

991995
add_credits(server, &credits, mid->optype);
996+
997+
if (mid->mid_state == MID_RESPONSE_RECEIVED)
998+
mid->mid_state = MID_RESPONSE_READY;
992999
}
9931000

9941001
static void
@@ -1209,7 +1216,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
12091216
send_cancel(server, &rqst[i], midQ[i]);
12101217
spin_lock(&server->mid_lock);
12111218
midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
1212-
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
1219+
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED ||
1220+
midQ[i]->mid_state == MID_RESPONSE_RECEIVED) {
12131221
midQ[i]->callback = cifs_cancelled_callback;
12141222
cancelled_mid[i] = true;
12151223
credits[i].value = 0;
@@ -1230,7 +1238,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
12301238
}
12311239

12321240
if (!midQ[i]->resp_buf ||
1233-
midQ[i]->mid_state != MID_RESPONSE_RECEIVED) {
1241+
midQ[i]->mid_state != MID_RESPONSE_READY) {
12341242
rc = -EIO;
12351243
cifs_dbg(FYI, "Bad MID state?\n");
12361244
goto out;
@@ -1417,7 +1425,8 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
14171425
if (rc != 0) {
14181426
send_cancel(server, &rqst, midQ);
14191427
spin_lock(&server->mid_lock);
1420-
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
1428+
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
1429+
midQ->mid_state == MID_RESPONSE_RECEIVED) {
14211430
/* no longer considered to be "in-flight" */
14221431
midQ->callback = release_mid;
14231432
spin_unlock(&server->mid_lock);
@@ -1434,7 +1443,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
14341443
}
14351444

14361445
if (!midQ->resp_buf || !out_buf ||
1437-
midQ->mid_state != MID_RESPONSE_RECEIVED) {
1446+
midQ->mid_state != MID_RESPONSE_READY) {
14381447
rc = -EIO;
14391448
cifs_server_dbg(VFS, "Bad MID state?\n");
14401449
goto out;
@@ -1558,14 +1567,16 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
15581567

15591568
/* Wait for a reply - allow signals to interrupt. */
15601569
rc = wait_event_interruptible(server->response_q,
1561-
(!(midQ->mid_state == MID_REQUEST_SUBMITTED)) ||
1570+
(!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
1571+
midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
15621572
((server->tcpStatus != CifsGood) &&
15631573
(server->tcpStatus != CifsNew)));
15641574

15651575
/* Were we interrupted by a signal ? */
15661576
spin_lock(&server->srv_lock);
15671577
if ((rc == -ERESTARTSYS) &&
1568-
(midQ->mid_state == MID_REQUEST_SUBMITTED) &&
1578+
(midQ->mid_state == MID_REQUEST_SUBMITTED ||
1579+
midQ->mid_state == MID_RESPONSE_RECEIVED) &&
15691580
((server->tcpStatus == CifsGood) ||
15701581
(server->tcpStatus == CifsNew))) {
15711582
spin_unlock(&server->srv_lock);
@@ -1596,7 +1607,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
15961607
if (rc) {
15971608
send_cancel(server, &rqst, midQ);
15981609
spin_lock(&server->mid_lock);
1599-
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
1610+
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
1611+
midQ->mid_state == MID_RESPONSE_RECEIVED) {
16001612
/* no longer considered to be "in-flight" */
16011613
midQ->callback = release_mid;
16021614
spin_unlock(&server->mid_lock);
@@ -1616,7 +1628,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
16161628
return rc;
16171629

16181630
/* rcvd frame is ok */
1619-
if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {
1631+
if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
16201632
rc = -EIO;
16211633
cifs_tcon_dbg(VFS, "Bad MID state?\n");
16221634
goto out;

0 commit comments

Comments
 (0)