Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3dff0eb authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'linux-can-fixes-for-5.4-20191113' of...

Merge tag 'linux-can-fixes-for-5.4-20191113' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can



Marc Kleine-Budde says:

====================
pull-request: can 2019-11-13

this is a pull request of 9 patches for net/master, hopefully for the v5.4
release cycle.

All nine patches are by Oleksij Rempel and fix locking and use-after-free bugs
in the j1939 stack found by the syzkaller syzbot.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c3afb7ea 4a15d574
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -65,5 +65,6 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev,
			      void *data);

extern int can_send(struct sk_buff *skb, int loop);
void can_sock_destruct(struct sock *sk);

#endif /* !_CAN_CORE_H */
+2 −1
Original line number Diff line number Diff line
@@ -86,11 +86,12 @@ static atomic_t skbcounter = ATOMIC_INIT(0);

/* af_can socket functions */

static void can_sock_destruct(struct sock *sk)
void can_sock_destruct(struct sock *sk)
{
	skb_queue_purge(&sk->sk_receive_queue);
	skb_queue_purge(&sk->sk_error_queue);
}
EXPORT_SYMBOL(can_sock_destruct);

static const struct can_proto *can_get_proto(int protocol)
{
+9 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ static void j1939_can_recv(struct sk_buff *iskb, void *data)
	if (!skb)
		return;

	j1939_priv_get(priv);
	can_skb_set_owner(skb, iskb->sk);

	/* get a pointer to the header of the skb
@@ -104,6 +105,7 @@ static void j1939_can_recv(struct sk_buff *iskb, void *data)
	j1939_simple_recv(priv, skb);
	j1939_sk_recv(priv, skb);
 done:
	j1939_priv_put(priv);
	kfree_skb(skb);
}

@@ -150,6 +152,10 @@ static void __j1939_priv_release(struct kref *kref)

	netdev_dbg(priv->ndev, "%s: 0x%p\n", __func__, priv);

	WARN_ON_ONCE(!list_empty(&priv->active_session_list));
	WARN_ON_ONCE(!list_empty(&priv->ecus));
	WARN_ON_ONCE(!list_empty(&priv->j1939_socks));

	dev_put(ndev);
	kfree(priv);
}
@@ -207,6 +213,9 @@ static inline struct j1939_priv *j1939_ndev_to_priv(struct net_device *ndev)
{
	struct can_ml_priv *can_ml_priv = ndev->ml_priv;

	if (!can_ml_priv)
		return NULL;

	return can_ml_priv->j1939_priv;
}

+74 −20
Original line number Diff line number Diff line
@@ -78,7 +78,6 @@ static void j1939_jsk_add(struct j1939_priv *priv, struct j1939_sock *jsk)
{
	jsk->state |= J1939_SOCK_BOUND;
	j1939_priv_get(priv);
	jsk->priv = priv;

	spin_lock_bh(&priv->j1939_socks_lock);
	list_add_tail(&jsk->list, &priv->j1939_socks);
@@ -91,7 +90,6 @@ static void j1939_jsk_del(struct j1939_priv *priv, struct j1939_sock *jsk)
	list_del_init(&jsk->list);
	spin_unlock_bh(&priv->j1939_socks_lock);

	jsk->priv = NULL;
	j1939_priv_put(priv);
	jsk->state &= ~J1939_SOCK_BOUND;
}
@@ -349,6 +347,34 @@ void j1939_sk_recv(struct j1939_priv *priv, struct sk_buff *skb)
	spin_unlock_bh(&priv->j1939_socks_lock);
}

static void j1939_sk_sock_destruct(struct sock *sk)
{
	struct j1939_sock *jsk = j1939_sk(sk);

	/* This function will be call by the generic networking code, when then
	 * the socket is ultimately closed (sk->sk_destruct).
	 *
	 * The race between
	 * - processing a received CAN frame
	 *   (can_receive -> j1939_can_recv)
	 *   and accessing j1939_priv
	 * ... and ...
	 * - closing a socket
	 *   (j1939_can_rx_unregister -> can_rx_unregister)
	 *   and calling the final j1939_priv_put()
	 *
	 * is avoided by calling the final j1939_priv_put() from this
	 * RCU deferred cleanup call.
	 */
	if (jsk->priv) {
		j1939_priv_put(jsk->priv);
		jsk->priv = NULL;
	}

	/* call generic CAN sock destruct */
	can_sock_destruct(sk);
}

