mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-27 22:51:31 +00:00
Merge branch 'l2tp-fix-races-with-ipv4-mapped-ipv6-addresses'
Paolo Abeni says: ==================== l2tp: fix races with ipv4-mapped ipv6 addresses The syzbot reported an l2tp oops that uncovered some races in the l2tp xmit path and a partially related issue in the generic ipv6 code. We need to address them separately. v1 -> v2: - add missing fixes tag in patch 1 - fix several issues in patch 2 v2 -> v3: - dropped some unneeded chunks in patch 2 ==================== Reviewed-by: Guillaume Nault <g.nault@alphalink.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
38fbbc9c23
3 changed files with 32 additions and 30 deletions
|
@ -146,10 +146,12 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
|
||||||
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
|
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
|
||||||
struct inet_sock *inet = inet_sk(sk);
|
struct inet_sock *inet = inet_sk(sk);
|
||||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||||
struct in6_addr *daddr;
|
struct in6_addr *daddr, old_daddr;
|
||||||
|
__be32 fl6_flowlabel = 0;
|
||||||
|
__be32 old_fl6_flowlabel;
|
||||||
|
__be32 old_dport;
|
||||||
int addr_type;
|
int addr_type;
|
||||||
int err;
|
int err;
|
||||||
__be32 fl6_flowlabel = 0;
|
|
||||||
|
|
||||||
if (usin->sin6_family == AF_INET) {
|
if (usin->sin6_family == AF_INET) {
|
||||||
if (__ipv6_only_sock(sk))
|
if (__ipv6_only_sock(sk))
|
||||||
|
@ -238,9 +240,13 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* save the current peer information before updating it */
|
||||||
|
old_daddr = sk->sk_v6_daddr;
|
||||||
|
old_fl6_flowlabel = np->flow_label;
|
||||||
|
old_dport = inet->inet_dport;
|
||||||
|
|
||||||
sk->sk_v6_daddr = *daddr;
|
sk->sk_v6_daddr = *daddr;
|
||||||
np->flow_label = fl6_flowlabel;
|
np->flow_label = fl6_flowlabel;
|
||||||
|
|
||||||
inet->inet_dport = usin->sin6_port;
|
inet->inet_dport = usin->sin6_port;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -250,11 +256,12 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
|
||||||
|
|
||||||
err = ip6_datagram_dst_update(sk, true);
|
err = ip6_datagram_dst_update(sk, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
/* Reset daddr and dport so that udp_v6_early_demux()
|
/* Restore the socket peer info, to keep it consistent with
|
||||||
* fails to find this socket
|
* the old socket state
|
||||||
*/
|
*/
|
||||||
memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
|
sk->sk_v6_daddr = old_daddr;
|
||||||
inet->inet_dport = 0;
|
np->flow_label = old_fl6_flowlabel;
|
||||||
|
inet->inet_dport = old_dport;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,13 @@ struct l2tp_net {
|
||||||
spinlock_t l2tp_session_hlist_lock;
|
spinlock_t l2tp_session_hlist_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
static bool l2tp_sk_is_v6(struct sock *sk)
|
||||||
|
{
|
||||||
|
return sk->sk_family == PF_INET6 &&
|
||||||
|
!ipv6_addr_v4mapped(&sk->sk_v6_daddr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
|
static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
|
||||||
{
|
{
|
||||||
|
@ -1049,7 +1056,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
|
||||||
/* Queue the packet to IP for output */
|
/* Queue the packet to IP for output */
|
||||||
skb->ignore_df = 1;
|
skb->ignore_df = 1;
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped)
|
if (l2tp_sk_is_v6(tunnel->sock))
|
||||||
error = inet6_csk_xmit(tunnel->sock, skb, NULL);
|
error = inet6_csk_xmit(tunnel->sock, skb, NULL);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
@ -1112,6 +1119,15 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The user-space may change the connection status for the user-space
|
||||||
|
* provided socket at run time: we must check it under the socket lock
|
||||||
|
*/
|
||||||
|
if (tunnel->fd >= 0 && sk->sk_state != TCP_ESTABLISHED) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
ret = NET_XMIT_DROP;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get routing info from the tunnel socket */
|
/* Get routing info from the tunnel socket */
|
||||||
skb_dst_drop(skb);
|
skb_dst_drop(skb);
|
||||||
skb_dst_set(skb, dst_clone(__sk_dst_check(sk, 0)));
|
skb_dst_set(skb, dst_clone(__sk_dst_check(sk, 0)));
|
||||||
|
@ -1131,7 +1147,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
|
||||||
|
|
||||||
/* Calculate UDP checksum if configured to do so */
|
/* Calculate UDP checksum if configured to do so */
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
|
if (l2tp_sk_is_v6(sk))
|
||||||
udp6_set_csum(udp_get_no_check6_tx(sk),
|
udp6_set_csum(udp_get_no_check6_tx(sk),
|
||||||
skb, &inet6_sk(sk)->saddr,
|
skb, &inet6_sk(sk)->saddr,
|
||||||
&sk->sk_v6_daddr, udp_len);
|
&sk->sk_v6_daddr, udp_len);
|
||||||
|
@ -1511,24 +1527,6 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
|
||||||
if (cfg != NULL)
|
if (cfg != NULL)
|
||||||
tunnel->debug = cfg->debug;
|
tunnel->debug = cfg->debug;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
|
||||||
if (sk->sk_family == PF_INET6) {
|
|
||||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
||||||
|
|
||||||
if (ipv6_addr_v4mapped(&np->saddr) &&
|
|
||||||
ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
|
|
||||||
struct inet_sock *inet = inet_sk(sk);
|
|
||||||
|
|
||||||
tunnel->v4mapped = true;
|
|
||||||
inet->inet_saddr = np->saddr.s6_addr32[3];
|
|
||||||
inet->inet_rcv_saddr = sk->sk_v6_rcv_saddr.s6_addr32[3];
|
|
||||||
inet->inet_daddr = sk->sk_v6_daddr.s6_addr32[3];
|
|
||||||
} else {
|
|
||||||
tunnel->v4mapped = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
|
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
|
||||||
tunnel->encap = encap;
|
tunnel->encap = encap;
|
||||||
if (encap == L2TP_ENCAPTYPE_UDP) {
|
if (encap == L2TP_ENCAPTYPE_UDP) {
|
||||||
|
|
|
@ -188,9 +188,6 @@ struct l2tp_tunnel {
|
||||||
struct sock *sock; /* Parent socket */
|
struct sock *sock; /* Parent socket */
|
||||||
int fd; /* Parent fd, if tunnel socket
|
int fd; /* Parent fd, if tunnel socket
|
||||||
* was created by userspace */
|
* was created by userspace */
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
|
||||||
bool v4mapped;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct work_struct del_work;
|
struct work_struct del_work;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue