aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Persson <lars.persson@axis.com>2018-06-25 08:05:25 -0400
committerSteve French <stfrench@microsoft.com>2018-07-05 14:48:24 -0400
commit696e420bb2a6624478105651d5368d45b502b324 (patch)
treeac581ea457603c258a8b6b3a47e79758f7e3e904
parent06c85639897cf3ea6a11c5cb6929fb0d9d7efea5 (diff)
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: stable@vger.kernel.org Signed-off-by: Lars Persson <larper@axis.com> Acked-by: Paulo Alcantara <palcantara@suse.de> Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/cifsproto.h1
-rw-r--r--fs/cifs/connect.c8
-rw-r--r--fs/cifs/smb1ops.c1
-rw-r--r--fs/cifs/smb2ops.c1
-rw-r--r--fs/cifs/smb2transport.c1
-rw-r--r--fs/cifs/transport.c18
7 files changed, 29 insertions, 2 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index bd78da59a4fd..a2962fd41c6f 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1416,6 +1416,7 @@ typedef int (mid_handle_t)(struct TCP_Server_Info *server,
1416/* one of these for every pending CIFS request to the server */ 1416/* one of these for every pending CIFS request to the server */
1417struct mid_q_entry { 1417struct mid_q_entry {
1418 struct list_head qhead; /* mids waiting on reply from this server */ 1418 struct list_head qhead; /* mids waiting on reply from this server */
1419 struct kref refcount;
1419 struct TCP_Server_Info *server; /* server corresponding to this mid */ 1420 struct TCP_Server_Info *server; /* server corresponding to this mid */
1420 __u64 mid; /* multiplex id */ 1421 __u64 mid; /* multiplex id */
1421 __u32 pid; /* process id */ 1422 __u32 pid; /* process id */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 03018be17283..1890f534c88b 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -82,6 +82,7 @@ extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
82 struct TCP_Server_Info *server); 82 struct TCP_Server_Info *server);
83extern void DeleteMidQEntry(struct mid_q_entry *midEntry); 83extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
84extern void cifs_delete_mid(struct mid_q_entry *mid); 84extern void cifs_delete_mid(struct mid_q_entry *mid);
85extern void cifs_mid_q_entry_release(struct mid_q_entry *midEntry);
85extern void cifs_wake_up_task(struct mid_q_entry *mid); 86extern void cifs_wake_up_task(struct mid_q_entry *mid);
86extern int cifs_handle_standard(struct TCP_Server_Info *server, 87extern int cifs_handle_standard(struct TCP_Server_Info *server,
87 struct mid_q_entry *mid); 88 struct mid_q_entry *mid);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a57da1b88bdf..5df2c0698cda 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -924,6 +924,7 @@ next_pdu:
924 server->pdu_size = next_offset; 924 server->pdu_size = next_offset;
925 } 925 }
926 926
927 mid_entry = NULL;
927 if (server->ops->is_transform_hdr && 928 if (server->ops->is_transform_hdr &&
928 server->ops->receive_transform && 929 server->ops->receive_transform &&
929 server->ops->is_transform_hdr(buf)) { 930 server->ops->is_transform_hdr(buf)) {
@@ -938,8 +939,11 @@ next_pdu:
938 length = mid_entry->receive(server, mid_entry); 939 length = mid_entry->receive(server, mid_entry);
939 } 940 }
940 941
941 if (length < 0) 942 if (length < 0) {
943 if (mid_entry)
944 cifs_mid_q_entry_release(mid_entry);
942 continue; 945 continue;
946 }
943 947
944 if (server->large_buf) 948 if (server->large_buf)
945 buf = server->bigbuf; 949 buf = server->bigbuf;
@@ -956,6 +960,8 @@ next_pdu:
956 960
957 if (!mid_entry->multiRsp || mid_entry->multiEnd) 961 if (!mid_entry->multiRsp || mid_entry->multiEnd)
958 mid_entry->callback(mid_entry); 962 mid_entry->callback(mid_entry);
963
964 cifs_mid_q_entry_release(mid_entry);
959 } else if (server->ops->is_oplock_break && 965 } else if (server->ops->is_oplock_break &&
960 server->ops->is_oplock_break(buf, server)) { 966 server->ops->is_oplock_break(buf, server)) {
961 cifs_dbg(FYI, "Received oplock break\n"); 967 cifs_dbg(FYI, "Received oplock break\n");
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index aff8ce8ba34d..646dcd149de1 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -107,6 +107,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
107 if (compare_mid(mid->mid, buf) && 107 if (compare_mid(mid->mid, buf) &&
108 mid->mid_state == MID_REQUEST_SUBMITTED && 108 mid->mid_state == MID_REQUEST_SUBMITTED &&
109 le16_to_cpu(mid->command) == buf->Command) { 109 le16_to_cpu(mid->command) == buf->Command) {
110 kref_get(&mid->refcount);
110 spin_unlock(&GlobalMid_Lock); 111 spin_unlock(&GlobalMid_Lock);
111 return mid; 112 return mid;
112 } 113 }
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 0356b5559c71..e9216ce88796 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -203,6 +203,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
203 if ((mid->mid == wire_mid) && 203 if ((mid->mid == wire_mid) &&
204 (mid->mid_state == MID_REQUEST_SUBMITTED) && 204 (mid->mid_state == MID_REQUEST_SUBMITTED) &&
205 (mid->command == shdr->Command)) { 205 (mid->command == shdr->Command)) {
206 kref_get(&mid->refcount);
206 spin_unlock(&GlobalMid_Lock); 207 spin_unlock(&GlobalMid_Lock);
207 return mid; 208 return mid;
208 } 209 }
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 51b9437c3c7b..50592976dcb4 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -548,6 +548,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
548 548
549 temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); 549 temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
550 memset(temp, 0, sizeof(struct mid_q_entry)); 550 memset(temp, 0, sizeof(struct mid_q_entry));
551 kref_init(&temp->refcount);
551 temp->mid = le64_to_cpu(shdr->MessageId); 552 temp->mid = le64_to_cpu(shdr->MessageId);
552 temp->pid = current->pid; 553 temp->pid = current->pid;
553 temp->command = shdr->Command; /* Always LE */ 554 temp->command = shdr->Command; /* Always LE */
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index fb57dfbfb749..208ecb830466 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -61,6 +61,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
61 61
62 temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); 62 temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
63 memset(temp, 0, sizeof(struct mid_q_entry)); 63 memset(temp, 0, sizeof(struct mid_q_entry));
64 kref_init(&temp->refcount);
64 temp->mid = get_mid(smb_buffer); 65 temp->mid = get_mid(smb_buffer);
65 temp->pid = current->pid; 66 temp->pid = current->pid;
66 temp->command = cpu_to_le16(smb_buffer->Command); 67 temp->command = cpu_to_le16(smb_buffer->Command);
@@ -82,6 +83,21 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
82 return temp; 83 return temp;
83} 84}
84 85
86static 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
94void 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
85void 101void
86DeleteMidQEntry(struct mid_q_entry *midEntry) 102DeleteMidQEntry(struct mid_q_entry *midEntry)
87{ 103{
@@ -110,7 +126,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
110 } 126 }
111 } 127 }
112#endif 128#endif
113 mempool_free(midEntry, cifs_mid_poolp); 129 cifs_mid_q_entry_release(midEntry);
114} 130}
115 131
116void 132void