Skip to content

Commit 696e420

Browse files
larperaxisSteve French
authored andcommitted
cifs: Fix use after free of a mid_q_entry
With protocol version 2.0 mounts we have seen crashes with corrupt mid entries. Either the server->pending_mid_q list becomes corrupt with a cyclic reference in one element or a mid object fetched by the demultiplexer thread becomes overwritten during use. Code review identified a race between the demultiplexer thread and the request issuing thread. The demultiplexer thread seems to be written with the assumption that it is the sole user of the mid object until it calls the mid callback which either wakes the issuer task or deletes the mid. This assumption is not true because the issuer task can be woken up earlier by a signal. If the demultiplexer thread has proceeded as far as setting the mid_state to MID_RESPONSE_RECEIVED then the issuer thread will happily end up calling cifs_delete_mid while the demultiplexer thread still is using the mid object. Inserting a delay in the cifs demultiplexer thread widens the race window and makes reproduction of the race very easy: if (server->large_buf) buf = server->bigbuf; + usleep_range(500, 4000); server->lstrp = jiffies; To resolve this I think the proper solution involves putting a reference count on the mid object. This patch makes sure that the demultiplexer thread holds a reference until it has finished processing the transaction. Cc: [email protected] Signed-off-by: Lars Persson <[email protected]> Acked-by: Paulo Alcantara <[email protected]> Reviewed-by: Ronnie Sahlberg <[email protected]> Reviewed-by: Pavel Shilovsky <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 06c8563 commit 696e420

File tree

7 files changed

+29
-2
lines changed

7 files changed

+29
-2
lines changed

fs/cifs/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,7 @@ typedef int (mid_handle_t)(struct TCP_Server_Info *server,
14161416
/* one of these for every pending CIFS request to the server */
14171417
struct mid_q_entry {
14181418
struct list_head qhead; /* mids waiting on reply from this server */
1419+
struct kref refcount;
14191420
struct TCP_Server_Info *server; /* server corresponding to this mid */
14201421
__u64 mid; /* multiplex id */
14211422
__u32 pid; /* process id */

fs/cifs/cifsproto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
8282
struct TCP_Server_Info *server);
8383
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
8484
extern void cifs_delete_mid(struct mid_q_entry *mid);
85+
extern void cifs_mid_q_entry_release(struct mid_q_entry *midEntry);
8586
extern void cifs_wake_up_task(struct mid_q_entry *mid);
8687
extern int cifs_handle_standard(struct TCP_Server_Info *server,
8788
struct mid_q_entry *mid);

fs/cifs/connect.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,7 @@ cifs_demultiplex_thread(void *p)
924924
server->pdu_size = next_offset;
925925
}
926926

927+
mid_entry = NULL;
927928
if (server->ops->is_transform_hdr &&
928929
server->ops->receive_transform &&
929930
server->ops->is_transform_hdr(buf)) {
@@ -938,8 +939,11 @@ cifs_demultiplex_thread(void *p)
938939
length = mid_entry->receive(server, mid_entry);
939940
}
940941

941-
if (length < 0)
942+
if (length < 0) {
943+
if (mid_entry)
944+
cifs_mid_q_entry_release(mid_entry);
942945
continue;
946+
}
943947

944948
if (server->large_buf)
945949
buf = server->bigbuf;
@@ -956,6 +960,8 @@ cifs_demultiplex_thread(void *p)
956960

957961
if (!mid_entry->multiRsp || mid_entry->multiEnd)
958962
mid_entry->callback(mid_entry);
963+
964+
cifs_mid_q_entry_release(mid_entry);
959965
} else if (server->ops->is_oplock_break &&
960966
server->ops->is_oplock_break(buf, server)) {
961967
cifs_dbg(FYI, "Received oplock break\n");

fs/cifs/smb1ops.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
107107
if (compare_mid(mid->mid, buf) &&
108108
mid->mid_state == MID_REQUEST_SUBMITTED &&
109109
le16_to_cpu(mid->command) == buf->Command) {
110+
kref_get(&mid->refcount);
110111
spin_unlock(&GlobalMid_Lock);
111112
return mid;
112113
}

fs/cifs/smb2ops.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
203203
if ((mid->mid == wire_mid) &&
204204
(mid->mid_state == MID_REQUEST_SUBMITTED) &&
205205
(mid->command == shdr->Command)) {
206+
kref_get(&mid->refcount);
206207
spin_unlock(&GlobalMid_Lock);
207208
return mid;
208209
}

fs/cifs/smb2transport.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
548548

549549
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
550550
memset(temp, 0, sizeof(struct mid_q_entry));
551+
kref_init(&temp->refcount);
551552
temp->mid = le64_to_cpu(shdr->MessageId);
552553
temp->pid = current->pid;
553554
temp->command = shdr->Command; /* Always LE */

fs/cifs/transport.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
6161

6262
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
6363
memset(temp, 0, sizeof(struct mid_q_entry));
64+
kref_init(&temp->refcount);
6465
temp->mid = get_mid(smb_buffer);
6566
temp->pid = current->pid;
6667
temp->command = cpu_to_le16(smb_buffer->Command);
@@ -82,6 +83,21 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
8283
return temp;
8384
}
8485

86+
static void _cifs_mid_q_entry_release(struct kref *refcount)
87+
{
88+
struct mid_q_entry *mid = container_of(refcount, struct mid_q_entry,
89+
refcount);
90+
91+
mempool_free(mid, cifs_mid_poolp);
92+
}
93+
94+
void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
95+
{
96+
spin_lock(&GlobalMid_Lock);
97+
kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
98+
spin_unlock(&GlobalMid_Lock);
99+
}
100+
85101
void
86102
DeleteMidQEntry(struct mid_q_entry *midEntry)
87103
{
@@ -110,7 +126,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
110126
}
111127
}
112128
#endif
113-
mempool_free(midEntry, cifs_mid_poolp);
129+
cifs_mid_q_entry_release(midEntry);
114130
}
115131

116132
void

0 commit comments

Comments
 (0)