diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 115 | 
1 files changed, 34 insertions, 81 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d032eb5fa6df..d5f10783c251 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -546,7 +546,7 @@ static void unix_write_space(struct sock *sk)  		if (skwq_has_sleeper(wq))  			wake_up_interruptible_sync_poll(&wq->wait,  				EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND); -		sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); +		sk_wake_async_rcu(sk, SOCK_WAKE_SPACE, POLL_OUT);  	}  	rcu_read_unlock();  } @@ -755,7 +755,7 @@ static int unix_bind(struct socket *, struct sockaddr *, int);  static int unix_stream_connect(struct socket *, struct sockaddr *,  			       int addr_len, int flags);  static int unix_socketpair(struct socket *, struct socket *); -static int unix_accept(struct socket *, struct socket *, int, bool); +static int unix_accept(struct socket *, struct socket *, struct proto_accept_arg *arg);  static int unix_getname(struct socket *, struct sockaddr *, int);  static __poll_t unix_poll(struct file *, struct socket *, poll_table *);  static __poll_t unix_dgram_poll(struct file *, struct socket *, @@ -979,11 +979,11 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern,  	sk->sk_max_ack_backlog	= net->unx.sysctl_max_dgram_qlen;  	sk->sk_destruct		= unix_sock_destructor;  	u = unix_sk(sk); -	u->inflight = 0; +	u->listener = NULL; +	u->vertex = NULL;  	u->path.dentry = NULL;  	u->path.mnt = NULL;  	spin_lock_init(&u->lock); -	INIT_LIST_HEAD(&u->link);  	mutex_init(&u->iolock); /* single task reading lock */  	mutex_init(&u->bindlock); /* single task binding lock */  	init_waitqueue_head(&u->peer_wait); @@ -1597,6 +1597,7 @@ restart:  	newsk->sk_type		= sk->sk_type;  	init_peercred(newsk);  	newu = unix_sk(newsk); +	newu->listener = other;  	RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);  	otheru = unix_sk(other); @@ -1688,19 +1689,18 @@ static void unix_sock_inherit_flags(const struct socket *old,  		set_bit(SOCK_PASSSEC, &new->flags);  } -static int unix_accept(struct socket *sock, struct socket *newsock, int flags, -		       bool kern) +static int unix_accept(struct socket *sock, struct socket *newsock, +		       struct proto_accept_arg *arg)  {  	struct sock *sk = sock->sk; -	struct sock *tsk;  	struct sk_buff *skb; -	int err; +	struct sock *tsk; -	err = -EOPNOTSUPP; +	arg->err = -EOPNOTSUPP;  	if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)  		goto out; -	err = -EINVAL; +	arg->err = -EINVAL;  	if (sk->sk_state != TCP_LISTEN)  		goto out; @@ -1708,12 +1708,12 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,  	 * so that no locks are necessary.  	 */ -	skb = skb_recv_datagram(sk, (flags & O_NONBLOCK) ? MSG_DONTWAIT : 0, -				&err); +	skb = skb_recv_datagram(sk, (arg->flags & O_NONBLOCK) ? MSG_DONTWAIT : 0, +				&arg->err);  	if (!skb) {  		/* This means receive shutdown. */ -		if (err == 0) -			err = -EINVAL; +		if (arg->err == 0) +			arg->err = -EINVAL;  		goto out;  	} @@ -1723,6 +1723,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,  	/* attach accepted sock to socket */  	unix_state_lock(tsk); +	unix_update_edges(unix_sk(tsk));  	newsock->state = SS_CONNECTED;  	unix_sock_inherit_flags(sock, newsock);  	sock_graft(tsk, newsock); @@ -1730,7 +1731,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,  	return 0;  out: -	return err; +	return arg->err;  } @@ -1789,81 +1790,29 @@ static inline bool too_many_unix_fds(struct task_struct *p)  static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)  { -	int i; -  	if (too_many_unix_fds(current))  		return -ETOOMANYREFS; -	/* Need to duplicate file references for the sake of garbage -	 * collection.  Otherwise a socket in the fps might become a -	 * candidate for GC while the skb is not yet queued. -	 */ -	UNIXCB(skb).fp = scm_fp_dup(scm->fp); -	if (!UNIXCB(skb).fp) -		return -ENOMEM; +	UNIXCB(skb).fp = scm->fp; +	scm->fp = NULL; -	for (i = scm->fp->count - 1; i >= 0; i--) -		unix_inflight(scm->fp->user, scm->fp->fp[i]); +	if (unix_prepare_fpl(UNIXCB(skb).fp)) +		return -ENOMEM;  	return 0;  }  static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)  { -	int i; -  	scm->fp = UNIXCB(skb).fp;  	UNIXCB(skb).fp = NULL; -	for (i = scm->fp->count - 1; i >= 0; i--) -		unix_notinflight(scm->fp->user, scm->fp->fp[i]); +	unix_destroy_fpl(scm->fp);  }  static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb)  {  	scm->fp = scm_fp_dup(UNIXCB(skb).fp); - -	/* -	 * Garbage collection of unix sockets starts by selecting a set of -	 * candidate sockets which have reference only from being in flight -	 * (total_refs == inflight_refs).  This condition is checked once during -	 * the candidate collection phase, and candidates are marked as such, so -	 * that non-candidates can later be ignored.  While inflight_refs is -	 * protected by unix_gc_lock, total_refs (file count) is not, hence this -	 * is an instantaneous decision. -	 * -	 * Once a candidate, however, the socket must not be reinstalled into a -	 * file descriptor while the garbage collection is in progress. -	 * -	 * If the above conditions are met, then the directed graph of -	 * candidates (*) does not change while unix_gc_lock is held. -	 * -	 * Any operations that changes the file count through file descriptors -	 * (dup, close, sendmsg) does not change the graph since candidates are -	 * not installed in fds. -	 * -	 * Dequeing a candidate via recvmsg would install it into an fd, but -	 * that takes unix_gc_lock to decrement the inflight count, so it's -	 * serialized with garbage collection. -	 * -	 * MSG_PEEK is special in that it does not change the inflight count, -	 * yet does install the socket into an fd.  The following lock/unlock -	 * pair is to ensure serialization with garbage collection.  It must be -	 * done between incrementing the file count and installing the file into -	 * an fd. -	 * -	 * If garbage collection starts after the barrier provided by the -	 * lock/unlock, then it will see the elevated refcount and not mark this -	 * as a candidate.  If a garbage collection is already in progress -	 * before the file count was incremented, then the lock/unlock pair will -	 * ensure that garbage collection is finished before progressing to -	 * installing the fd. -	 * -	 * (*) A -> B where B is on the queue of A or B is on the queue of C -	 * which is on the queue of listening socket A. -	 */ -	spin_lock(&unix_gc_lock); -	spin_unlock(&unix_gc_lock);  }  static void unix_destruct_scm(struct sk_buff *skb) @@ -1937,8 +1886,10 @@ static void scm_stat_add(struct sock *sk, struct sk_buff *skb)  	struct scm_fp_list *fp = UNIXCB(skb).fp;  	struct unix_sock *u = unix_sk(sk); -	if (unlikely(fp && fp->count)) +	if (unlikely(fp && fp->count)) {  		atomic_add(fp->count, &u->scm_stat.nr_fds); +		unix_add_edges(fp, u); +	}  }  static void scm_stat_del(struct sock *sk, struct sk_buff *skb) @@ -1946,8 +1897,10 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb)  	struct scm_fp_list *fp = UNIXCB(skb).fp;  	struct unix_sock *u = unix_sk(sk); -	if (unlikely(fp && fp->count)) +	if (unlikely(fp && fp->count)) {  		atomic_sub(fp->count, &u->scm_stat.nr_fds); +		unix_del_edges(fp); +	}  }  /* @@ -2270,7 +2223,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,  			goto out_err;  	} -	if (sk->sk_shutdown & SEND_SHUTDOWN) +	if (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN)  		goto pipe_err;  	while (sent < len) { @@ -2663,7 +2616,9 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,  					WRITE_ONCE(u->oob_skb, NULL);  					consume_skb(skb);  				} -			} else if (!(flags & MSG_PEEK)) { +			} else if (flags & MSG_PEEK) { +				skb = NULL; +			} else {  				skb_unlink(skb, &sk->sk_receive_queue);  				WRITE_ONCE(u->oob_skb, NULL);  				if (!WARN_ON_ONCE(skb_unref(skb))) @@ -2741,18 +2696,16 @@ redo:  		last = skb = skb_peek(&sk->sk_receive_queue);  		last_len = last ? last->len : 0; +again:  #if IS_ENABLED(CONFIG_AF_UNIX_OOB)  		if (skb) {  			skb = manage_oob(skb, sk, flags, copied); -			if (!skb) { +			if (!skb && copied) {  				unix_state_unlock(sk); -				if (copied) -					break; -				goto redo; +				break;  			}  		}  #endif -again:  		if (skb == NULL) {  			if (copied >= target)  				goto unlock;  | 
