Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4952cd3e authored by Richard Alpe's avatar Richard Alpe Committed by David S. Miller
Browse files

tipc: refactor node xmit and fix memory leaks



Refactor tipc_node_xmit() to fail fast and fail early. Fix several
potential memory leaks in unexpected error paths.

Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Reviewed-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarRichard Alpe <richard.alpe@ericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 37ace20a
Loading
Loading
Loading
Loading
+6 −2
Original line number Original line Diff line number Diff line
@@ -903,8 +903,10 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
		if (unlikely(l->backlog[i].len >= l->backlog[i].limit))
		if (unlikely(l->backlog[i].len >= l->backlog[i].limit))
			return link_schedule_user(l, list);
			return link_schedule_user(l, list);
	}
	}
	if (unlikely(msg_size(hdr) > mtu))
	if (unlikely(msg_size(hdr) > mtu)) {
		skb_queue_purge(list);
		return -EMSGSIZE;
		return -EMSGSIZE;
	}


	/* Prepare each packet for sending, and add to relevant queue: */
	/* Prepare each packet for sending, and add to relevant queue: */
	while (skb_queue_len(list)) {
	while (skb_queue_len(list)) {
@@ -916,8 +918,10 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,


		if (likely(skb_queue_len(transmq) < maxwin)) {
		if (likely(skb_queue_len(transmq) < maxwin)) {
			_skb = skb_clone(skb, GFP_ATOMIC);
			_skb = skb_clone(skb, GFP_ATOMIC);
			if (!_skb)
			if (!_skb) {
				skb_queue_purge(list);
				return -ENOBUFS;
				return -ENOBUFS;
			}
			__skb_dequeue(list);
			__skb_dequeue(list);
			__skb_queue_tail(transmq, skb);
			__skb_queue_tail(transmq, skb);
			__skb_queue_tail(xmitq, _skb);
			__skb_queue_tail(xmitq, _skb);
+32 −22
Original line number Original line Diff line number Diff line
@@ -1166,7 +1166,7 @@ static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
 * @dnode: address of destination node
 * @dnode: address of destination node
 * @selector: a number used for deterministic link selection
 * @selector: a number used for deterministic link selection
 * Consumes the buffer chain, except when returning -ELINKCONG
 * Consumes the buffer chain, except when returning -ELINKCONG
 * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE
 * Returns 0 if success, otherwise: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE,-ENOBUF
 */
 */
int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
		   u32 dnode, int selector)
		   u32 dnode, int selector)
@@ -1174,33 +1174,43 @@ int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
	struct tipc_link_entry *le = NULL;
	struct tipc_link_entry *le = NULL;
	struct tipc_node *n;
	struct tipc_node *n;
	struct sk_buff_head xmitq;
	struct sk_buff_head xmitq;
	int bearer_id = -1;
	int bearer_id;
	int rc = -EHOSTUNREACH;
	int rc;

	if (in_own_node(net, dnode)) {
		tipc_sk_rcv(net, list);
		return 0;
	}


	__skb_queue_head_init(&xmitq);
	n = tipc_node_find(net, dnode);
	n = tipc_node_find(net, dnode);
	if (likely(n)) {
	if (unlikely(!n)) {
		skb_queue_purge(list);
		return -EHOSTUNREACH;
	}

	tipc_node_read_lock(n);
	tipc_node_read_lock(n);
	bearer_id = n->active_links[selector & 1];
	bearer_id = n->active_links[selector & 1];
		if (bearer_id >= 0) {
	if (unlikely(bearer_id == INVALID_BEARER_ID)) {
		tipc_node_read_unlock(n);
		tipc_node_put(n);
		skb_queue_purge(list);
		return -EHOSTUNREACH;
	}

	__skb_queue_head_init(&xmitq);
	le = &n->links[bearer_id];
	le = &n->links[bearer_id];
	spin_lock_bh(&le->lock);
	spin_lock_bh(&le->lock);
	rc = tipc_link_xmit(le->link, list, &xmitq);
	rc = tipc_link_xmit(le->link, list, &xmitq);
	spin_unlock_bh(&le->lock);
	spin_unlock_bh(&le->lock);
		}
	tipc_node_read_unlock(n);
	tipc_node_read_unlock(n);
		if (likely(!rc))

	if (likely(rc == 0))
		tipc_bearer_xmit(net, bearer_id, &xmitq, &le->maddr);
		tipc_bearer_xmit(net, bearer_id, &xmitq, &le->maddr);
	else if (rc == -ENOBUFS)
	else if (rc == -ENOBUFS)
		tipc_node_link_down(n, bearer_id, false);
		tipc_node_link_down(n, bearer_id, false);

	tipc_node_put(n);
	tipc_node_put(n);
		return rc;
	}


	if (likely(in_own_node(net, dnode))) {
		tipc_sk_rcv(net, list);
		return 0;
	}
	return rc;
	return rc;
}
}