static int j1939_sk_init(struct sock *sk)
{
	struct j1939_sock *jsk = j1939_sk(sk);
@@ -371,6 +397,7 @@ static int j1939_sk_init(struct sock *sk)
	atomic_set(&jsk->skb_pending, 0);
	spin_lock_init(&jsk->sk_session_queue_lock);
	INIT_LIST_HEAD(&jsk->sk_session_queue);
	sk->sk_destruct = j1939_sk_sock_destruct;

	return 0;
}
@@ -443,6 +470,12 @@ static int j1939_sk_bind(struct socket *sock, struct sockaddr *uaddr, int len)
		}

		jsk->ifindex = addr->can_ifindex;

		/* the corresponding j1939_priv_put() is called via
		 * sk->sk_destruct, which points to j1939_sk_sock_destruct()
		 */
		j1939_priv_get(priv);
		jsk->priv = priv;
	}

	/* set default transmit pgn */
@@ -560,8 +593,8 @@ static int j1939_sk_release(struct socket *sock)
	if (!sk)
		return 0;

	jsk = j1939_sk(sk);
	lock_sock(sk);
	jsk = j1939_sk(sk);

	if (jsk->state & J1939_SOCK_BOUND) {
		struct j1939_priv *priv = jsk->priv;
@@ -1059,51 +1092,72 @@ static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
{
	struct sock *sk = sock->sk;
	struct j1939_sock *jsk = j1939_sk(sk);
	struct j1939_priv *priv = jsk->priv;
	struct j1939_priv *priv;
	int ifindex;
	int ret;

	lock_sock(sock->sk);
	/* various socket state tests */
	if (!(jsk->state & J1939_SOCK_BOUND))
		return -EBADFD;
	if (!(jsk->state & J1939_SOCK_BOUND)) {
		ret = -EBADFD;
		goto sendmsg_done;
	}

	priv = jsk->priv;
	ifindex = jsk->ifindex;

	if (!jsk->addr.src_name && jsk->addr.sa == J1939_NO_ADDR)
	if (!jsk->addr.src_name && jsk->addr.sa == J1939_NO_ADDR) {
		/* no source address assigned yet */
		return -EBADFD;
		ret = -EBADFD;
		goto sendmsg_done;
	}

	/* deal with provided destination address info */
	if (msg->msg_name) {
		struct sockaddr_can *addr = msg->msg_name;

		if (msg->msg_namelen < J1939_MIN_NAMELEN)
			return -EINVAL;
		if (msg->msg_namelen < J1939_MIN_NAMELEN) {
			ret = -EINVAL;
			goto sendmsg_done;
		}

		if (addr->can_family != AF_CAN)
			return -EINVAL;
		if (addr->can_family != AF_CAN) {
			ret = -EINVAL;
			goto sendmsg_done;
		}

		if (addr->can_ifindex && addr->can_ifindex != ifindex)
			return -EBADFD;
		if (addr->can_ifindex && addr->can_ifindex != ifindex) {
			ret = -EBADFD;
			goto sendmsg_done;
		}

		if (j1939_pgn_is_valid(addr->can_addr.j1939.pgn) &&
		    !j1939_pgn_is_clean_pdu(addr->can_addr.j1939.pgn))
			return -EINVAL;
		    !j1939_pgn_is_clean_pdu(addr->can_addr.j1939.pgn)) {
			ret = -EINVAL;
			goto sendmsg_done;
		}

		if (!addr->can_addr.j1939.name &&
		    addr->can_addr.j1939.addr == J1939_NO_ADDR &&
		    !sock_flag(sk, SOCK_BROADCAST))
		    !sock_flag(sk, SOCK_BROADCAST)) {
			/* broadcast, but SO_BROADCAST not set */
			return -EACCES;
			ret = -EACCES;
			goto sendmsg_done;
		}
	} else {
		if (!jsk->addr.dst_name && jsk->addr.da == J1939_NO_ADDR &&
		    !sock_flag(sk, SOCK_BROADCAST))
		    !sock_flag(sk, SOCK_BROADCAST)) {
			/* broadcast, but SO_BROADCAST not set */
			return -EACCES;
			ret = -EACCES;
			goto sendmsg_done;
		}
	}

	ret = j1939_sk_send_loop(priv, sk, msg, size);

sendmsg_done:
	release_sock(sock->sk);

	return ret;
}

+27 −9
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ static void __j1939_session_drop(struct j1939_session *session)
		return;

	j1939_sock_pending_del(session->sk);
	sock_put(session->sk);
}

static void j1939_session_destroy(struct j1939_session *session)
@@ -266,6 +267,9 @@ static void j1939_session_destroy(struct j1939_session *session)

	netdev_dbg(session->priv->ndev, "%s: 0x%p\n", __func__, session);

	WARN_ON_ONCE(!list_empty(&session->sk_session_queue_entry));
	WARN_ON_ONCE(!list_empty(&session->active_session_list_entry));

	skb_queue_purge(&session->skb_queue);
	__j1939_session_drop(session);
	j1939_priv_put(session->priv);
