]> git.proxmox.com Git - mirror_ubuntu-kernels.git/blobdiff - net/can/raw.c
can: raw: add missing refcount for memory leak fix
[mirror_ubuntu-kernels.git] / net / can / raw.c
index e10f59375659f8342ff7e2d6da834920fd526184..d50c3f3d892f9382a547b7e8dddcab327623ff88 100644 (file)
@@ -85,6 +85,7 @@ struct raw_sock {
        int bound;
        int ifindex;
        struct net_device *dev;
+       netdevice_tracker dev_tracker;
        struct list_head notifier;
        int loopback;
        int recv_own_msgs;
@@ -285,8 +286,10 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
        case NETDEV_UNREGISTER:
                lock_sock(sk);
                /* remove current filters & unregister */
-               if (ro->bound)
+               if (ro->bound) {
                        raw_disable_allfilters(dev_net(dev), dev, sk);
+                       netdev_put(dev, &ro->dev_tracker);
+               }
 
                if (ro->count > 1)
                        kfree(ro->filter);
@@ -391,10 +394,12 @@ static int raw_release(struct socket *sock)
 
        /* remove current filters & unregister */
        if (ro->bound) {
-               if (ro->dev)
+               if (ro->dev) {
                        raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
-               else
+                       netdev_put(ro->dev, &ro->dev_tracker);
+               } else {
                        raw_disable_allfilters(sock_net(sk), NULL, sk);
+               }
        }
 
        if (ro->count > 1)
@@ -445,10 +450,10 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
                        goto out;
                }
                if (dev->type != ARPHRD_CAN) {
-                       dev_put(dev);
                        err = -ENODEV;
-                       goto out;
+                       goto out_put_dev;
                }
+
                if (!(dev->flags & IFF_UP))
                        notify_enetdown = 1;
 
@@ -456,7 +461,9 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
 
                /* filters set by default/setsockopt */
                err = raw_enable_allfilters(sock_net(sk), dev, sk);
-               dev_put(dev);
+               if (err)
+                       goto out_put_dev;
+
        } else {
                ifindex = 0;
 
@@ -467,18 +474,28 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
        if (!err) {
                if (ro->bound) {
                        /* unregister old filters */
-                       if (ro->dev)
+                       if (ro->dev) {
                                raw_disable_allfilters(dev_net(ro->dev),
                                                       ro->dev, sk);
-                       else
+                               /* drop reference to old ro->dev */
+                               netdev_put(ro->dev, &ro->dev_tracker);
+                       } else {
                                raw_disable_allfilters(sock_net(sk), NULL, sk);
+                       }
                }
                ro->ifindex = ifindex;
                ro->bound = 1;
+               /* bind() ok -> hold a reference for new ro->dev */
                ro->dev = dev;
+               if (ro->dev)
+                       netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
        }
 
- out:
+out_put_dev:
+       /* remove potential reference from dev_get_by_index() */
+       if (dev)
+               dev_put(dev);
+out:
        release_sock(sk);
        rtnl_unlock();