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

Commit 04cb69df authored by Paolo Abeni's avatar Paolo Abeni Committed by Subash Abhinov Kasiviswanathan
Browse files

udp6: fix socket leak on early demux



When an early demuxed packet reaches __udp6_lib_lookup_skb(), the
sk reference is retrieved and used, but the relevant reference
count is leaked and the socket destructor is never called.
Beyond leaking the sk memory, if there are pending UDP packets
in the receive queue, even the related accounted memory is leaked.

In the long run, this will cause persistent forward allocation errors
and no UDP skbs (both ipv4 and ipv6) will be able to reach the
user-space.

Fix this by explicitly accessing the early demux reference before
the lookup, and properly decreasing the socket reference count
after usage.

Also drop the skb_steal_sock() in __udp6_lib_lookup_skb(), and
the now obsoleted comment about "socket cache".

The newly added code is derived from the current ipv4 code for the
similar path.

v1 -> v2:
  fixed the __udp6_lib_rcv() return code for resubmission,
  as suggested by Eric

CRs-Fixed: 2122802
Change-Id: I2ede4e7661a35bd2234258c7514fd99ea9bbeea5
Reported-by: default avatarSam Edwards <CFSworks@gmail.com>
Reported-by: default avatarMarc Haber <mh+netdev@zugschlus.de>
Fixes: 5425077d73e0 ("net: ipv6: Add early demux handler for UDP unicast")
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Git-commit: c9f2c1ae123a751d4e4f949144500219354d5ee1
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git


Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
parent d61f7af0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -247,6 +247,7 @@ static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb,

/* net/ipv4/udp.c */
void udp_v4_early_demux(struct sk_buff *skb);
void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
int udp_get_port(struct sock *sk, unsigned short snum,
		 int (*saddr_cmp)(const struct sock *,
				  const struct sock *));
+2 −1
Original line number Diff line number Diff line
@@ -1627,7 +1627,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
/* For TCP sockets, sk_rx_dst is protected by socket lock
 * For UDP, we use xchg() to guard against concurrent changes.
 */
static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
{
	struct dst_entry *old;

@@ -1635,6 +1635,7 @@ static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
	old = xchg(&sk->sk_rx_dst, dst);
	dst_release(old);
}
EXPORT_SYMBOL(udp_sk_rx_dst_set);

/*
 *	Multicasts and broadcasts go to each listener.
+18 −9
Original line number Diff line number Diff line
@@ -278,11 +278,7 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
					  struct udp_table *udptable)
{
	const struct ipv6hdr *iph = ipv6_hdr(skb);
	struct sock *sk;

	sk = skb_steal_sock(skb);
	if (unlikely(sk))
		return sk;
	return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport,
				 &iph->daddr, dport, inet6_iif(skb),
				 udptable, skb);
@@ -800,6 +796,24 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
	if (udp6_csum_init(skb, uh, proto))
		goto csum_error;

	/* Check if the socket is already available, e.g. due to early demux */
	sk = skb_steal_sock(skb);
	if (sk) {
		struct dst_entry *dst = skb_dst(skb);
		int ret;

		if (unlikely(sk->sk_rx_dst != dst))
			udp_sk_rx_dst_set(sk, dst);

		ret = udpv6_queue_rcv_skb(sk, skb);
		sock_put(sk);

		/* a return value > 0 means to resubmit the input */
		if (ret > 0)
			return ret;
		return 0;
	}

	/*
	 *	Multicast receive code
	 */
@@ -808,11 +822,6 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
				saddr, daddr, udptable, proto);

	/* Unicast */

	/*
	 * check socket cache ... must talk to Alan about his plans
	 * for sock caches... i'll skip this for now.
	 */
	sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
	if (sk) {
		int ret;