tipc: eliminate buffer leak in bearer layer
authorJon Paul Maloy <jon.maloy@ericsson.com>
Thu, 7 Apr 2016 14:09:13 +0000 (10:09 -0400)
committerDavid S. Miller <davem@davemloft.net>
Thu, 7 Apr 2016 21:00:13 +0000 (17:00 -0400)
When enabling a bearer we create a 'neigbor discoverer' instance by
calling the function tipc_disc_create() before the bearer is actually
registered in the list of enabled bearers. Because of this, the very
first discovery broadcast message, created by the mentioned function,
is lost, since it cannot find any valid bearer to use. Furthermore,
the used send function, tipc_bearer_xmit_skb() does not free the given
buffer when it cannot find a  bearer, resulting in the leak of exactly
one send buffer each time a bearer is enabled.

This commit fixes this problem by introducing two changes:

1) Instead of attemting to send the discovery message directly, we let
   tipc_disc_create() return the discovery buffer to the calling
   function, tipc_enable_bearer(), so that the latter can send it
   when the enabling sequence is finished.

2) In tipc_bearer_xmit_skb(), as well as in the two other transmit
   functions at the bearer layer, we now free the indicated buffer or
   buffer chain when a valid bearer cannot be found.

Acked-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/tipc/bearer.c
net/tipc/discover.c
net/tipc/discover.h

index 27a5406213c650239de192c423f28a88912a42d2..20566e9a13690afa8478e55eb9cd0e89875999bf 100644 (file)
@@ -205,6 +205,7 @@ static int tipc_enable_bearer(struct net *net, const char *name,
        struct tipc_bearer *b;
        struct tipc_media *m;
        struct tipc_bearer_names b_names;
+       struct sk_buff *skb;
        char addr_string[16];
        u32 bearer_id;
        u32 with_this_prio;
@@ -301,7 +302,7 @@ restart:
        b->net_plane = bearer_id + 'A';
        b->priority = priority;
 
-       res = tipc_disc_create(net, b, &b->bcast_addr);
+       res = tipc_disc_create(net, b, &b->bcast_addr, &skb);
        if (res) {
                bearer_disable(net, b);
                pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
@@ -310,7 +311,8 @@ restart:
        }
 
        rcu_assign_pointer(tn->bearer_list[bearer_id], b);
-
+       if (skb)
+               tipc_bearer_xmit_skb(net, bearer_id, skb, &b->bcast_addr);
        pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
                name,
                tipc_addr_string_fill(addr_string, disc_domain), priority);
@@ -450,6 +452,8 @@ void tipc_bearer_xmit_skb(struct net *net, u32 bearer_id,
        b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
        if (likely(b))
                b->media->send_msg(net, skb, b, dest);
+       else
+               kfree_skb(skb);
        rcu_read_unlock();
 }
 
@@ -468,11 +472,11 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id,
 
        rcu_read_lock();
        b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (likely(b)) {
-               skb_queue_walk_safe(xmitq, skb, tmp) {
-                       __skb_dequeue(xmitq);
-                       b->media->send_msg(net, skb, b, dst);
-               }
+       if (unlikely(!b))
+               __skb_queue_purge(xmitq);
+       skb_queue_walk_safe(xmitq, skb, tmp) {
+               __skb_dequeue(xmitq);
+               b->media->send_msg(net, skb, b, dst);
        }
        rcu_read_unlock();
 }
@@ -490,14 +494,14 @@ void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
 
        rcu_read_lock();
        b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (likely(b)) {
-               skb_queue_walk_safe(xmitq, skb, tmp) {
-                       hdr = buf_msg(skb);
-                       msg_set_non_seq(hdr, 1);
-                       msg_set_mc_netid(hdr, net_id);
-                       __skb_dequeue(xmitq);
-                       b->media->send_msg(net, skb, b, &b->bcast_addr);
-               }
+       if (unlikely(!b))
+               __skb_queue_purge(xmitq);
+       skb_queue_walk_safe(xmitq, skb, tmp) {
+               hdr = buf_msg(skb);
+               msg_set_non_seq(hdr, 1);
+               msg_set_mc_netid(hdr, net_id);
+               __skb_dequeue(xmitq);
+               b->media->send_msg(net, skb, b, &b->bcast_addr);
        }
        rcu_read_unlock();
 }
@@ -513,24 +517,21 @@ void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
  * ignores packets sent using interface multicast, and traffic sent to other
  * nodes (which can happen if interface is running in promiscuous mode).
  */
-static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
+static int tipc_l2_rcv_msg(struct sk_buff *skb, struct net_device *dev,
                           struct packet_type *pt, struct net_device *orig_dev)
 {
        struct tipc_bearer *b;
 
        rcu_read_lock();
        b = rcu_dereference_rtnl(dev->tipc_ptr);
-       if (likely(b)) {
-               if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
-                       buf->next = NULL;
-                       tipc_rcv(dev_net(dev), buf, b);
-                       rcu_read_unlock();
-                       return NET_RX_SUCCESS;
-               }
+       if (likely(b && (skb->pkt_type <= PACKET_BROADCAST))) {
+               skb->next = NULL;
+               tipc_rcv(dev_net(dev), skb, b);
+               rcu_read_unlock();
+               return NET_RX_SUCCESS;
        }
        rcu_read_unlock();
-
-       kfree_skb(buf);
+       kfree_skb(skb);
        return NET_RX_DROP;
 }
 
index f1e738e80535af42704f6956a251ef3fee1c1c24..ad9d477cc2428a5835fcff849815050bda571299 100644 (file)
@@ -268,10 +268,9 @@ exit:
  * Returns 0 if successful, otherwise -errno.
  */
 int tipc_disc_create(struct net *net, struct tipc_bearer *b,
-                    struct tipc_media_addr *dest)
+                    struct tipc_media_addr *dest, struct sk_buff **skb)
 {
        struct tipc_link_req *req;
-       struct sk_buff *skb;
 
        req = kmalloc(sizeof(*req), GFP_ATOMIC);
        if (!req)
@@ -293,9 +292,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,
        setup_timer(&req->timer, disc_timeout, (unsigned long)req);
        mod_timer(&req->timer, jiffies + req->timer_intv);
        b->link_req = req;
-       skb = skb_clone(req->buf, GFP_ATOMIC);
-       if (skb)
-               tipc_bearer_xmit_skb(net, req->bearer_id, skb, &req->dest);
+       *skb = skb_clone(req->buf, GFP_ATOMIC);
        return 0;
 }
 
index c9b12770c5ed9a4d4e38f17e5434c0bb86eecac5..b80a335389c0e918ed98d559ae239b3f96d8e8c4 100644 (file)
@@ -40,7 +40,7 @@
 struct tipc_link_req;
 
 int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
-                    struct tipc_media_addr *dest);
+                    struct tipc_media_addr *dest, struct sk_buff **skb);
 void tipc_disc_delete(struct tipc_link_req *req);
 void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr);
 void tipc_disc_add_dest(struct tipc_link_req *req);