diff options
Diffstat (limited to 'net/netlink/af_netlink.c')
| -rw-r--r-- | net/netlink/af_netlink.c | 86 | 
1 files changed, 47 insertions, 39 deletions
| diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 5acee49db90b..b9e0ee4e22f5 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -128,7 +128,6 @@ static const char *const nlk_cb_mutex_key_strings[MAX_LINKS + 1] = {  };  static int netlink_dump(struct sock *sk); -static void netlink_skb_destructor(struct sk_buff *skb);  /* nl_table locking explained:   * Lookup and traversal are protected with an RCU read-side lock. Insertion @@ -691,6 +690,9 @@ static void deferred_put_nlk_sk(struct rcu_head *head)  	struct netlink_sock *nlk = container_of(head, struct netlink_sock, rcu);  	struct sock *sk = &nlk->sk; +	kfree(nlk->groups); +	nlk->groups = NULL; +  	if (!refcount_dec_and_test(&sk->sk_refcnt))  		return; @@ -769,9 +771,6 @@ static int netlink_release(struct socket *sock)  		netlink_table_ungrab();  	} -	kfree(nlk->groups); -	nlk->groups = NULL; -  	local_bh_disable();  	sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1);  	local_bh_enable(); @@ -955,7 +954,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,  	struct net *net = sock_net(sk);  	struct netlink_sock *nlk = nlk_sk(sk);  	struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; -	int err; +	int err = 0;  	long unsigned int groups = nladdr->nl_groups;  	bool bound; @@ -983,6 +982,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,  			return -EINVAL;  	} +	netlink_lock_table();  	if (nlk->netlink_bind && groups) {  		int group; @@ -993,7 +993,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,  			if (!err)  				continue;  			netlink_undo_bind(group, groups, sk); -			return err; +			goto unlock;  		}  	} @@ -1006,12 +1006,13 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,  			netlink_autobind(sock);  		if (err) {  			netlink_undo_bind(nlk->ngroups, groups, sk); -			return err; +			goto unlock;  		}  	}  	if (!groups && (nlk->groups == NULL || !(u32)nlk->groups[0])) -		return 0; +		goto unlock; +	netlink_unlock_table();  	netlink_table_grab();  	netlink_update_subscriptions(sk, nlk->subscriptions + @@ -1022,6 +1023,10 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,  	netlink_table_ungrab();  	return 0; + +unlock: +	netlink_unlock_table(); +	return err;  }  static int netlink_connect(struct socket *sock, struct sockaddr *addr, @@ -1079,7 +1084,9 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr,  		nladdr->nl_groups = netlink_group_mask(nlk->dst_group);  	} else {  		nladdr->nl_pid = nlk->portid; +		netlink_lock_table();  		nladdr->nl_groups = nlk->groups ? nlk->groups[0] : 0; +		netlink_unlock_table();  	}  	return 0;  } @@ -2128,7 +2135,7 @@ static int netlink_dump(struct sock *sk)  	struct sk_buff *skb = NULL;  	struct nlmsghdr *nlh;  	struct module *module; -	int len, err = -ENOBUFS; +	int err = -ENOBUFS;  	int alloc_min_size;  	int alloc_size; @@ -2175,9 +2182,11 @@ static int netlink_dump(struct sock *sk)  	skb_reserve(skb, skb_tailroom(skb) - alloc_size);  	netlink_skb_set_owner_r(skb, sk); -	len = cb->dump(skb, cb); +	if (nlk->dump_done_errno > 0) +		nlk->dump_done_errno = cb->dump(skb, cb); -	if (len > 0) { +	if (nlk->dump_done_errno > 0 || +	    skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) {  		mutex_unlock(nlk->cb_mutex);  		if (sk_filter(sk, skb)) @@ -2187,13 +2196,15 @@ static int netlink_dump(struct sock *sk)  		return 0;  	} -	nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); -	if (!nlh) +	nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, +			       sizeof(nlk->dump_done_errno), NLM_F_MULTI); +	if (WARN_ON(!nlh))  		goto errout_skb;  	nl_dump_check_consistent(cb, nlh); -	memcpy(nlmsg_data(nlh), &len, sizeof(len)); +	memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, +	       sizeof(nlk->dump_done_errno));  	if (sk_filter(sk, skb))  		kfree_skb(skb); @@ -2258,14 +2269,19 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,  	cb->min_dump_alloc = control->min_dump_alloc;  	cb->skb = skb; +	if (cb->start) { +		ret = cb->start(cb); +		if (ret) +			goto error_unlock; +	} +  	nlk->cb_running = true; +	nlk->dump_done_errno = INT_MAX;  	mutex_unlock(nlk->cb_mutex); -	if (cb->start) -		cb->start(cb); -  	ret = netlink_dump(sk); +  	sock_put(sk);  	if (ret) @@ -2295,27 +2311,26 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,  	size_t tlvlen = 0;  	struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk);  	unsigned int flags = 0; +	bool nlk_has_extack = nlk->flags & NETLINK_F_EXT_ACK;  	/* Error messages get the original request appened, unless the user  	 * requests to cap the error message, and get extra error data if  	 * requested.  	 */ +	if (nlk_has_extack && extack && extack->_msg) +		tlvlen += nla_total_size(strlen(extack->_msg) + 1); +  	if (err) {  		if (!(nlk->flags & NETLINK_F_CAP_ACK))  			payload += nlmsg_len(nlh);  		else  			flags |= NLM_F_CAPPED; -		if (nlk->flags & NETLINK_F_EXT_ACK && extack) { -			if (extack->_msg) -				tlvlen += nla_total_size(strlen(extack->_msg) + 1); -			if (extack->bad_attr) -				tlvlen += nla_total_size(sizeof(u32)); -		} +		if (nlk_has_extack && extack && extack->bad_attr) +			tlvlen += nla_total_size(sizeof(u32));  	} else {  		flags |= NLM_F_CAPPED; -		if (nlk->flags & NETLINK_F_EXT_ACK && -		    extack && extack->cookie_len) +		if (nlk_has_extack && extack && extack->cookie_len)  			tlvlen += nla_total_size(extack->cookie_len);  	} @@ -2324,16 +2339,8 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,  	skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);  	if (!skb) { -		struct sock *sk; - -		sk = netlink_lookup(sock_net(in_skb->sk), -				    in_skb->sk->sk_protocol, -				    NETLINK_CB(in_skb).portid); -		if (sk) { -			sk->sk_err = ENOBUFS; -			sk->sk_error_report(sk); -			sock_put(sk); -		} +		NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; +		NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk);  		return;  	} @@ -2343,11 +2350,12 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,  	errmsg->error = err;  	memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh)); -	if (nlk->flags & NETLINK_F_EXT_ACK && extack) { +	if (nlk_has_extack && extack) { +		if (extack->_msg) { +			WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, +					       extack->_msg)); +		}  		if (err) { -			if (extack->_msg) -				WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, -						       extack->_msg));  			if (extack->bad_attr &&  			    !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||  				     (u8 *)extack->bad_attr >= in_skb->data + | 
