linux-can-fixes-for-5.4-20191113

-----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEmvEkXzgOfc881GuFWsYho5HknSAFAl3L0SETHG1rbEBwZW5n
 dXRyb25peC5kZQAKCRBaxiGjkeSdIBReCACawwJlue5MIbawq+JD8wQ+RE117kJc
 x6z2QFEynNchywthMRfaCSj+QP0n5C4JY6BHtn7LMT2JOrPVzQAwz2cZ4eUPinUA
 eSd0/0ZqoBH+IZW4c3sYNkrGSi47qIytGi3jgbfNbZj4mIYBCikLDhLhWVk9+rTp
 HZiqS2vWUaZ42bKvbHE2CHTV/oPdz/3wJIQmctkyoFvxKcJM1WyI6y7QSJ/VJWv1
 bpX4dymK4qgnGHGyiOcoeRmaDFOg53gDTRfIQZoc++5/dlczov7eA48XedrzQUV7
 qGR4j6BjZrxloQsS3Y4ynTRC+x5aE+jajEaf3QEKrO7VCRS6jdV1bX2E
 =13Km
 -----END PGP SIGNATURE-----

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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-11-13 11:47:02 -08:00
commit b3dff0eb4b
5 changed files with 113 additions and 30 deletions

View File

@ -65,5 +65,6 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev,
void *data); void *data);
extern int can_send(struct sk_buff *skb, int loop); extern int can_send(struct sk_buff *skb, int loop);
void can_sock_destruct(struct sock *sk);
#endif /* !_CAN_CORE_H */ #endif /* !_CAN_CORE_H */

View File

