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

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
...@@ -903,8 +903,10 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list, ...@@ -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, ...@@ -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);
......
...@@ -1166,7 +1166,7 @@ static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node) ...@@ -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, ...@@ -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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment