can: raw: fix receiver memory leak
[linux-2.6-microblaze.git] / net / can / raw.c
index 15c79b0..2302e48 100644 (file)
@@ -84,6 +84,7 @@ struct raw_sock {
        struct sock sk;
        int bound;
        int ifindex;
+       struct net_device *dev;
        struct list_head notifier;
        int loopback;
        int recv_own_msgs;
@@ -277,7 +278,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
        if (!net_eq(dev_net(dev), sock_net(sk)))
                return;
 
-       if (ro->ifindex != dev->ifindex)
+       if (ro->dev != dev)
                return;
 
        switch (msg) {
@@ -292,6 +293,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
 
                ro->ifindex = 0;
                ro->bound = 0;
+               ro->dev = NULL;
                ro->count = 0;
                release_sock(sk);
 
@@ -337,6 +339,7 @@ static int raw_init(struct sock *sk)
 
        ro->bound            = 0;
        ro->ifindex          = 0;
+       ro->dev              = NULL;
 
        /* set default filter to single entry dfilter */
        ro->dfilter.can_id   = 0;
@@ -385,19 +388,13 @@ static int raw_release(struct socket *sock)
 
        lock_sock(sk);
 
+       rtnl_lock();
        /* remove current filters & unregister */
        if (ro->bound) {
-               if (ro->ifindex) {
-                       struct net_device *dev;
-
-                       dev = dev_get_by_index(sock_net(sk), ro->ifindex);
-                       if (dev) {
-                               raw_disable_allfilters(dev_net(dev), dev, sk);
-                               dev_put(dev);
-                       }
-               } else {
+               if (ro->dev)
+                       raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
+               else
                        raw_disable_allfilters(sock_net(sk), NULL, sk);
-               }
        }
 
        if (ro->count > 1)
@@ -405,8 +402,10 @@ static int raw_release(struct socket *sock)
 
        ro->ifindex = 0;
        ro->bound = 0;
+       ro->dev = NULL;
        ro->count = 0;
        free_percpu(ro->uniq);
+       rtnl_unlock();
 
        sock_orphan(sk);
        sock->sk = NULL;
@@ -422,6 +421,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
        struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
        struct sock *sk = sock->sk;
        struct raw_sock *ro = raw_sk(sk);
+       struct net_device *dev = NULL;
        int ifindex;
        int err = 0;
        int notify_enetdown = 0;
@@ -431,14 +431,13 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
        if (addr->can_family != AF_CAN)
                return -EINVAL;
 
+       rtnl_lock();
        lock_sock(sk);
 
        if (ro->bound && addr->can_ifindex == ro->ifindex)
                goto out;
 
        if (addr->can_ifindex) {
-               struct net_device *dev;
-
                dev = dev_get_by_index(sock_net(sk), addr->can_ifindex);
                if (!dev) {
                        err = -ENODEV;
@@ -467,26 +466,20 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
        if (!err) {
                if (ro->bound) {
                        /* unregister old filters */
-                       if (ro->ifindex) {
-                               struct net_device *dev;
-
-                               dev = dev_get_by_index(sock_net(sk),
-                                                      ro->ifindex);
-                               if (dev) {
-                                       raw_disable_allfilters(dev_net(dev),
-                                                              dev, sk);
-                                       dev_put(dev);
-                               }
-                       } else {
+                       if (ro->dev)
+                               raw_disable_allfilters(dev_net(ro->dev),
+                                                      ro->dev, sk);
+                       else
                                raw_disable_allfilters(sock_net(sk), NULL, sk);
-                       }
                }
                ro->ifindex = ifindex;
                ro->bound = 1;
+               ro->dev = dev;
        }
 
  out:
        release_sock(sk);
+       rtnl_unlock();
 
        if (notify_enetdown) {
                sk->sk_err = ENETDOWN;
@@ -553,9 +546,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                rtnl_lock();
                lock_sock(sk);
 
-               if (ro->bound && ro->ifindex) {
-                       dev = dev_get_by_index(sock_net(sk), ro->ifindex);
-                       if (!dev) {
+               dev = ro->dev;
+               if (ro->bound && dev) {
+                       if (dev->reg_state != NETREG_REGISTERED) {
                                if (count > 1)
                                        kfree(filter);
                                err = -ENODEV;
@@ -596,7 +589,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                ro->count  = count;
 
  out_fil:
-               dev_put(dev);
                release_sock(sk);
                rtnl_unlock();
 
@@ -614,9 +606,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                rtnl_lock();
                lock_sock(sk);
 
-               if (ro->bound && ro->ifindex) {
-                       dev = dev_get_by_index(sock_net(sk), ro->ifindex);
-                       if (!dev) {
+               dev = ro->dev;
+               if (ro->bound && dev) {
+                       if (dev->reg_state != NETREG_REGISTERED) {
                                err = -ENODEV;
                                goto out_err;
                        }
@@ -640,7 +632,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                ro->err_mask = err_mask;
 
  out_err:
-               dev_put(dev);
                release_sock(sk);
                rtnl_unlock();