@ -86,11 +86,12 @@ static atomic_t skbcounter = ATOMIC_INIT(0);
/* af_can socket functions */ /* 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_receive_queue);
skb_queue_purge(&sk->sk_error_queue); skb_queue_purge(&sk->sk_error_queue);
} }
EXPORT_SYMBOL(can_sock_destruct);
static const struct can_proto *can_get_proto(int protocol) static const struct can_proto *can_get_proto(int protocol)
{ {

View File

@ -51,6 +51,7 @@ static void j1939_can_recv(struct sk_buff *iskb, void *data)
if (!skb) if (!skb)
return; return;
j1939_priv_get(priv);
can_skb_set_owner(skb, iskb->sk); can_skb_set_owner(skb, iskb->sk);
/* get a pointer to the header of the skb /* 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_simple_recv(priv, skb);
j1939_sk_recv(priv, skb); j1939_sk_recv(priv, skb);
done: done:
j1939_priv_put(priv);
kfree_skb(skb); 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); 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); dev_put(ndev);
kfree(priv); 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; struct can_ml_priv *can_ml_priv = ndev->ml_priv;
if (!can_ml_priv)
return NULL;
return can_ml_priv->j1939_priv; return can_ml_priv->j1939_priv;
} }

View File

@ -78,7 +78,6 @@ static void j1939_jsk_add(struct j1939_priv *priv, struct j1939_sock *jsk)
{ {
jsk->state |= J1939_SOCK_BOUND; jsk->state |= J1939_SOCK_BOUND;
j1939_priv_get(priv); j1939_priv_get(priv);
jsk->priv = priv;
spin_lock_bh(&priv->j1939_socks_lock); spin_lock_bh(&priv->j1939_socks_lock);
list_add_tail(&jsk->list, &priv->j1939_socks); 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); list_del_init(&jsk->list);
spin_unlock_bh(&priv->j1939_socks_lock); spin_unlock_bh(&priv->j1939_socks_lock);
jsk->priv = NULL;
j1939_priv_put(priv); j1939_priv_put(priv);
jsk->state &= ~J1939_SOCK_BOUND; 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); 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) static int j1939_sk_init(struct sock *sk)
{ {
struct j1939_sock *jsk = j1939_sk(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); atomic_set(&jsk->skb_pending, 0);
spin_lock_init(&jsk->sk_session_queue_lock); spin_lock_init(&jsk->sk_session_queue_lock);
INIT_LIST_HEAD(&jsk->sk_session_queue); INIT_LIST_HEAD(&jsk->sk_session_queue);
sk->sk_destruct = j1939_sk_sock_destruct;
return 0; return 0;
} }
@ -443,6 +470,12 @@ static int j1939_sk_bind(struct socket *sock, struct sockaddr *uaddr, int len)
} }
jsk->ifindex = addr->can_ifindex; 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 */ /* set default transmit pgn */
@ -560,8 +593,8 @@ static int j1939_sk_release(struct socket *sock)
if (!sk) if (!sk)
return 0; return 0;
jsk = j1939_sk(sk);
lock_sock(sk); lock_sock(sk);
jsk = j1939_sk(sk);
if (jsk->state & J1939_SOCK_BOUND) { if (jsk->state & J1939_SOCK_BOUND) {
struct j1939_priv *priv = jsk->priv; 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 sock *sk = sock->sk;
struct j1939_sock *jsk = j1939_sk(sk); struct j1939_sock *jsk = j1939_sk(sk);
struct j1939_priv *priv = jsk->priv; struct j1939_priv *priv;
int ifindex; int ifindex;
int ret; int ret;
lock_sock(sock->sk);
/* various socket state tests */ /* various socket state tests */
if (!(jsk->state & J1939_SOCK_BOUND)) if (!(jsk->state & J1939_SOCK_BOUND)) {
return -EBADFD; ret = -EBADFD;
goto sendmsg_done;
}
priv = jsk->priv;
ifindex = jsk->ifindex; 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 */ /* no source address assigned yet */
return -EBADFD; ret = -EBADFD;
goto sendmsg_done;
}
/* deal with provided destination address info */ /* deal with provided destination address info */
if (msg->msg_name) { if (msg->msg_name) {
struct sockaddr_can *addr = msg->msg_name; struct sockaddr_can *addr = msg->msg_name;
if (msg->msg_namelen < J1939_MIN_NAMELEN) if (msg->msg_namelen < J1939_MIN_NAMELEN) {
return -EINVAL; ret = -EINVAL;
goto sendmsg_done;
}
if (addr->can_family != AF_CAN) if (addr->can_family != AF_CAN) {
return -EINVAL; ret = -EINVAL;
goto sendmsg_done;
}
if (addr->can_ifindex && addr->can_ifindex != ifindex) if (addr->can_ifindex && addr->can_ifindex != ifindex) {
return -EBADFD; ret = -EBADFD;
goto sendmsg_done;
}
if (j1939_pgn_is_valid(addr->can_addr.j1939.pgn) && if (j1939_pgn_is_valid(addr->can_addr.j1939.pgn) &&
!j1939_pgn_is_clean_pdu(addr->can_addr.j1939.pgn)) !j1939_pgn_is_clean_pdu(addr->can_addr.j1939.pgn)) {
return -EINVAL; ret = -EINVAL;
goto sendmsg_done;
}
if (!addr->can_addr.j1939.name && if (!addr->can_addr.j1939.name &&
addr->can_addr.j1939.addr == J1939_NO_ADDR && addr->can_addr.j1939.addr == J1939_NO_ADDR &&
!sock_flag(sk, SOCK_BROADCAST)) !sock_flag(sk, SOCK_BROADCAST)) {
/* broadcast, but SO_BROADCAST not set */ /* broadcast, but SO_BROADCAST not set */
return -EACCES; ret = -EACCES;
goto sendmsg_done;
}
} else { } else {
if (!jsk->addr.dst_name && jsk->addr.da == J1939_NO_ADDR && 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 */ /* broadcast, but SO_BROADCAST not set */
return -EACCES; ret = -EACCES;
goto sendmsg_done;
}
} }
ret = j1939_sk_send_loop(priv, sk, msg, size); ret = j1939_sk_send_loop(priv, sk, msg, size);
sendmsg_done:
release_sock(sock->sk);
return ret; return ret;
} }