@@ -1042,12 +1046,13 @@ j1939_session_deactivate_activate_next(struct j1939_session *session)
		j1939_sk_queue_activate_next(session);
}

static void j1939_session_cancel(struct j1939_session *session,
static void __j1939_session_cancel(struct j1939_session *session,
				 enum j1939_xtp_abort err)
{
	struct j1939_priv *priv = session->priv;

	WARN_ON_ONCE(!err);
	lockdep_assert_held(&session->priv->active_session_list_lock);

	session->err = j1939_xtp_abort_to_errno(priv, err);
	/* do not send aborts on incoming broadcasts */
@@ -1062,6 +1067,20 @@ static void j1939_session_cancel(struct j1939_session *session,
		j1939_sk_send_loop_abort(session->sk, session->err);
}

static void j1939_session_cancel(struct j1939_session *session,
				 enum j1939_xtp_abort err)
{
	j1939_session_list_lock(session->priv);

	if (session->state >= J1939_SESSION_ACTIVE &&
	    session->state < J1939_SESSION_WAITING_ABORT) {
		j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
		__j1939_session_cancel(session, err);
	}

	j1939_session_list_unlock(session->priv);
}

static enum hrtimer_restart j1939_tp_txtimer(struct hrtimer *hrtimer)
{
	struct j1939_session *session =
@@ -1108,8 +1127,6 @@ static enum hrtimer_restart j1939_tp_txtimer(struct hrtimer *hrtimer)
		netdev_alert(priv->ndev, "%s: 0x%p: tx aborted with unknown reason: %i\n",
			     __func__, session, ret);
		if (session->skcb.addr.type != J1939_SIMPLE) {
			j1939_tp_set_rxtimeout(session,
					       J1939_XTP_ABORT_TIMEOUT_MS);
			j1939_session_cancel(session, J1939_XTP_ABORT_OTHER);
		} else {
			session->err = ret;
@@ -1169,7 +1186,7 @@ static enum hrtimer_restart j1939_tp_rxtimer(struct hrtimer *hrtimer)
			hrtimer_start(&session->rxtimer,
				      ms_to_ktime(J1939_XTP_ABORT_TIMEOUT_MS),
				      HRTIMER_MODE_REL_SOFT);
			j1939_session_cancel(session, J1939_XTP_ABORT_TIMEOUT);
			__j1939_session_cancel(session, J1939_XTP_ABORT_TIMEOUT);
		}
		j1939_session_list_unlock(session->priv);
	}
@@ -1375,7 +1392,6 @@ j1939_xtp_rx_cts_one(struct j1939_session *session, struct sk_buff *skb)

 out_session_cancel:
	j1939_session_timers_cancel(session);
	j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
	j1939_session_cancel(session, err);
}

@@ -1572,7 +1588,6 @@ static int j1939_xtp_rx_rts_session_active(struct j1939_session *session,

		/* RTS on active session */
		j1939_session_timers_cancel(session);
		j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
		j1939_session_cancel(session, J1939_XTP_ABORT_BUSY);
	}

@@ -1583,7 +1598,6 @@ static int j1939_xtp_rx_rts_session_active(struct j1939_session *session,
			     session->last_cmd);

		j1939_session_timers_cancel(session);
		j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
		j1939_session_cancel(session, J1939_XTP_ABORT_BUSY);

		return -EBUSY;
@@ -1785,7 +1799,6 @@ static void j1939_xtp_rx_dat_one(struct j1939_session *session,

 out_session_cancel:
	j1939_session_timers_cancel(session);
	j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
	j1939_session_cancel(session, J1939_XTP_ABORT_FAULT);
	j1939_session_put(session);
}
@@ -1866,6 +1879,7 @@ struct j1939_session *j1939_tp_send(struct j1939_priv *priv,
		return ERR_PTR(-ENOMEM);

	/* skb is recounted in j1939_session_new() */
	sock_hold(skb->sk);
	session->sk = skb->sk;
	session->transmission = true;
	session->pkt.total = (size + 6) / 7;
@@ -2028,7 +2042,11 @@ int j1939_cancel_active_session(struct j1939_priv *priv, struct sock *sk)
				 &priv->active_session_list,
				 active_session_list_entry) {
		if (!sk || sk == session->sk) {
			j1939_session_timers_cancel(session);
			if (hrtimer_try_to_cancel(&session->txtimer) == 1)
				j1939_session_put(session);
			if (hrtimer_try_to_cancel(&session->rxtimer) == 1)
				j1939_session_put(session);

			session->err = ESHUTDOWN;
			j1939_session_deactivate_locked(session);
		}