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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.