From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934069AbcBCWg4 (ORCPT ); Wed, 3 Feb 2016 17:36:56 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:46925 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934032AbcBCWgp (ORCPT ); Wed, 3 Feb 2016 17:36:45 -0500 From: Luis Henriques To: linux-kernel@vger.kernel.org, stable@vger.kernel.org, kernel-team@lists.ubuntu.com Cc: Rabin Vincent , Steve French , Luis Henriques Subject: [PATCH 3.16.y-ckt 105/180] cifs: fix race between call_async() and reconnect() Date: Wed, 3 Feb 2016 22:31:51 +0000 Message-Id: <1454538786-12215-106-git-send-email-luis.henriques@canonical.com> In-Reply-To: <1454538786-12215-1-git-send-email-luis.henriques@canonical.com> References: <1454538786-12215-1-git-send-email-luis.henriques@canonical.com> X-Extended-Stable: 3.16 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.16.7-ckt24 -stable review patch. If anyone has any objections, please let me know. ---8<------------------------------------------------------------ From: Rabin Vincent commit 820962dc700598ffe8cd21b967e30e7520c34748 upstream. cifs_call_async() queues the MID to the pending list and calls smb_send_rqst(). If smb_send_rqst() performs a partial send, it sets the tcpStatus to CifsNeedReconnect and returns an error code to cifs_call_async(). In this case, cifs_call_async() removes the MID from the list and returns to the caller. However, cifs_call_async() releases the server mutex _before_ removing the MID. This means that a cifs_reconnect() can race with this function and manage to remove the MID from the list and delete the entry before cifs_call_async() calls cifs_delete_mid(). This leads to various crashes due to the use after free in cifs_delete_mid(). Task1 Task2 cifs_call_async(): - rc = -EAGAIN - mutex_unlock(srv_mutex) cifs_reconnect(): - mutex_lock(srv_mutex) - mutex_unlock(srv_mutex) - list_delete(mid) - mid->callback() cifs_writev_callback(): - mutex_lock(srv_mutex) - delete(mid) - mutex_unlock(srv_mutex) - cifs_delete_mid(mid) <---- use after free Fix this by removing the MID in cifs_call_async() before releasing the srv_mutex. Also hold the srv_mutex in cifs_reconnect() until the MIDs are moved out of the pending list. Signed-off-by: Rabin Vincent Acked-by: Shirish Pargaonkar Signed-off-by: Steve French [ luis: backported to 3.16: adjusted context ] Signed-off-by: Luis Henriques --- fs/cifs/connect.c | 2 +- fs/cifs/transport.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 20d75b8ddb26..3a4fc1bcde43 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -354,7 +354,6 @@ cifs_reconnect(struct TCP_Server_Info *server) server->session_key.response = NULL; server->session_key.len = 0; server->lstrp = jiffies; - mutex_unlock(&server->srv_mutex); /* mark submitted MIDs for retry and issue callback */ INIT_LIST_HEAD(&retry_list); @@ -367,6 +366,7 @@ cifs_reconnect(struct TCP_Server_Info *server) list_move(&mid_entry->qhead, &retry_list); } spin_unlock(&GlobalMid_Lock); + mutex_unlock(&server->srv_mutex); cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); list_for_each_safe(tmp, tmp2, &retry_list) { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 18cd5650a5fc..f3e4e3b72df2 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -564,14 +564,16 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, cifs_in_send_dec(server); cifs_save_when_sent(mid); - if (rc < 0) + if (rc < 0) { server->sequence_number -= 2; + cifs_delete_mid(mid); + } + mutex_unlock(&server->srv_mutex); if (rc == 0) return 0; - cifs_delete_mid(mid); add_credits(server, 1, optype); wake_up(&server->request_q); return rc;