From: sashiko-bot@kernel.org
Cc: selinux@vger.kernel.org, linux-security-module@vger.kernel.org
Subject: Re: [RFC PATCH] af_unix: introduce MSG_CMSG_SEALED
Date: Tue, 03 Nov 2026 13:05:50 +0000 [thread overview]
Message-ID: <sashiko-outbox-195303@kernel.org> (raw)
In-Reply-To: <20261102130540.252894-1-omosnacek@gmail.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] net: scm: missing get_file() and fput() when unwrapping in scm_fp_copy()
- [High] net: scm: missing fput() on error paths in scm_fp_copy()
- [High] net: scm: file reference leak in scm_recv_one_fd() when FD_PREPARE() fails
- [Medium] net: sk_getsockopt() copies zeroed int for SO_RIGHTS_SEALED_SUPPORTED
--
commit bff932de78fdb71452bc8ca043d1f0a85ab5a42a
Author: Ondrej Mosnáček <omosnacek@gmail.com>
af_unix: introduce MSG_CMSG_SEALED
This commit introduces the MSG_CMSG_SEALED flag for AF_UNIX SCM_RIGHTS to
allow processes to receive file descriptors wrapped in an opaque anonymous
inode without triggering the security_file_receive() LSM hook, and to
unwrap them when forwarding via sendmsg(). It also adds the read-only
SO_RIGHTS_SEALED_SUPPORTED socket option to query support for this feature.
> diff --git a/net/core/scm.c b/net/core/scm.c
> index ae38f3c1165c8..08822b2a1f222 100644
> --- a/net/core/scm.c
> +++ b/net/core/scm.c
[ ... ]
> @@ -114,10 +127,29 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
>
> if (fd < 0 || !(file = fget_raw(fd)))
> return -EBADF;
> - /* don't allow io_uring files */
> - if (io_is_uring_fops(file)) {
> - fput(file);
> - return -EINVAL;
> +
> + if (sealed) {
> + /* Only allow sealed files */
> + if (file->f_op != &envelope_ops)
> + return -EINVAL;
> +
> + file = (struct file *)file->private_data;
[Severity: Critical]
When unwrapping the envelope file in scm_fp_copy(), doesn't overwriting file
with file->private_data need to take a new reference on the inner struct file
with get_file() and drop the reference on the outer envelope file from
fget_raw() with fput()?
Without calling fput() on the outer envelope file, the reference acquired by
fget_raw(fd) is leaked on every sealed sendmsg(), preventing
envelope_release() from running when the envelope descriptor is closed.
At the same time, storing the inner struct file into fpl->fp[] without
calling get_file() means fpl->fp[] does not own a reference to it, even
though __scm_destroy() will later call fput() on every entry in fpl->fp[]:
net/core/scm.c:__scm_destroy() {
...
for (i=fpl->count-1; i>=0; i--)
fput(fpl->fp[i]);
...
}
If a caller sends the same envelope descriptor more than once with
MSG_CMSG_SEALED, __scm_destroy() will decrement the inner file's reference
count multiple times without matching increments, leading to a reference
count underflow and use-after-free on the inner struct file.
> +
> + /*
> + * No need to check for io_uring files here as the
> + * underlying file had already successfully passed the
> + * check when it was first sent.
> + */
> + } else {
> + /* Don't allow sealed files */
> + if (file->f_op == &envelope_ops)
> + return -EINVAL;
> +
> + /* don't allow io_uring files */
> + if (io_is_uring_fops(file)) {
> + fput(file);
> + return -EINVAL;
> + }
> }
[Severity: High]
Do the two envelope_ops checks in scm_fp_copy() leak the struct file
reference acquired by fget_raw(fd)?
When sealed is true and file->f_op != &envelope_ops, or when sealed is false
and file->f_op == &envelope_ops, file has not yet been added to fpl->fp[], so
__scm_destroy() in __scm_send()'s error path will not release it. Unlike the
io_is_uring_fops(file) check right below, returning -EINVAL directly without
calling fput(file) leaves the reference from fget_raw(fd) leaked.
[ ... ]
> @@ -365,18 +398,37 @@ static int scm_max_fds(struct msghdr *msg)
> }
>
> int scm_recv_one_fd(struct file *f, int __user *ufd, unsigned int flags,
> - bool notrunc)
> + bool notrunc, bool sealed)
> {
> + struct file *f_received;
> int error;
>
> if (!ufd)
> return -EFAULT;
>
> - error = security_file_receive(f);
> - if (error)
> - return notrunc ? put_user(error, ufd) : error;
> + if (sealed) {
> + /*
> + * Receive fd sealed in an "envelope" anonymous fd.
> + * This will install a special file that can be unsealed
> + * upon sending to another process, otherwise the underlying
> + * file will not be accessible to this process, avoiding the
> + * need to pass the LSM check below.
> + */
> + f_received = anon_inode_getfile("[envelope]", &envelope_ops, f,
> + O_RDONLY);
> + if (IS_ERR(f_received))
> + return PTR_ERR(f_received);
> +
> + get_file(f);
> + } else {
> + error = security_file_receive(f);
> + if (error)
> + return notrunc ? put_user(error, ufd) : error;
>
> - FD_PREPARE(fdf, flags, get_file(f));
> + f_received = get_file(f);
> + }
> +
> + FD_PREPARE(fdf, flags, f_received);
> if (fdf->fd < 0)
> return fdf->fd;
[Severity: High]
If get_unused_fd_flags() fails inside FD_PREPARE(), what releases f_received?
Looking at __FD_PREPARE() in include/linux/file.h:
#define __FD_PREPARE(_guard, _fdf, _fd_flags, _file_owned) \
struct fd_prepare _guard __cleanup(__fd_prepare_cleanup) = ({ \
int __fd = get_unused_fd_flags(_fd_flags); \
__fd_prepare(__fd, __fd < 0 ? NULL : (_file_owned)); \
}); \
const struct fd_prepare *const _fdf = &_guard
Previously, get_file(f) was passed directly as the _file_owned argument to
FD_PREPARE(), so it was only evaluated when __fd >= 0. Because f_received is
now allocated or incremented before FD_PREPARE(), when get_unused_fd_flags()
returns a negative error (such as -EMFILE), _guard.file is initialized to
NULL and __fd_prepare_cleanup() does not call fput() on f_received, leaking
f_received (and the inner file reference in the sealed case).
> diff --git a/net/core/sock.c b/net/core/sock.c
> index e7405be8941f9..68a447d8bf52b 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1959,6 +1959,15 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
> v.val = sk->sk_scm_rights;
> break;
>
> + case SO_RIGHTS_SEALED_SUPPORTED:
> + if (!sk_is_unix(sk))
> + return -EOPNOTSUPP;
> +
> + /* Just succeed without returning anything, signaling
> + * support.
> + */
> + break;
[Severity: Medium]
Should the SO_RIGHTS_SEALED_SUPPORTED case in sk_getsockopt() set lv = 0 (or
return 0 directly) instead of breaking out of the switch with lv still set to
sizeof(int)?
At the start of sk_getsockopt(), lv is initialized to sizeof(int) and v is
zeroed. Breaking out of the switch runs the shared copy-out epilogue:
if (len > lv)
len = lv;
if (copy_to_sockptr(optval, &v, len))
return -EFAULT;
lenout:
if (copy_to_sockptr(optlen, &len, sizeof(int)))
return -EFAULT;
return 0;
If a caller passes a non-zero optlen, sk_getsockopt() will either return
-EFAULT when optval is NULL or write 0 into optval and update *optlen to
up to sizeof(int), rather than succeeding without returning a value as
described in the comment.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102130540.252894-1-omosnacek@gmail.com?part=1
prev parent reply other threads:[~2026-11-03 13:05 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 13:05 [RFC PATCH] af_unix: introduce MSG_CMSG_SEALED Ondrej Mosnáček
2026-11-02 13:13 ` Ondrej Mosnáček
2026-11-02 15:14 ` Stephen Smalley
2026-11-02 19:06 ` Stephen Smalley
2026-11-02 19:39 ` Paul Moore
2026-11-03 13:05 ` sashiko-bot [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=sashiko-outbox-195303@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=selinux@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