Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp3465913imm; Tue, 29 May 2018 07:44:07 -0700 (PDT) X-Google-Smtp-Source: AB8JxZontgZ+sSK/TMZBYmI6QRSCkohprG72iZQGNB8p3ZjI1O7eh0r2+kH7tVvp+nRCljOm3bQB X-Received: by 2002:a17:902:125:: with SMTP id 34-v6mr18320156plb.42.1527605047079; Tue, 29 May 2018 07:44:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527605047; cv=none; d=google.com; s=arc-20160816; b=wj0xzfgtq9qkXTfTB+Tg7QOaNN/tXthyqy6rlgekExVuMGeOViEPOb3/WwIZyXzjLD m2eQ1kZWASY89IWftOivPATMHWYIG+z4lGrVeUINytT2Rej6v1m4L8oDDmbGh50vZ4Zj qqWVuqczfqDRDBHAM8raj8SIfXYT+mR/GFB7b5hb2hzTsgsAJxXaEK4r1XIOWIxC4Ph+ eENBIHt1xedlRtpOkBmU9N/ZcW7LydoXz/I52wqh0BWAjUv3ozkeBC+7ofz5wOdGT7zK LFEluPKJCIeSzMfFvKKXxHSr64rXcdzFtAj+ej4wITprJGQEwcfKKoT1or4D7nu1Xlum VHUQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=53ZIQla1hR5Z7f9c+JKR/MxRo99GSX5FarhAiacxpCs=; b=xFeJkU8hxKT1FUx7h/RtkRhens128Al48la+7ZN3Q9Rq75sPDrbXsg+396E+qaTO/O 2RsWHx49VzjHV93hVIcyL0PiwFGjSDkY41O5rSTB0z7kDcooGcI8GQSnuCBR79Rkdm6O HS2mw2+XzvT70XnNPtHywLNIKcO+TU/3ftMlCqUTPTyoP0SX4rav7AYmGdpOC5vgeHl2 yy0FAa+VHiUoNE1QArw7oxcCVafgJBGwqcbsTcW7VlQqCjV3zT1NxRUeqhFsTUXAOJg3 JoMl7yLD9C1aUBR0e0AXya0H3Afq2qkc0A/3yJ8cY2d6BLtleSCV9ZFbRZLQYWzXSAMV D3FQ== ARC-Authentication-Results: i=1; mx.google.com; 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a4-v6si32511577plp.219.2018.05.29.07.43.53; Tue, 29 May 2018 07:44:07 -0700 (PDT) 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; 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935285AbeE2OmX (ORCPT + 99 others); Tue, 29 May 2018 10:42:23 -0400 Received: from mail-wr0-f194.google.com ([209.85.128.194]:32903 "EHLO mail-wr0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935220AbeE2OmC (ORCPT ); Tue, 29 May 2018 10:42:02 -0400 Received: by mail-wr0-f194.google.com with SMTP id a15-v6so25985733wrm.0 for ; Tue, 29 May 2018 07:42:01 -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=53ZIQla1hR5Z7f9c+JKR/MxRo99GSX5FarhAiacxpCs=; b=sqBudGFn/V/8Fq9ffrRfZfgS+y6ZtOl4QAv74UaTu6RDH3815A6XWxY1Hfm04rko+t EDI1AbpfVKpw8NBrGMuDnf0qpFiktNhKxvt3RU5P6YrvyxNO0x1kacPifMUvHNYMDvZX HfWberOvucHcHOPEKYsRnPybmy6uLNMe0eena6bu8rLtyLK3KSLwgLLRvsoJ91acsaR/ 8/3x/k6eDiJXMM0s9TcWSaC5YGSvWAyjhnADpnW4Zi+u8IarltX0h05KfR2MhtYv+ob6 VX5ahPfVDgJUmNdjKNJWrQ+wwnUM+B9LcDKrq4M67ZdZL0Pfibd6IFErqDyVBTD1VoRK HbiQ== X-Gm-Message-State: ALKqPwcBSY1TaUFjlx/f9iePkdbiSwA6VRJgoRACl1m4NIopgZavl9H4 pNaP49yiCPcVGDxQmOXHLg2opYmBdPM= X-Received: by 2002:adf:8df3:: with SMTP id o106-v6mr15140166wrb.251.1527604921072; Tue, 29 May 2018 07:42:01 -0700 (PDT) Received: from veci.piliscsaba.redhat.com (catv-176-63-54-97.catv.broadband.hu. [176.63.54.97]) by smtp.gmail.com with ESMTPSA id q17-v6sm12572113wmf.3.2018.05.29.07.41.59 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 29 May 2018 07:42:00 -0700 (PDT) From: Miklos Szeredi To: linux-unionfs@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 11/11] ovl: use inode_insert5() to hash a newly created inode Date: Tue, 29 May 2018 16:41:43 +0200 Message-Id: <20180529144143.16378-12-mszeredi@redhat.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20180529144143.16378-1-mszeredi@redhat.com> References: <20180529144143.16378-1-mszeredi@redhat.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Amir Goldstein Currently, there is a small window where ovl_obtain_alias() can race with ovl_instantiate() and create two different overlay inodes with the same underlying real non-dir non-hardlink inode. The race requires an adversary to guess the file handle of the yet to be created upper inode and decode the guessed file handle after ovl_creat_real(), but before ovl_instantiate(). This race does not affect overlay directory inodes, because those are decoded via ovl_lookup_real() and not with ovl_obtain_alias(). This patch fixes the race, by using inode_insert5() to add a newly created inode to cache. If the newly created inode apears to already exist in cache (hashed by the same real upper inode), we instantiate the dentry with the old inode and drop the new inode, instead of silently not hashing the new inode. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/dir.c | 59 ++++++++++++++++++++++++++++++++++++++++-------- fs/overlayfs/inode.c | 12 ++++++++-- fs/overlayfs/overlayfs.h | 1 + 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b33d37d1a87c..b2bc313241a6 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -228,24 +228,54 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry) return ovl_set_opaque_xerr(dentry, upperdentry, -EIO); } -/* Common operations required to be done after creation of file on upper */ -static void ovl_instantiate(struct dentry *dentry, struct inode *inode, - struct dentry *newdentry, bool hardlink) +/* + * Common operations required to be done after creation of file on upper. + * If @hardlink is false, then @inode is a pre-allocated inode, we may or + * may not use to instantiate the new dentry. + */ +static int ovl_instantiate(struct dentry *dentry, struct inode *inode, + struct dentry *newdentry, bool hardlink) { + struct ovl_inode_params oip = { + .upperdentry = newdentry, + .newinode = inode, + }; + ovl_dentry_version_inc(dentry->d_parent, false); ovl_dentry_set_upper_alias(dentry); if (!hardlink) { - ovl_inode_update(inode, newdentry); - ovl_copyattr(newdentry->d_inode, inode); + /* + * ovl_obtain_alias() can be called after ovl_create_real() + * and before we get here, so we may get an inode from cache + * with the same real upperdentry that is not the inode we + * pre-allocated. In this case we will use the cached inode + * to instantiate the new dentry. + * + * XXX: if we ever use ovl_obtain_alias() to decode directory + * file handles, need to use ovl_get_inode_locked() and + * d_instantiate_new() here to prevent from creating two + * hashed directory inode aliases. + */ + inode = ovl_get_inode(dentry->d_sb, &oip); + if (WARN_ON(IS_ERR(inode))) + return PTR_ERR(inode); } else { WARN_ON(ovl_inode_real(inode) != d_inode(newdentry)); dput(newdentry); inc_nlink(inode); } + d_instantiate(dentry, inode); + if (inode != oip.newinode) { + pr_warn_ratelimited("overlayfs: newly created inode found in cache (%pd2)\n", + dentry); + } + /* Force lookup of new upper hardlink to find its lower */ if (hardlink) d_drop(dentry); + + return 0; } static bool ovl_type_merge(struct dentry *dentry) @@ -284,11 +314,17 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode, ovl_set_opaque(dentry, newdentry); } - ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink); - err = 0; + err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink); + if (err) + goto out_cleanup; out_unlock: inode_unlock(udir); return err; + +out_cleanup: + ovl_cleanup(udir, newdentry); + dput(newdentry); + goto out_unlock; } static struct dentry *ovl_clear_empty(struct dentry *dentry, @@ -473,8 +509,9 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, if (err) goto out_cleanup; } - ovl_instantiate(dentry, inode, newdentry, hardlink); - err = 0; + err = ovl_instantiate(dentry, inode, newdentry, hardlink); + if (err) + goto out_cleanup; out_dput: dput(upper); out_unlock: @@ -557,6 +594,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, if (err) goto out; + /* Preallocate inode to be used by ovl_get_inode() */ err = -ENOMEM; inode = ovl_new_inode(dentry->d_sb, mode, rdev); if (!inode) @@ -566,7 +604,8 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, attr.mode = inode->i_mode; err = ovl_create_or_link(dentry, inode, &attr, false); - if (err) + /* Did we end up using the preallocated inode? */ + if (inode != d_inode(dentry)) iput(inode); out_drop_write: diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 2b9e8370500c..1db5b3b458a1 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -749,6 +749,15 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, return true; } +static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode, + struct inode *key) +{ + return newinode ? inode_insert5(newinode, (unsigned long) key, + ovl_inode_test, ovl_inode_set, key) : + iget5_locked(sb, (unsigned long) key, + ovl_inode_test, ovl_inode_set, key); +} + struct inode *ovl_get_inode(struct super_block *sb, struct ovl_inode_params *oip) { @@ -776,8 +785,7 @@ struct inode *ovl_get_inode(struct super_block *sb, upperdentry); unsigned int nlink = is_dir ? 1 : realinode->i_nlink; - inode = iget5_locked(sb, (unsigned long) key, ovl_inode_test, - ovl_inode_set, key); + inode = ovl_iget5(sb, oip->newinode, key); if (!inode) goto out_nomem; if (!(inode->i_state & I_NEW)) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index b8a0160742b2..3c5e9f18b0d9 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -329,6 +329,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); bool ovl_is_private_xattr(const char *name); struct ovl_inode_params { + struct inode *newinode; struct dentry *upperdentry; struct ovl_path *lowerpath; struct dentry *index; -- 2.14.3