FS/XFS testing framework
 help / color / mirror / Atom feed
From: cem@kernel.org
To: zlang@kernel.org
Cc: djwong@kernel.org, hch@lst.de, linux-xfs@vger.kernel.org,
	fstests@vger.kernel.org, jack@suse.cz
Subject: [RFC PATCH 2/2] generic: add test for quota enforcement via a nfs share
Date: Mon,  7 Sep 2026 18:40:48 +0200	[thread overview]
Message-ID: <20260907164054.111393-3-cem@kernel.org> (raw)
In-Reply-To: <20260907164054.111393-1-cem@kernel.org>

From: Carlos Maiolino <cem@kernel.org>

This tests the quota enforcement when changing the group of a file via a
NFS share, caused by mishandling of capabilities check.
This initially has been found with a XFS filesystem backing the NFS
share, but this still possible to occur on a different filesystem if it
happens to also mishandle capabilities check.

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 tests/generic/803     | 106 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/803.out |   2 +
 2 files changed, 108 insertions(+)
 create mode 100755 tests/generic/803
 create mode 100644 tests/generic/803.out

diff --git a/tests/generic/803 b/tests/generic/803
new file mode 100755
index 000000000000..ab889466d80f
--- /dev/null
+++ b/tests/generic/803
@@ -0,0 +1,106 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Red Hat.  All Rights Reserved.
+#
+# FS QA Test No. 803
+#
+# Regression test for group quota evasion via NFS.
+#
+# A user belonging to two groups with quotas enforced on both could bypass
+# group quota by changing a file's group via an NFS client. The NFS server
+# runs as root (real credentials) but impersonates the client user via
+# override_creds() (effective credentials). The old code used
+# has_capability_noaudit(), which checks real credentials, causing
+# XFS_QMOPT_FORCE_RES to be set and bypassing quota checks.
+#
+. ./common/preamble
+_begin_fstest auto quick quota
+
+_cleanup()
+{
+	cd /
+	[ -n "$mpoint" ] && $UMOUNT_PROG $mpoint 2>/dev/null
+	sleep 2
+	[ -n "$exportdir" ] && exportfs -u 127.0.0.1:$exportdir 2>/dev/null
+	_stop_rpcbind
+	$added_to_fsgqa2 && gpasswd -d fsgqa fsgqa2 >> $seqres.full 2>&1
+	rm -rf $tmp.* $mpoint
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/quota
+. ./common/nfs
+
+_require_scratch
+_require_quota
+_require_user
+_require_group fsgqa2
+_require_nfs_server
+_require_nfs_client
+
+_fixed_by_kernel_commit e2f62a9744ebad3bcb6347a648e615026e9efeff \
+	"xfs: fix capability check in xfs"
+
+added_to_fsgqa2=false
+
+_qmount_option "usrquota,grpquota"
+_scratch_mkfs >> $seqres.full 2>&1
+_qmount
+
+# Ensure fsgqa belongs to both fsgqa and fsgqa2 groups.
+if ! id fsgqa | grep -qw fsgqa2; then
+	gpasswd -a fsgqa fsgqa2 >> $seqres.full 2>&1 || \
+		_notrun "Cannot add fsgqa to fsgqa2 group"
+	added_to_fsgqa2=true
+fi
+
+exportdir=$SCRATCH_MNT/exportdir
+mpoint=$TEST_DIR/mpoint-$seq
+nfs_userdir=$mpoint/userdir
+
+mkdir -p $exportdir/userdir $mpoint
+chown fsgqa:fsgqa $exportdir/userdir
+
+setquota -g fsgqa  0 2048 0 0 $SCRATCH_MNT
+setquota -g fsgqa2 0 4096 0 0 $SCRATCH_MNT
+
+# Use exportfs directly so we don't need to deal with /etc/exports file
+exportfs -o rw,insecure,no_root_squash,sync 127.0.0.1:$exportdir \
+	>> $seqres.full 2>&1 || _fail "NFS export of $exportdir failed"
+mount -t nfs -o vers=4 127.0.0.1:$exportdir $mpoint \
+	>> $seqres.full 2>&1 || _fail "NFS mount of $exportdir at $mpoint failed"
+
+# Write a 2MiB file via the NFS share as fsgqa, filling the fsgqa group quota.
+_su fsgqa -c "dd if=/dev/zero of=$nfs_userdir/file1 bs=1M count=2 conv=fsync" \
+	>> $seqres.full 2>&1
+
+# Confirm we hit fsgqa group quota limit, a second write must fail.
+_su fsgqa -c "dd if=/dev/zero of=$nfs_userdir/verify bs=1M count=1 conv=fsync" \
+	>> $seqres.full 2>&1 \
+	&& _fail "fsgqa group quota not enforced after writing 2MiB"
+rm -f $nfs_userdir/verify
+
+# Change file1 group to fsgqa2 using the NFS share.
+_su fsgqa -c "chgrp fsgqa2 $nfs_userdir/file1" \
+	|| _fail "chgrp of file1 to fsgqa2 failed unexpectedly"
+
+# Write a second 2MiB file via NFS as fsgqa.
+_su fsgqa -c "dd if=/dev/zero of=$nfs_userdir/file2 bs=1M count=2 conv=fsync" \
+	>> $seqres.full 2>&1
+
+# Change file2 to group fsgqa2 via using the NFS share.
+_su fsgqa -c "chgrp fsgqa2 $nfs_userdir/file2" \
+	|| _fail "chgrp of file2 to fsgqa2 failed unexpectedly"
+
+# Write a third 2MiB file via the NFS share as fsgqa.
+_su fsgqa -c "dd if=/dev/zero of=$nfs_userdir/file3 bs=1M count=2 conv=fsync" \
+	>> $seqres.full 2>&1
+
+# This chgrp must fail: fsgqa2 is at its 4MiB hardlimit.
+_user_do "chgrp fsgqa2 $nfs_userdir/file3" | \
+	sed "s|$nfs_userdir|MPOINT/userdir|g"
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/803.out b/tests/generic/803.out
new file mode 100644
index 000000000000..3830b8595793
--- /dev/null
+++ b/tests/generic/803.out
@@ -0,0 +1,2 @@
+QA output created by 803
+chgrp: changing group of 'MPOINT/userdir/file3': Disk quota exceeded
-- 
2.55.0


  parent reply	other threads:[~2026-09-07 16:41 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-07 16:40 [RFC PATCH 0/2] quota evasion test and nfs helpers cem
2026-09-07 16:40 ` [RFC PATCH 1/2] common/nfs: add management helpers cem
2026-09-08 14:22   ` Darrick J. Wong
2026-09-09  6:12     ` Christoph Hellwig
2026-09-09 16:13       ` Darrick J. Wong
2026-09-10  9:45         ` Carlos Maiolino
2026-09-10 11:30       ` Jeff Layton
2026-09-10 11:40         ` Carlos Maiolino
2026-09-10 11:48           ` Jeff Layton
2026-09-07 16:40 ` cem [this message]
2026-09-08 10:25   ` [RFC PATCH 2/2] generic: add test for quota enforcement via a nfs share Jan Kara
2026-09-08 10:29 ` [RFC PATCH 0/2] quota evasion test and nfs helpers Jan Kara
2026-09-10 11:34 ` Jeff Layton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260907164054.111393-3-cem@kernel.org \
    --to=cem@kernel.org \
    --cc=djwong@kernel.org \
    --cc=fstests@vger.kernel.org \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=linux-xfs@vger.kernel.org \
    --cc=zlang@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox