Received: by 2002:a25:31c3:0:0:0:0:0 with SMTP id x186csp2917585ybx; Fri, 8 Nov 2019 11:15:00 -0800 (PST) X-Google-Smtp-Source: APXvYqy2rSR5TsNBjpBEy9VQcRGCJf2pdAuQSCWW/rCB5u9EErQ1ojpYbbGrTjxs1PvsEwYFSPd0 X-Received: by 2002:a17:906:1354:: with SMTP id x20mr10612924ejb.131.1573240500355; Fri, 08 Nov 2019 11:15:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1573240500; cv=none; d=google.com; s=arc-20160816; b=ell2f+uEVMsBAmkQZzzU8AEESlsPxC/QhYqzTnpP/mdXYnAbEPIifyFkU5ICSKr8xw 1jzeoY3uSyzfyTkOCuPF/wbfJnNZ3Knsp6VBbzX0UEDDbIrATWLmdpjM5MD5JuFinKbU ABKgNNT8ta0ILlY/0+kTclekpJmv9MGrJTy5TgaRiavGGpYMMGH5pFz3YCHobKClqr+p dfRwJkUVrP+ge7g2FgXwILDptYzy4QZTMMJZecRPIg0lhekksYPrtcSE8LDNsKzo6RE+ y1J9Nl6Rl+dzoiHgc96Uj2U0IcHIGE9bhqXj1PiQOS4CgyGiObwtvy+r/+lftQakh655 sB5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=3qudcK0Z1uKhwNG9KAICufwpsoiNLmXea8peTKju2js=; b=x2QvT1Uz5Y5neTg07UpOfiVHLd1XR0sQROM54c0oQAlZLrkZbGI0U+wNR4rCle5bg4 +FJ9NYQt0F3PnbUCh8JJRdXILtvkuZvfhWX8W8oQQzR9guL7bdPGnJcM4b49VX/nd0EG 9ntcktZN59hMv6gWUcDFUD8rtf9qULaiC39xmL7ToYzHScDFDCwR5jRIDGWYgA/Yq4D4 6KMC04EJaLMlk2PMjAXkAkcfe3cQnDG9yp8wka5v5b+iXNaSjZAQOL80mU81Dhd4b/Bu Ixinx9qjhxXRL/A1kTjmu0mwWPHBwYa+RLl0YoKZWe/whUv75qCen7rSeDeShR5ECZMd DfuA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="09apCWw/"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c1si4500315ejb.116.2019.11.08.11.14.37; Fri, 08 Nov 2019 11:15:00 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="09apCWw/"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2391799AbfKHTJr (ORCPT + 99 others); Fri, 8 Nov 2019 14:09:47 -0500 Received: from mail.kernel.org ([198.145.29.99]:41572 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2391386AbfKHTJo (ORCPT ); Fri, 8 Nov 2019 14:09:44 -0500 Received: from localhost (83-86-89-107.cable.dynamic.v4.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id A4F7120673; Fri, 8 Nov 2019 19:09:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1573240183; bh=vKql6JUiWmAi+TSOjs1wfIulR/tIVLIp34fI11fbSJU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=09apCWw/C7Dh6+QA5L6i1XigV5jisZz9097xuFIoXqBwK5OlVDJL7SPSgrfX8P/oU E6pWRDolPkwg/PYT0kcQiAXbJil0N+KCeLWDmmcvIb2pShYO5ECeJm8HD4K6hNidQg fkWVoLkncaAhHlPd1sGIAn5VCn8VAQh2AoFiJQzo= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Dave Wysochanski , Ronnie Sahlberg , Pavel Shilovsky , Sasha Levin Subject: [PATCH 5.3 075/140] cifs: Fix cifsInodeInfo lock_sem deadlock when reconnect occurs Date: Fri, 8 Nov 2019 19:50:03 +0100 Message-Id: <20191108174909.848986828@linuxfoundation.org> X-Mailer: git-send-email 2.24.0 In-Reply-To: <20191108174900.189064908@linuxfoundation.org> References: <20191108174900.189064908@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Dave Wysochanski [ Upstream commit d46b0da7a33dd8c99d969834f682267a45444ab3 ] There's a deadlock that is possible and can easily be seen with a test where multiple readers open/read/close of the same file and a disruption occurs causing reconnect. The deadlock is due a reader thread inside cifs_strict_readv calling down_read and obtaining lock_sem, and then after reconnect inside cifs_reopen_file calling down_read a second time. If in between the two down_read calls, a down_write comes from another process, deadlock occurs. CPU0 CPU1 ---- ---- cifs_strict_readv() down_read(&cifsi->lock_sem); _cifsFileInfo_put OR cifs_new_fileinfo down_write(&cifsi->lock_sem); cifs_reopen_file() down_read(&cifsi->lock_sem); Fix the above by changing all down_write(lock_sem) calls to down_write_trylock(lock_sem)/msleep() loop, which in turn makes the second down_read call benign since it will never block behind the writer while holding lock_sem. Signed-off-by: Dave Wysochanski Suggested-by: Ronnie Sahlberg Reviewed--by: Ronnie Sahlberg Reviewed-by: Pavel Shilovsky Signed-off-by: Sasha Levin --- fs/cifs/cifsglob.h | 5 +++++ fs/cifs/cifsproto.h | 1 + fs/cifs/file.c | 23 +++++++++++++++-------- fs/cifs/smb2file.c | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 5ef5a16c01d26..7289d443bfb33 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1379,6 +1379,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file); struct cifsInodeInfo { bool can_cache_brlcks; struct list_head llist; /* locks helb by this inode */ + /* + * NOTE: Some code paths call down_read(lock_sem) twice, so + * we must always use use cifs_down_write() instead of down_write() + * for this semaphore to avoid deadlocks. + */ struct rw_semaphore lock_sem; /* protect the fields above */ /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 592a6cea2b79f..65b07f92bc71d 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -166,6 +166,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, const unsigned int xid); extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); +extern void cifs_down_write(struct rw_semaphore *sem); extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, struct tcon_link *tlink, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 53dbb6e0d390d..facb52d37d19c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -281,6 +281,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode) return has_locks; } +void +cifs_down_write(struct rw_semaphore *sem) +{ + while (!down_write_trylock(sem)) + msleep(10); +} + struct cifsFileInfo * cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, struct tcon_link *tlink, __u32 oplock) @@ -306,7 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, INIT_LIST_HEAD(&fdlocks->locks); fdlocks->cfile = cfile; cfile->llist = fdlocks; - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); list_add(&fdlocks->llist, &cinode->llist); up_write(&cinode->lock_sem); @@ -464,7 +471,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) * Delete any outstanding lock records. We'll lose them when the file * is closed anyway. */ - down_write(&cifsi->lock_sem); + cifs_down_write(&cifsi->lock_sem); list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { list_del(&li->llist); cifs_del_lock_waiters(li); @@ -1027,7 +1034,7 @@ static void cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) { struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); list_add_tail(&lock->llist, &cfile->llist->locks); up_write(&cinode->lock_sem); } @@ -1049,7 +1056,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, try_again: exist = false; - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, lock->type, lock->flags, &conf_lock, @@ -1072,7 +1079,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, (lock->blist.next == &lock->blist)); if (!rc) goto try_again; - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); list_del_init(&lock->blist); } @@ -1125,7 +1132,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock) return rc; try_again: - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); if (!cinode->can_cache_brlcks) { up_write(&cinode->lock_sem); return rc; @@ -1331,7 +1338,7 @@ cifs_push_locks(struct cifsFileInfo *cfile) int rc = 0; /* we are going to update can_cache_brlcks here - need a write access */ - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); if (!cinode->can_cache_brlcks) { up_write(&cinode->lock_sem); return rc; @@ -1522,7 +1529,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, if (!buf) return -ENOMEM; - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); for (i = 0; i < 2; i++) { cur = buf; num = 0; diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index e6a1fc72018fd..8b0b512c57920 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -145,7 +145,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, cur = buf; - down_write(&cinode->lock_sem); + cifs_down_write(&cinode->lock_sem); list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { if (flock->fl_start > li->offset || (flock->fl_start + length) < -- 2.20.1