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)