The inode is I_FREEING | I_CLEAR.
I suspect this is botched error handling in the recent conversion to
FD_PREPARE machinery.
#syz test
diff --git b/net/sctp/socket.c a/net/sctp/socket.c
index 1e59ac734f91..ed8293a34240 100644
--- b/net/sctp/socket.c
+++ a/net/sctp/socket.c
@@ -5664,45 +5664,47 @@ static int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id,
return err;
}
-static int sctp_getsockopt_peeloff_common(struct sock *sk,
- sctp_peeloff_arg_t *peeloff, int len,
- char __user *optval,
- int __user *optlen, unsigned flags)
+static int sctp_getsockopt_peeloff_common(struct sock *sk, sctp_peeloff_arg_t *peeloff,
+ struct file **newfile, unsigned flags)
{
struct socket *newsock;
int retval;
retval = sctp_do_peeloff(sk, peeloff->associd, &newsock);
if (retval < 0)
- return retval;
+ goto out;
- FD_PREPARE(fdf, flags & SOCK_CLOEXEC, sock_alloc_file(newsock, 0, NULL));
- if (fdf.err) {
+ /* Map the socket to an unused fd that can be returned to the user. */
+ retval = get_unused_fd_flags(flags & SOCK_CLOEXEC);
+ if (retval < 0) {
sock_release(newsock);
- return fdf.err;
+ goto out;
}
- pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
- fd_prepare_fd(fdf));
-
- if (flags & SOCK_NONBLOCK)
- fd_prepare_file(fdf)->f_flags |= O_NONBLOCK;
+ *newfile = sock_alloc_file(newsock, 0, NULL);
+ if (IS_ERR(*newfile)) {
+ put_unused_fd(retval);
+ retval = PTR_ERR(*newfile);
+ *newfile = NULL;
+ return retval;
+ }
- /* Return the fd mapped to the new socket. */
- if (put_user(len, optlen))
- return -EFAULT;
+ pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
+ retval);
- peeloff->sd = fd_prepare_fd(fdf);
- if (copy_to_user(optval, peeloff, len))
- return -EFAULT;
+ peeloff->sd = retval;
- return fd_publish(fdf);
+ if (flags & SOCK_NONBLOCK)
+ (*newfile)->f_flags |= O_NONBLOCK;
+out:
+ return retval;
}
-static int sctp_getsockopt_peeloff(struct sock *sk, int len,
- char __user *optval, int __user *optlen)
+static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
sctp_peeloff_arg_t peeloff;
+ struct file *newfile = NULL;
+ int retval = 0;
if (len < sizeof(sctp_peeloff_arg_t))
return -EINVAL;
@@ -5710,13 +5712,33 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len,
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
- return sctp_getsockopt_peeloff_common(sk, &peeloff, len, optval, optlen, 0);
+ retval = sctp_getsockopt_peeloff_common(sk, &peeloff, &newfile, 0);
+ if (retval < 0)
+ goto out;
+
+ /* Return the fd mapped to the new socket. */
+ if (put_user(len, optlen)) {
+ fput(newfile);
+ put_unused_fd(retval);
+ return -EFAULT;
+ }
+
+ if (copy_to_user(optval, &peeloff, len)) {
+ fput(newfile);
+ put_unused_fd(retval);
+ return -EFAULT;
+ }
+ fd_install(retval, newfile);
+out:
+ return retval;
}
static int sctp_getsockopt_peeloff_flags(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
sctp_peeloff_flags_arg_t peeloff;
+ struct file *newfile = NULL;
+ int retval = 0;
if (len < sizeof(sctp_peeloff_flags_arg_t))
return -EINVAL;
@@ -5724,8 +5746,26 @@ static int sctp_getsockopt_peeloff_flags(struct sock *sk, int len,
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
- return sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg, len, optval,
- optlen, peeloff.flags);
+ retval = sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg,
+ &newfile, peeloff.flags);
+ if (retval < 0)
+ goto out;
+
+ /* Return the fd mapped to the new socket. */
+ if (put_user(len, optlen)) {
+ fput(newfile);
+ put_unused_fd(retval);
+ return -EFAULT;
+ }
+
+ if (copy_to_user(optval, &peeloff, len)) {
+ fput(newfile);
+ put_unused_fd(retval);
+ return -EFAULT;
+ }
+ fd_install(retval, newfile);
+out:
+ return retval;
}
/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)