View File

@ -255,6 +255,7 @@ static void __j1939_session_drop(struct j1939_session *session)
return; return;
j1939_sock_pending_del(session->sk); j1939_sock_pending_del(session->sk);
sock_put(session->sk);
} }
static void j1939_session_destroy(struct j1939_session *session) 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); 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); skb_queue_purge(&session->skb_queue);
__j1939_session_drop(session); __j1939_session_drop(session);
j1939_priv_put(session->priv); j1939_priv_put(session->priv);
@ -1042,12 +1046,13 @@ j1939_session_deactivate_activate_next(struct j1939_session *session)
j1939_sk_queue_activate_next(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) enum j1939_xtp_abort err)
{ {
struct j1939_priv *priv = session->priv; struct j1939_priv *priv = session->priv;
WARN_ON_ONCE(!err); WARN_ON_ONCE(!err);
lockdep_assert_held(&session->priv->active_session_list_lock);
session->err = j1939_xtp_abort_to_errno(priv, err); session->err = j1939_xtp_abort_to_errno(priv, err);
/* do not send aborts on incoming broadcasts */ /* 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); 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) static enum hrtimer_restart j1939_tp_txtimer(struct hrtimer *hrtimer)
{ {
struct j1939_session *session = 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", netdev_alert(priv->ndev, "%s: 0x%p: tx aborted with unknown reason: %i\n",
__func__, session, ret); __func__, session, ret);
if (session->skcb.addr.type != J1939_SIMPLE) { 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); j1939_session_cancel(session, J1939_XTP_ABORT_OTHER);
} else { } else {
session->err = ret; session->err = ret;
@ -1169,7 +1186,7 @@ static enum hrtimer_restart j1939_tp_rxtimer(struct hrtimer *hrtimer)
hrtimer_start(&session->rxtimer, hrtimer_start(&session->rxtimer,
ms_to_ktime(J1939_XTP_ABORT_TIMEOUT_MS), ms_to_ktime(J1939_XTP_ABORT_TIMEOUT_MS),
HRTIMER_MODE_REL_SOFT); 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); 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: out_session_cancel:
j1939_session_timers_cancel(session); j1939_session_timers_cancel(session);
j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
j1939_session_cancel(session, err); 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 */ /* RTS on active session */
j1939_session_timers_cancel(session); j1939_session_timers_cancel(session);
j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
j1939_session_cancel(session, J1939_XTP_ABORT_BUSY); 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); session->last_cmd);
j1939_session_timers_cancel(session); j1939_session_timers_cancel(session);
j1939_tp_set_rxtimeout(session, J1939_XTP_ABORT_TIMEOUT_MS);
j1939_session_cancel(session, J1939_XTP_ABORT_BUSY); j1939_session_cancel(session, J1939_XTP_ABORT_BUSY);
return -EBUSY; return -EBUSY;
@ -1785,7 +1799,6 @@ static void j1939_xtp_rx_dat_one(struct j1939_session *session,
out_session_cancel: out_session_cancel:
j1939_session_timers_cancel(session); 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_cancel(session, J1939_XTP_ABORT_FAULT);
j1939_session_put(session); j1939_session_put(session);
} }
@ -1866,6 +1879,7 @@ struct j1939_session *j1939_tp_send(struct j1939_priv *priv,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* skb is recounted in j1939_session_new() */ /* skb is recounted in j1939_session_new() */
sock_hold(skb->sk);
session->sk = skb->sk; session->sk = skb->sk;
session->transmission = true; session->transmission = true;
session->pkt.total = (size + 6) / 7; 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, &priv->active_session_list,
active_session_list_entry) { active_session_list_entry) {
if (!sk || sk == session->sk) { 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; session->err = ESHUTDOWN;
j1939_session_deactivate_locked(session); j1939_session_deactivate_locked(session);
} }