diff --git a/net/can/isotp.c b/net/can/isotp.c index e4957855829e..28e9d22772fa 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1550,7 +1550,6 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l struct sock *sk = sock->sk; struct isotp_sock *so = isotp_sk(sk); struct net *net = sock_net(sk); - int ifindex; struct net_device *dev; canid_t tx_id = addr->can_addr.tp.tx_id; canid_t rx_id = addr->can_addr.tp.rx_id; @@ -1627,26 +1626,36 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l err = -EINVAL; goto out_put_dev; } - if (!(dev->flags & IFF_UP)) - notify_enetdown = 1; - ifindex = dev->ifindex; - - if (isotp_register_rxid(so)) - can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id), - isotp_rcv, sk, "isotp", sk); + /* switch to new settings */ + so->ifindex = dev->ifindex; + so->rxid = rx_id; + so->txid = tx_id; /* no consecutive frame echo skb in flight */ WRITE_ONCE(so->cfecho, 0); - /* register for echo skb's */ - can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), - isotp_rcv_echo, sk, "isotpe", sk); + /* register for echo skb's first (no echo traffic at this point) */ + err = can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), + isotp_rcv_echo, sk, "isotpe", sk); + if (err) + goto out_put_dev; + + if (isotp_register_rxid(so)) { + err = can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id), + isotp_rcv, sk, "isotp", sk); + if (err) { + /* unregister echo skb subscription */ + can_rx_unregister(net, dev, tx_id, SINGLE_MASK(tx_id), + isotp_rcv_echo, sk); + goto out_put_dev; + } + } + + /* report a down device only for a successful bind() */ + if (!(dev->flags & IFF_UP)) + notify_enetdown = 1; - /* switch to new settings */ - so->ifindex = ifindex; - so->rxid = rx_id; - so->txid = tx_id; so->bound = 1; /* bind() ok -> hold a reference for so->dev so that isotp_release() @@ -1657,6 +1666,9 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL); out_put_dev: + if (err) + so->ifindex = 0; /* reset on failed bind() */ + /* remove potential reference from dev_get_by_index() */ dev_put(dev); out: