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

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

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: default avatarGuillaume Nault <g.nault@alphalink.fr>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b7475948 b954f940
Loading
Loading
Loading
Loading
+14 −7
Original line number Diff line number Diff line
@@ -146,10 +146,12 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
	struct sockaddr_in6	*usin = (struct sockaddr_in6 *) uaddr;
	struct inet_sock	*inet = inet_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			err;
	__be32			fl6_flowlabel = 0;

	if (usin->sin6_family == AF_INET) {
		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;
	np->flow_label = fl6_flowlabel;

	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);
	if (err) {
		/* Reset daddr and dport so that udp_v6_early_demux()
		 * fails to find this socket
		/* Restore the socket peer info, to keep it consistent with
		 * the old socket state
		 */
		memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
		inet->inet_dport = 0;
		sk->sk_v6_daddr = old_daddr;
		np->flow_label = old_fl6_flowlabel;
		inet->inet_dport = old_dport;
		goto out;
	}

+18 −20
Original line number Diff line number Diff line
@@ -111,6 +111,13 @@ struct l2tp_net {
	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)
{
@@ -1049,7 +1056,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
	/* Queue the packet to IP for output */
	skb->ignore_df = 1;
#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);
	else
#endif
@@ -1112,6 +1119,15 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
		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 */
	skb_dst_drop(skb);
	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 */
#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),
				      skb, &inet6_sk(sk)->saddr,
				      &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)
		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 */
	tunnel->encap = encap;
	if (encap == L2TP_ENCAPTYPE_UDP) {
+0 −3
Original line number Diff line number Diff line
@@ -188,9 +188,6 @@ struct l2tp_tunnel {
	struct sock		*sock;		/* Parent socket */
	int			fd;		/* Parent fd, if tunnel socket
						 * was created by userspace */
#if IS_ENABLED(CONFIG_IPV6)
	bool			v4mapped;
#endif

	struct work_struct	del_work;