From patchwork Thu Oct 19 13:58:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miklos Szeredi X-Patchwork-Id: 10017109 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E6BC1600CC for ; Thu, 19 Oct 2017 14:01:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E1E5D28D61 for ; Thu, 19 Oct 2017 14:01:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D6DC828D6B; Thu, 19 Oct 2017 14:01:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.4 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 67AF028D61 for ; Thu, 19 Oct 2017 14:01:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754170AbdJSOAB (ORCPT ); Thu, 19 Oct 2017 10:00:01 -0400 Received: from mail-wr0-f194.google.com ([209.85.128.194]:45284 "EHLO mail-wr0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754211AbdJSN6m (ORCPT ); Thu, 19 Oct 2017 09:58:42 -0400 Received: by mail-wr0-f194.google.com with SMTP id k7so8441896wre.2 for ; Thu, 19 Oct 2017 06:58:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nxkKufAQC8xupCK9BzXNHuQCO9/UW+pKDbXy18T6CCk=; b=fOvMNcDqbvbNZVPglGfGewogxyqPabqoUbqRJURFvrcSyZ2VbnAPLttkoXEZSPm3I1 kt6AzyUBHWz0wQDK4z+lIUSZN/g0pxfGFjwM3sJ8SsFooXJRCoXfCfmB6hTUmf4eka7A /GHYITLTP8kfow64pkqGQmn99Q4EghPIj8lQE5kIxigyzZgOQPB9I1hRrQd+9Nxjgwv/ KEEKPWws90nVc9pBGImtiTQYZuOQLoyOZUDHU0vMh3ZlrgxNjA3kPn25hiKmkUxRrQ+r tUB39VRooAR83uoUO6qt0kYMXvvnulmjXLCIR3QZuHLw5zZMqZ2OtHGCdb6e8VDpRoWD AXKA== X-Gm-Message-State: AMCzsaUY78ZwB+N0aHzTnripN9Pk7TJETJnRrSuuNmKKig6zwMXj6ygc uJWXyoqDNBHmZsw3qknj8ECH+BUuVXw= X-Google-Smtp-Source: ABhQp+SEAr9DNf9UvNf4PXE6HRmsmntKFb6CALA1vltv26KGxIFwaJpWv9cyMRXg5HPIdgC+YmbR9Q== X-Received: by 10.223.164.2 with SMTP id d2mr1868612wra.103.1508421521358; Thu, 19 Oct 2017 06:58:41 -0700 (PDT) Received: from veci.piliscsaba.szeredi.hu (catv-176-63-54-97.catv.broadband.hu. [176.63.54.97]) by smtp.gmail.com with ESMTPSA id h185sm3613279wma.19.2017.10.19.06.58.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 19 Oct 2017 06:58:40 -0700 (PDT) From: Miklos Szeredi To: linux-fsdevel@vger.kernel.org Cc: Jan Kara , Amir Goldstein , Xiong Zhou , linux-kernel@vger.kernel.org Subject: [PATCH 1/4] fsnotify: fix pinning of marks and groups Date: Thu, 19 Oct 2017 15:58:34 +0200 Message-Id: <1508421517-22678-2-git-send-email-mszeredi@redhat.com> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1508421517-22678-1-git-send-email-mszeredi@redhat.com> References: <1508421517-22678-1-git-send-email-mszeredi@redhat.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP We may fail to pin one of the marks in fsnotify_prepare_user_wait() when dropping the srcu read lock, resulting in use after free at the next iteration. Solution is to store both marks in iter_info instead of just the one we'll be sending the event for, as well as pinning the group for both (if they have the same group: fine, pinnig it twice doesn't hurt). Also blind increment of group's user_waits is not enough, we could be far enough in the group's destruction that it isn't taken into account. Instead we need to check (under lock) that the mark is still attached to the group after having obtained a ref to the mark. If not, skip it. In the process of fixing the above, clean up fsnotify_prepare_user_wait()/ fsnotify_finish_user_wait(). Signed-off-by: Miklos Szeredi Fixes: 9385a84d7e1f ("fsnotify: Pass fsnotify_iter_info into handle_event handler") Cc: # v4.12 --- fs/notify/fsnotify.c | 6 ++-- fs/notify/mark.c | 100 +++++++++++++++++++++++---------------------------- 2 files changed, 48 insertions(+), 58 deletions(-) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 0c4583b61717..48ec61f4c4d5 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -336,6 +336,9 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is, vfsmount_group = vfsmount_mark->group; } + iter_info.inode_mark = inode_mark; + iter_info.vfsmount_mark = vfsmount_mark; + if (inode_group && vfsmount_group) { int cmp = fsnotify_compare_groups(inode_group, vfsmount_group); @@ -348,9 +351,6 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is, } } - iter_info.inode_mark = inode_mark; - iter_info.vfsmount_mark = vfsmount_mark; - ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask, data, data_is, cookie, file_name, &iter_info); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 9991f8826734..9a7c8dbeeb59 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -109,16 +109,6 @@ void fsnotify_get_mark(struct fsnotify_mark *mark) atomic_inc(&mark->refcnt); } -/* - * Get mark reference when we found the mark via lockless traversal of object - * list. Mark can be already removed from the list by now and on its way to be - * destroyed once SRCU period ends. - */ -static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark) -{ - return atomic_inc_not_zero(&mark->refcnt); -} - static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) { u32 new_mask = 0; @@ -256,32 +246,53 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) FSNOTIFY_REAPER_DELAY); } -bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info) +/* + * Get mark reference when we found the mark via lockless traversal of object + * list. Mark can be already removed from the list by now and on its way to be + * destroyed once SRCU period ends. + */ +static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark) { - struct fsnotify_group *group; - - if (WARN_ON_ONCE(!iter_info->inode_mark && !iter_info->vfsmount_mark)) - return false; - - if (iter_info->inode_mark) - group = iter_info->inode_mark->group; - else - group = iter_info->vfsmount_mark->group; + if (!mark) + return true; + + if (atomic_inc_not_zero(&mark->refcnt)) { + spin_lock(&mark->lock); + if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) { + /* mark is attached, group is still alive then */ + atomic_inc(&mark->group->user_waits); + spin_unlock(&mark->lock); + return true; + } + spin_unlock(&mark->lock); + fsnotify_put_mark(mark); + } + return false; +} - /* - * Since acquisition of mark reference is an atomic op as well, we can - * be sure this inc is seen before any effect of refcount increment. - */ - atomic_inc(&group->user_waits); +static void fsnotify_put_mark_wait(struct fsnotify_mark *mark) +{ + if (mark) { + struct fsnotify_group *group = mark->group; - if (iter_info->inode_mark) { - /* This can fail if mark is being removed */ - if (!fsnotify_get_mark_safe(iter_info->inode_mark)) - goto out_wait; + fsnotify_put_mark(mark); + /* + * We abuse notification_waitq on group shutdown for waiting for all + * marks pinned when waiting for userspace. + */ + if (atomic_dec_and_test(&group->user_waits) && group->shutdown) + wake_up(&group->notification_waitq); } - if (iter_info->vfsmount_mark) { - if (!fsnotify_get_mark_safe(iter_info->vfsmount_mark)) - goto out_inode; +} + +bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info) +{ + /* This can fail if mark is being removed */ + if (!fsnotify_get_mark_safe(iter_info->inode_mark)) + return false; + if (!fsnotify_get_mark_safe(iter_info->vfsmount_mark)) { + fsnotify_put_mark_wait(iter_info->inode_mark); + return false; } /* @@ -292,34 +303,13 @@ bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info) srcu_read_unlock(&fsnotify_mark_srcu, iter_info->srcu_idx); return true; -out_inode: - if (iter_info->inode_mark) - fsnotify_put_mark(iter_info->inode_mark); -out_wait: - if (atomic_dec_and_test(&group->user_waits) && group->shutdown) - wake_up(&group->notification_waitq); - return false; } void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info) { - struct fsnotify_group *group = NULL; - iter_info->srcu_idx = srcu_read_lock(&fsnotify_mark_srcu); - if (iter_info->inode_mark) { - group = iter_info->inode_mark->group; - fsnotify_put_mark(iter_info->inode_mark); - } - if (iter_info->vfsmount_mark) { - group = iter_info->vfsmount_mark->group; - fsnotify_put_mark(iter_info->vfsmount_mark); - } - /* - * We abuse notification_waitq on group shutdown for waiting for all - * marks pinned when waiting for userspace. - */ - if (atomic_dec_and_test(&group->user_waits) && group->shutdown) - wake_up(&group->notification_waitq); + fsnotify_put_mark_wait(iter_info->inode_mark); + fsnotify_put_mark_wait(iter_info->vfsmount_mark); } /*