Received: by 2002:a5d:9c59:0:0:0:0:0 with SMTP id 25csp2143580iof; Tue, 7 Jun 2022 21:12:21 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyMQUAFtrrRSVrS2va+hjdWnx2xLYo1jD1dp+8evRex9LpS/Ryon5UFDJVfDzKgJ32E8MXV X-Received: by 2002:a17:90b:3696:b0:1e6:6f6d:962b with SMTP id mj22-20020a17090b369600b001e66f6d962bmr37557061pjb.8.1654661541468; Tue, 07 Jun 2022 21:12:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1654661541; cv=none; d=google.com; s=arc-20160816; b=d6w0G6KsqoFV/v39M7ONzYz9L76+BY0qoI2uXel7/c0lVR5kr63XgwxpRd3RXBxcod PvbQDpx1qF0+u+zj0Ygjmh8RYlN9WGzabxTYxqAisQ+GRexc7IaSVCCfYr/RJdsQbrMF kNqlHDzlrnztEy4mprIgdM7p3KoAfk5Tifnu45n8dVWTHAg9BqcH9tttJpE1RHqn1FUZ cR4mtjXjo6wxQGmH7mcAMPWjOWSQkRtDk4Lv2jgg6ECDS6HaO0k059dEGMM+JpMWLCVp VYTsspVNTq4lfyow2rD5FlNCD+ceaq5jS5tQ84L125v8avt8iJ4Su7XSf3ktlPzapnTK 1kLQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=zD04YG6GSzYLWnlTyPGjyEqio95kK4hIiyZnIbnYzUs=; b=MOBsjr72EXDJpisx1Cq2qIi0z7WDQLbMpV3Pvh1Mb1x7hyV8QXgB5NvN0K7lOE6z86 2zy697/z+XoHPidy+L8FU+Vedn1y79yHRfQVK+DloeLlAkyzbswmccPgCU+Rnuk9JYKl fik05aN+KdyIRhxILw00aoHTg7JS5mf6IYuC/r4q0L/F6nerGxmWbahD0Ap1Wl7n4eDI 7Lq5Hc59IUjRQfpCp1LjY/F2FmtSH8smOYsK7a3pMFOuZonHR1oJ7WDEuHn3i+3yYteD +XC71tL1rUKRnK48fw2VWHwTYA3UQr8bD39WZUkrk5eGItbqq0IkvPaLP2dmxCQU2r8Y PowQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=2a96sKNS; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net. [2620:137:e000::1:18]) by mx.google.com with ESMTPS id j189-20020a6380c6000000b003f62669e1a7si24916907pgd.767.2022.06.07.21.12.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jun 2022 21:12:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) client-ip=2620:137:e000::1:18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=2a96sKNS; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E320C2B07FC; Tue, 7 Jun 2022 20:43:52 -0700 (PDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347959AbiFGSJ2 (ORCPT + 99 others); Tue, 7 Jun 2022 14:09:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47694 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349159AbiFGRue (ORCPT ); Tue, 7 Jun 2022 13:50:34 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E760A11825; Tue, 7 Jun 2022 10:37:55 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 7D69761534; Tue, 7 Jun 2022 17:37:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8DEEEC385A5; Tue, 7 Jun 2022 17:37:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1654623474; bh=jR5s3Ypb1MTMNYEF/AV9ntqzryfF/tWSuG1hMJfGYz8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=2a96sKNSECWAcgUs4aherwNYQk3jP/Bpkv3HvEh0Fez8u3qoK5N5SinSwKBFwZfhI 632yw92RIap9mn884/VMZkoM1WrH4BEODcHYrt59nqnVFq0SFbZ53VP6QavkOsd+2s azT62n9TM9n2RnT+AUjUcMofMAMBTcV/fPQGtIKY= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, "Darrick J. Wong" , Christoph Hellwig , Brian Foster , Amir Goldstein Subject: [PATCH 5.10 432/452] xfs: fix chown leaking delalloc quota blocks when fssetxattr fails Date: Tue, 7 Jun 2022 19:04:49 +0200 Message-Id: <20220607164921.428582341@linuxfoundation.org> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220607164908.521895282@linuxfoundation.org> References: <20220607164908.521895282@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-3.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RDNS_NONE,SPF_HELO_NONE,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "Darrick J. Wong" commit 1aecf3734a95f3c167d1495550ca57556d33f7ec upstream. While refactoring the quota code to create a function to allocate inode change transactions, I noticed that xfs_qm_vop_chown_reserve does more than just make reservations: it also *modifies* the incore counts directly to handle the owner id change for the delalloc blocks. I then observed that the fssetxattr code continues validating input arguments after making the quota reservation but before dirtying the transaction. If the routine decides to error out, it fails to undo the accounting switch! This leads to incorrect quota reservation and failure down the line. We can fix this by making the reservation function do only that -- for the new dquot, it reserves ondisk and delalloc blocks to the transaction, and the old dquot hangs on to its incore reservation for now. Once we actually switch the dquots, we can then update the incore reservations because we've dirtied the transaction and it's too late to turn back now. No fixes tag because this has been broken since the start of git. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Brian Foster Signed-off-by: Amir Goldstein Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_qm.c | 92 +++++++++++++++++++++----------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-) --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1786,6 +1786,29 @@ xfs_qm_vop_chown( xfs_trans_mod_dquot(tp, newdq, XFS_TRANS_DQ_ICOUNT, 1); /* + * Back when we made quota reservations for the chown, we reserved the + * ondisk blocks + delalloc blocks with the new dquot. Now that we've + * switched the dquots, decrease the new dquot's block reservation + * (having already bumped up the real counter) so that we don't have + * any reservation to give back when we commit. + */ + xfs_trans_mod_dquot(tp, newdq, XFS_TRANS_DQ_RES_BLKS, + -ip->i_delayed_blks); + + /* + * Give the incore reservation for delalloc blocks back to the old + * dquot. We don't normally handle delalloc quota reservations + * transactionally, so just lock the dquot and subtract from the + * reservation. Dirty the transaction because it's too late to turn + * back now. + */ + tp->t_flags |= XFS_TRANS_DIRTY; + xfs_dqlock(prevdq); + ASSERT(prevdq->q_blk.reserved >= ip->i_delayed_blks); + prevdq->q_blk.reserved -= ip->i_delayed_blks; + xfs_dqunlock(prevdq); + + /* * Take an extra reference, because the inode is going to keep * this dquot pointer even after the trans_commit. */ @@ -1807,84 +1830,39 @@ xfs_qm_vop_chown_reserve( uint flags) { struct xfs_mount *mp = ip->i_mount; - uint64_t delblks; unsigned int blkflags; - struct xfs_dquot *udq_unres = NULL; - struct xfs_dquot *gdq_unres = NULL; - struct xfs_dquot *pdq_unres = NULL; struct xfs_dquot *udq_delblks = NULL; struct xfs_dquot *gdq_delblks = NULL; struct xfs_dquot *pdq_delblks = NULL; - int error; - ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); ASSERT(XFS_IS_QUOTA_RUNNING(mp)); - delblks = ip->i_delayed_blks; blkflags = XFS_IS_REALTIME_INODE(ip) ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS; if (XFS_IS_UQUOTA_ON(mp) && udqp && - i_uid_read(VFS_I(ip)) != udqp->q_id) { + i_uid_read(VFS_I(ip)) != udqp->q_id) udq_delblks = udqp; - /* - * If there are delayed allocation blocks, then we have to - * unreserve those from the old dquot, and add them to the - * new dquot. - */ - if (delblks) { - ASSERT(ip->i_udquot); - udq_unres = ip->i_udquot; - } - } + if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp && - i_gid_read(VFS_I(ip)) != gdqp->q_id) { + i_gid_read(VFS_I(ip)) != gdqp->q_id) gdq_delblks = gdqp; - if (delblks) { - ASSERT(ip->i_gdquot); - gdq_unres = ip->i_gdquot; - } - } if (XFS_IS_PQUOTA_ON(ip->i_mount) && pdqp && - ip->i_d.di_projid != pdqp->q_id) { + ip->i_d.di_projid != pdqp->q_id) pdq_delblks = pdqp; - if (delblks) { - ASSERT(ip->i_pdquot); - pdq_unres = ip->i_pdquot; - } - } - - error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount, - udq_delblks, gdq_delblks, pdq_delblks, - ip->i_d.di_nblocks, 1, flags | blkflags); - if (error) - return error; /* - * Do the delayed blks reservations/unreservations now. Since, these - * are done without the help of a transaction, if a reservation fails - * its previous reservations won't be automatically undone by trans - * code. So, we have to do it manually here. + * Reserve enough quota to handle blocks on disk and reserved for a + * delayed allocation. We'll actually transfer the delalloc + * reservation between dquots at chown time, even though that part is + * only semi-transactional. */ - if (delblks) { - /* - * Do the reservations first. Unreservation can't fail. - */ - ASSERT(udq_delblks || gdq_delblks || pdq_delblks); - ASSERT(udq_unres || gdq_unres || pdq_unres); - error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, - udq_delblks, gdq_delblks, pdq_delblks, - (xfs_qcnt_t)delblks, 0, flags | blkflags); - if (error) - return error; - xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, - udq_unres, gdq_unres, pdq_unres, - -((xfs_qcnt_t)delblks), 0, blkflags); - } - - return 0; + return xfs_trans_reserve_quota_bydquots(tp, ip->i_mount, udq_delblks, + gdq_delblks, pdq_delblks, + ip->i_d.di_nblocks + ip->i_delayed_blks, + 1, blkflags | flags); } int