can: j1939: socket: rework socket locking for j1939_sk_release() and j1939_sk_sendmsg()

j1939_sk_sendmsg() should be protected by lock_sock() to avoid race with
j1939_sk_bind() and j1939_sk_release().

Reported-by: syzbot+afd421337a736d6c1ee6@syzkaller.appspotmail.com
Reported-by: syzbot+6d04f6a1b31a0ae12ca9@syzkaller.appspotmail.com
Fixes: 9d71dd0c70 ("can: add support of SAE J1939 protocol")
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
This commit is contained in:
Oleksij Rempel 2019-11-05 14:31:58 +01:00 committed by Marc Kleine-Budde
parent c48c8c1e2e
commit fd81ebfe79
1 changed files with 39 additions and 18 deletions

View File

@ -593,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;
@ -1092,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;
} }