SELinux Security Module development
 help / color / mirror / Atom feed
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

      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