From: sashiko-bot@kernel.org
To: "Jan Koester" <jan.koester@tuxist.de>,
"Christian Brauner" <brauner@kernel.org>,
"Alexander Viro" <viro@zeniv.linux.org.uk>,
"Serge E. Hallyn" <serge@hallyn.com>
Cc: "Jan Kara" <jack@suse.cz>, "Theodore Y. Ts'o" <tytso@mit.edu>,
"Paul Moore" <paul@paul-moore.com>,
"Arnd Bergmann" <arnd@arndb.de>,
linux-security-module@vger.kernel.org,
linux-ext4@vger.kernel.org, audit@vger.kernel.org,
keyrings@vger.kernel.org, "Chuck Lever" <cel@kernel.org>,
"Jeff Layton" <jlayton@kernel.org>,
"Anna Schumaker" <anna@kernel.org>
Subject: Re: [RFC PATCH v2 21/41] fs: add chown_r(), lchown_r(), fchown_r() and fchownat_r()
Date: Thu, 22 Oct 2026 03:17:45 +0000 [thread overview]
Message-ID: <sashiko-outbox-180922@kernel.org> (raw)
In-Reply-To: <54f57a7ac52b6b2b130b5a6e911e0998c9630da8.1792552161.git.jan.koester@tuxist.de>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] fs: broken unchanged and malformed ID handling in chown_path() for !MULTIUSER
- [High] fs: missing s_user_ns mapping check for UUID/GUID in chown_path()
- [Medium] fs: chown_ids_r() accepts nonzero reserved bytes for UID_R_NONE and -1
--
commit c1c7ee0a217eb922615794b81d3b58341468ee6f
Author: Jan Koester <jan.koester@tuxist.de>
fs: add chown_r(), lchown_r(), fchown_r() and fchownat_r()
This commit adds the chown_r(), lchown_r(), fchown_r(), and fchownat_r()
system calls accepting struct __kernel_uid_r owners and groups, and refactors
the classic chown paths to share lookup and setattr logic via struct
chown_ids and chown_path().
> diff --git a/fs/open.c b/fs/open.c
> index 6b1c14e684a93..26175b5aa8aa6 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -785,7 +785,46 @@ static inline bool setattr_vfsgid(struct iattr *attr, kgid_t kgid)
> return true;
> }
>
> -int chown_common(const struct path *path, uid_t user, gid_t group)
> +/*
> + * The new owner and group of a chown. Invalid ids stand for "unchanged";
> + * @err is an error of their conversion, reported once the path is found,
> + * which keeps the classic calls' order of errors.
> + */
> +struct chown_ids {
> + kuid_t uid;
> + kgid_t gid;
> + int err;
> +};
> +
> +static struct chown_ids chown_ids_classic(uid_t user, gid_t group)
> +{
> + struct user_namespace *ns = current_user_ns();
> + struct chown_ids ids = {
> + .uid = make_kuid(ns, user),
> + .gid = make_kgid(ns, group),
> + };
> +
> + if ((user != (uid_t)-1 && !uid_valid(ids.uid)) ||
> + (group != (gid_t)-1 && !gid_valid(ids.gid)))
> + ids.err = -EINVAL;
> + return ids;
> +}
> +
> +static int chown_ids_r(const struct __kernel_uid_r __user *user,
> + const struct __kernel_uid_r __user *group,
> + struct chown_ids *ids)
> +{
> + struct user_namespace *ns = current_user_ns();
> + int err;
> +
> + ids->err = 0;
> + err = get_kuid_r(ns, user, &ids->uid);
> + if (!err)
> + err = get_kgid_r(ns, group, &ids->gid);
> + return err;
> +}
[Severity: Medium]
When userspace calls sys_chown_r(), sys_lchown_r(), sys_fchown_r(), or
sys_fchownat_r() with a struct __kernel_uid_r whose type is UID_R_NONE or
UID_R_LEGACY with legacy == (u32)-1, can non-zero bytes in the reserved union
area bypass validation?
In include/uapi/linux/uidgid_r.h, struct __kernel_uid_r documents:
/*
* Bytes past the member .type selects are reserved and must be zero.
*/
However, chown_ids_r() delegates to get_kuid_r() and get_kgid_r() in
kernel/uidgid_r.c, which return 0 as soon as kid_r_is_none(&id) matches,
before make_kuid_r() or make_kgid_r() calls kid_r_valid(&id):
static bool kid_r_is_none(const struct __kernel_uid_r *id)
{
return id->type == UID_R_NONE ||
(id->type == UID_R_LEGACY && id->legacy == (u32)-1);
}
int get_kuid_r(struct user_namespace *from,
const struct __kernel_uid_r __user *uid, kuid_t *kuid)
{
...
if (kid_r_is_none(&id))
return 0;
*kuid = make_kuid_r(from, &id);
return uid_valid(*kuid) ? 0 : -EINVAL;
}
Because kid_r_is_none() does not check the 16 union bytes for UID_R_NONE or
the 12 trailing bytes (id->uuid + sizeof(id->legacy)) for UID_R_LEGACY with
legacy == (u32)-1, shouldn't those reserved bytes be verified to be zero so
that non-zero trailing bytes are rejected with -EINVAL?
> +
> +static int chown_path(const struct path *path, const struct chown_ids *ids)
> {
> struct mnt_idmap *idmap;
> struct user_namespace *fs_userns;
> @@ -793,11 +832,9 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
> struct delegated_inode delegated_inode = { };
> int error;
> struct iattr newattrs;
> - kuid_t uid;
> - kgid_t gid;
>
> - uid = make_kuid(current_user_ns(), user);
> - gid = make_kgid(current_user_ns(), group);
> + if (ids->err)
> + return ids->err;
>
> idmap = mnt_idmap(path->mnt);
> fs_userns = i_user_ns(inode);
> @@ -806,9 +843,9 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
> newattrs.ia_vfsuid = INVALID_VFSUID;
> newattrs.ia_vfsgid = INVALID_VFSGID;
> newattrs.ia_valid = ATTR_CTIME;
> - if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
> + if (uid_valid(ids->uid) && !setattr_vfsuid(&newattrs, ids->uid))
> return -EINVAL;
> - if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
> + if (gid_valid(ids->gid) && !setattr_vfsgid(&newattrs, ids->gid))
> return -EINVAL;
[Severity: High]
How does chown_path() handle unchanged IDs (-1, NULL, or UID_R_NONE) and
malformed IDs when CONFIG_MULTIUSER is disabled?
Previously, chown_common() checked (user != (uid_t)-1) and
(group != (gid_t)-1) before calling setattr_vfsuid() and setattr_vfsgid().
With this change, chown_ids_classic() and chown_ids_r() encode an unchanged
owner or group as INVALID_UID / INVALID_GID, and chown_path() uses
uid_valid(ids->uid) and gid_valid(ids->gid) to decide whether to set ATTR_UID
and ATTR_GID.
In include/linux/uidgid.h, when !CONFIG_MULTIUSER, __kuid_val() and
__kgid_val() always return 0:
static inline uid_t __kuid_val(kuid_t uid)
{
return 0;
}
static inline bool uid_valid(kuid_t uid)
{
return !uid_is_legacy(uid) || __kuid_val(uid) != (uid_t) -1;
}
As a result, uid_valid(INVALID_UID) and gid_valid(INVALID_GID) always return
true on !CONFIG_MULTIUSER builds:
1. Calling chown(), lchown(), fchown(), or fchownat() with -1, or calling
chown_r(), lchown_r(), fchown_r(), or fchownat_r() with NULL, UID_R_NONE,
or legacy -1 causes chown_path() to call setattr_vfsuid() and
setattr_vfsgid() with INVALID_UID and INVALID_GID. That sets ATTR_UID and
ATTR_GID in newattrs.ia_valid and copies VFSUIDT_INIT(INVALID_UID) and
VFSGIDT_INIT(INVALID_GID) (with .legacy = -1) into newattrs.ia_vfsuid and
newattrs.ia_vfsgid, causing notify_change() -> may_setattr() to reject
no-op chown calls on immutable/append-only files with -EPERM and causing
setattr_copy() to write INVALID_UID / INVALID_GID into inode->i_uid and
inode->i_gid.
2. In chown_ids_r() -> get_kuid_r() / get_kgid_r(), malformed
struct __kernel_uid_r inputs for which make_kuid_r() or make_kgid_r()
returns INVALID_UID / INVALID_GID pass uid_valid(*kuid) / gid_valid(*kgid)
and return 0 instead of -EINVAL.
3. In chown_path(), because setattr_vfsuid() and setattr_vfsgid() only return
false when !uid_valid() or !gid_valid(), gating them with
uid_valid(ids->uid) and gid_valid(ids->gid) also makes the two
return -EINVAL branches unreachable.
> error = inode_lock_killable(inode);
> if (error)
[Severity: High]
What prevents chown_path() from setting a UUID or GUID owner or group on a
non-idmapped mount of a filesystem mounted in a non-initial user namespace
(inode->i_sb->s_user_ns != &init_user_ns)?
When a process in init_user_ns calls chown_r(), lchown_r(), fchown_r(), or
fchownat_r() with a UUID or GUID on a regular mount (idmap == &nop_mnt_idmap)
of a filesystem with SB_I_UID_R mounted in a child user namespace (such as
tmpfs, ramfs, devpts, or overlayfs), chown_path() calls security_path_chown()
with from_vfsuid() / from_vfsgid() and then calls notify_change().
In fs/attr.c:notify_change(), the superblock namespace check uses
vfsuid_has_fsmapping() and vfsgid_has_fsmapping():
if (ia_valid & ATTR_UID &&
!vfsuid_has_fsmapping(idmap, inode->i_sb->s_user_ns,
attr->ia_vfsuid))
return -EOVERFLOW;
However, vfsuid_has_fsmapping() in include/linux/mnt_idmapping.h simply
checks uid_valid(from_vfsuid(idmap, fs_userns, vfsuid)), and from_vfsuid() in
fs/mnt_idmapping.c returns early for nop_mnt_idmap before checking
kuid_has_mapping(fs_userns, ...):
kuid_t from_vfsuid(struct mnt_idmap *idmap,
struct user_namespace *fs_userns, vfsuid_t vfsuid)
{
uid_t uid;
if (idmap == &nop_mnt_idmap)
return AS_KUIDT(vfsuid);
if (idmap == &invalid_mnt_idmap)
return INVALID_UID;
if (!vfsuid_is_legacy(vfsuid))
return kuid_has_mapping(fs_userns, AS_KUIDT(vfsuid)) ?
AS_KUIDT(vfsuid) : INVALID_UID;
Because UUID and GUID identities only have mappings in init_user_ns,
shouldn't non-legacy IDs also be checked against fs_userns when
idmap == &nop_mnt_idmap so that notify_change() rejects them with -EOVERFLOW
instead of writing an unmapped UUID or GUID to the inode?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1792552161.git.jan.koester@tuxist.de?part=21
next prev parent reply other threads:[~2026-10-22 3:17 UTC|newest]
Thread overview: 89+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 3:15 [RFC PATCH v2 00/41] UUID and GUID user and group ids (*_r system calls) Jan Koester
2026-10-21 3:15 ` [RFC PATCH v2 01/41] uuid: move uuid_t and guid_t into <linux/uuid_types.h> Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 02/41] uapi: add <linux/uidgid_r.h> for 128 bit capable user and group ids Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 03/41] uidgid: add uid_is_legacy() and gid_is_legacy() Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 04/41] net: sock: read sk_uid through a seqcount Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 05/41] af_unix: keep sender ids in skb->cb only when they are classic ids Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 06/41] netlink: keep classic sender ids in skb->cb Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 07/41] Bluetooth: " Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 08/41] smb: client: use __kuid_val() for the unix SIDs of the caller Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 09/41] uidgid: let kuid_t and kgid_t hold a UUID or a GUID Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 10/41] uidgid: convert between kernel ids and the ids of the *_r system calls Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 11/41] fscrypt: show the whole uid in the description of user key claims Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 12/41] keys: name per-user keyrings of UUID and GUID users by the whole uid Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 13/41] ublk: refuse unprivileged devices to UUID and GUID users Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 14/41] nfsd: don't hand out uid 0 for read layouts of UUID-owned files Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 15/41] SUNRPC: name UUID and GUID users to gssd as uid_r= Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 16/41] ecryptfs: hash daemons by kuid_hash() Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 17/41] quota: don't account UUID and GUID owners Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 18/41] fs: keep UUID and GUID owners off filesystems that can't store them Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 19/41] cred: add the *_r variants of the get/set*id() system calls Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 20/41] groups: add getgroups_r() and setgroups_r() Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 21/41] fs: add chown_r(), lchown_r(), fchown_r() and fchownat_r() Jan Koester
2026-10-22 3:17 ` sashiko-bot [this message]
2026-10-21 3:15 ` [RFC PATCH v2 22/41] arch: wire up the *_r uid and gid system calls Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 23/41] statx: report UUID and GUID owners Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 24/41] shmem, ramfs: allow " Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 25/41] uidgid: parse and print ids in their text form Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 26/41] proc: show UUID and GUID ids in status, accept them for loginuid Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 27/41] audit: log UUID and GUID ids in full Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 28/41] net: add SO_PEERCRED_R, SO_PEERGROUPS_R and SCM_CREDENTIALS_R Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 29/41] signal: report the sender's UUID or GUID uid in si_uid_r Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 30/41] ipc: add IPC_STAT_R and IPC_SET_R Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 31/41] ext4: add the uid_r feature for UUID and GUID owners Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 32/41] ext4: let uid_r be turned on while the filesystem is mounted Jan Koester
2026-10-22 3:17 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 33/41] posix_acl: support UUID and GUID entries Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 34/41] ext4: store ACLs with UUID and GUID entries on uid_r filesystems Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 35/41] Documentation: describe UUID and GUID user and group ids Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 36/41] selftests: add tests for UUID and GUID ids Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 37/41] ovl: allow UUID and GUID owners when the upper layer does Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 38/41] devpts: allow UUID and GUID owners Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:15 ` [RFC PATCH v2 39/41] ksmbd: log in UUID and GUID users and map them to SIDs Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:16 ` [RFC PATCH v2 40/41] nfsd, nfs: carry UUID and GUID owners as their text form Jan Koester
2026-10-21 17:35 ` Chuck Lever
2026-10-22 2:52 ` Takeshi Nishimura
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:16 ` [RFC PATCH v2 41/41] Documentation: uid_r: describe SMB and NFSv4 Jan Koester
2026-10-22 3:18 ` sashiko-bot
2026-10-21 3:20 ` [RFC PATCH v2 00/41] UUID and GUID user and group ids (*_r system calls) jan Koester
2026-10-21 14:26 ` Adding dreaded Windows SID-style, or. Repeating history, the bad chapters. UUID/GUIDRe: " Lionel Cons
2026-10-22 17:00 ` Casey Schaufler
2026-10-22 18:26 ` Theodore Tso
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=sashiko-outbox-180922@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=anna@kernel.org \
--cc=arnd@arndb.de \
--cc=audit@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=cel@kernel.org \
--cc=jack@suse.cz \
--cc=jan.koester@tuxist.de \
--cc=jlayton@kernel.org \
--cc=keyrings@vger.kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=paul@paul-moore.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=serge@hallyn.com \
--cc=tytso@mit.edu \
--cc=viro@zeniv.linux.org.uk \
/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