can: af_can: fix NULL pointer dereference in can_rx_register()
[platform/kernel/linux-rpi.git] / net / can / bcm.c
index 508f67d..aab3a18 100644 (file)
@@ -100,6 +100,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
 
 struct bcm_op {
        struct list_head list;
+       struct rcu_head rcu;
        int ifindex;
        canid_t can_id;
        u32 flags;
@@ -273,6 +274,7 @@ static void bcm_can_tx(struct bcm_op *op)
        struct sk_buff *skb;
        struct net_device *dev;
        struct canfd_frame *cf = op->frames + op->cfsiz * op->currframe;
+       int err;
 
        /* no target device? => exit */
        if (!op->ifindex)
@@ -297,11 +299,11 @@ static void bcm_can_tx(struct bcm_op *op)
        /* send with loopback */
        skb->dev = dev;
        can_skb_set_owner(skb, op->sk);
-       can_send(skb, 1);
+       err = can_send(skb, 1);
+       if (!err)
+               op->frames_abs++;
 
-       /* update statistics */
        op->currframe++;
-       op->frames_abs++;
 
        /* reached last frame? */
        if (op->currframe >= op->nframes)
@@ -718,10 +720,9 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
        return NULL;
 }
 
-static void bcm_remove_op(struct bcm_op *op)
+static void bcm_free_op_rcu(struct rcu_head *rcu_head)
 {
-       hrtimer_cancel(&op->timer);
-       hrtimer_cancel(&op->thrtimer);
+       struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
 
        if ((op->frames) && (op->frames != &op->sframe))
                kfree(op->frames);
@@ -732,6 +733,14 @@ static void bcm_remove_op(struct bcm_op *op)
        kfree(op);
 }
 
+static void bcm_remove_op(struct bcm_op *op)
+{
+       hrtimer_cancel(&op->timer);
+       hrtimer_cancel(&op->thrtimer);
+
+       call_rcu(&op->rcu, bcm_free_op_rcu);
+}
+
 static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
 {
        if (op->rx_reg_dev == dev) {
@@ -757,6 +766,9 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
                if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
                    (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
 
+                       /* disable automatic timer on frame reception */
+                       op->flags |= RX_NO_AUTOTIMER;
+
                        /*
                         * Don't care if we're bound or not (due to netdev
                         * problems) can_rx_unregister() is always a save
@@ -785,7 +797,6 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
                                                  bcm_rx_handler, op);
 
                        list_del(&op->list);
-                       synchronize_rcu();
                        bcm_remove_op(op);
                        return 1; /* done */
                }