From: "Darrick J. Wong" <djwong@kernel.org>
To: Carlos Maiolino <cem@kernel.org>
Cc: Christoph Hellwig <hch@lst.de>, Jan Kara <jack@suse.cz>,
linux-xfs@vger.kernel.org, stable@vger.kernel.org,
Dave Chinner <david@fromorbit.com>,
Eric Sandeen <sandeen@redhat.com>,
"Dr. Thomas Orgis" <thomas.orgis@uni-hamburg.de>,
linux-fsdevel@vger.kernel.org,
Christian Brauner <brauner@kernel.org>
Subject: Re: [PATCH] xfs: fix capabily check in xfs_setattr_nonsize
Date: Thu, 25 Jun 2026 09:03:17 -0700 [thread overview]
Message-ID: <20260625160317.GY6078@frogsfrogsfrogs> (raw)
In-Reply-To: <aj1CdKwE3A40vQhQ@nidhogg.toxiclabs.cc>
On Thu, Jun 25, 2026 at 05:00:21PM +0200, Carlos Maiolino wrote:
> On Thu, Jun 25, 2026 at 02:37:54PM +0200, Christoph Hellwig wrote:
> > On Thu, Jun 25, 2026 at 12:43:54PM +0200, Jan Kara wrote:
> > > On Wed 24-06-26 15:40:39, Christoph Hellwig wrote:
> > > > Adding Jan and Christian for quota and user_ns knowledge.
> > > >
> > > > On Wed, Jun 24, 2026 at 12:14:29PM +0200, cem@kernel.org wrote:
> > > > > From: Carlos Maiolino <cem@kernel.org>
> > > > >
> > > > > An user reported a bug where he managed to evade group's quota
> > > > > by changing a file's gid to a different group id the same user
> > > > > belonged to, even though quotas were enforced on both gids and the
> > > > > file's size was big enough to exceed the quota's hardlimit.
> > > > >
> > > > > Commit eba0549bc7d1 replaced a capable() call by a
> > > > > has_capability_noaudit() to prevent unnecessary selinux audit messages.
> > > > > Turns out that both calls have slightly different semantics even though
> > > > > their documentation seems similar. Where in a nutshell:
> > > > >
> > > > > capable() - Tests the task's effective credentials
> > > > > has_ns_capability_noaudit() - Tests the task's real credentials
> > > >
> > > > Eww..
> > >
> > > Yeah, that's a catch.
I spent a while trying to figure out how I went wrong in selecting the
function name:
* capable - Determine if the current task has a superior capability in effect
* @cap: The capability to be tested for
*
* Return true if the current task has the given superior capability currently
* available for use, false if not.
vs.
* has_capability_noaudit - Does a task have a capability (unaudited) in the
* initial user ns
* @t: The task in question
* @cap: The capability to be tested for
*
* Return true if the specified task has the given superior capability
* currently in effect to init_user_ns, false if not. Don't write an
* audit message for the check.
vs.
* ns_capable_noaudit - Determine if the current task has a superior capability
* (unaudited) in effect
* @ns: The usernamespace we want the capability in
* @cap: The capability to be tested for
*
* Return true if the current task has the given superior capability currently
* available for use, false if not.
All three of these sound the same to me. But what about the call path?
capable -> ns_capable -> ns_capable_common
has_capability_noaudit -> has_ns_capability_noaudit
ns_capable_noaudit -> ns_capable_common
Hum, maybe that's the difference -- ns_capable_common vs.
has_ns_capability_noaudit?
ns_capable_common calls security_capable() with current_cred(), whereas
has_ns_capability_noaudit calls it with __task_cred(t), which is
@current in the xfs_trans_alloc_ichange case.
Aha! current_cred is current->cred, whereas __task_cred(current) is
current->real_cred, and these aren't the same thing:
/* Objective and real subjective task credentials (COW): */
const struct cred __rcu *real_cred;
/* Effective (overridable) subjective task credentials (COW): */
const struct cred __rcu *cred;
So I guess ns_capable_common (and wrappers) are testing the process'
effective credentials, whereas has_ns_capability_noaudit is testing the
process' real credentials?
It would have been *really* nice if the documentation for those
functions mentioned that distinction!
> > > > > This most of the time has no practical difference but in some cases like
> > > > > changing attrs (specifically group id in this case) through a NFS client
> > > > > this will allow the quota code to use XFS_QMOPT_FORCE_RES, effectively
> > > > > bypassing quota accounting checks.
> > > >
> > > > Yeah, this does look wrong. Do the other conversion in the above commit
> > > > have tthe same issue?
> > > >
> > > > > Using instead ns_capable_noaudit() should fix this issue and prevent
> > > > > selinux audit messages.
> > > >
> > > > The generic quota code manages to do without either has_capability_noaudit
> > > > or ns_capable_noaudit. I think this might be hidden behind
> > > > inode_owner_or_capable calls. Any idea why we're different?
> > >
> > > Actually no. Generic quota code has equivalent checks in ignore_hardlimit()
> > > function which does capable(CAP_SYS_RESOURCE) check. I guess the reason why
> > > nobody complained about generic quota code is that we call
> > > ignore_hardlimit() only if we are above hardlimit whereas XFS calls this
> > > for every transaction...
> >
> > I guess we should aim for the same to avoid the spurious audit logs.
> >
> > I.e. xfs_trans_alloc_ichange is currently always called either with
> > force = true or force = this capable check. So as a first step we can
> > move the check into xfs_trans_alloc_ichange for the !force case, and the
> > propagate that through XFS_QMOPT_FORCE_RES into xfs_trans_dqresv, i.e.
> > only set XFS_QMOPT_FORCE_RES for the real forced case and instead
> > have the capable check down in xfs_trans_dqresv.
> >
>
> Sounds fair, I'll give it a try tomorrow.
So yeah, I agree we should change that to:
ns_capable_noaudit(&init_user_ns, CAP_FOWNER)
Though it's also weird that XFS gates it on CAP_FOWNER whereas the VFS
checks CAP_SYS_RESOURCE. Though I would have added this:
static inline bool
current_may_ignore_quota_limits(void)
{
/*
* If the current process' effective credentials include
* CAP_FOWNER, then they're allowed to ignore the hard limit.
*/
return ns_capable_noaudit(&init_user_ns, CAP_FOWNER);
}
and then changed the callsites in xfs_ioctl/xfs_iops.c to:
error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
current_may_ignore_quota_limits(), &tp);
The has_capability_noaudit call in xfs_fsmap.c should change to
ns_capable_noaudit.
--D
next prev parent reply other threads:[~2026-06-25 16:03 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-24 10:14 [PATCH] xfs: fix capabily check in xfs_setattr_nonsize cem
2026-06-24 13:40 ` Christoph Hellwig
2026-06-24 18:59 ` Carlos Maiolino
2026-06-25 10:43 ` Jan Kara
2026-06-25 11:01 ` Carlos Maiolino
2026-06-25 12:37 ` Christoph Hellwig
2026-06-25 15:00 ` Carlos Maiolino
2026-06-25 16:03 ` Darrick J. Wong [this message]
2026-06-26 7:34 ` Carlos Maiolino
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=20260625160317.GY6078@frogsfrogsfrogs \
--to=djwong@kernel.org \
--cc=brauner@kernel.org \
--cc=cem@kernel.org \
--cc=david@fromorbit.com \
--cc=hch@lst.de \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
--cc=sandeen@redhat.com \
--cc=stable@vger.kernel.org \
--cc=thomas.orgis@uni-hamburg.de \
/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