Commit bd2ec34d authored by Paolo Abeni's avatar Paolo Abeni
Browse files
Steffen Klassert says:

====================
pull request (net): ipsec 2025-05-21

1) Fix some missing kfree_skb in the error paths of espintcp.
   From Sabrina Dubroca.

2) Fix a reference leak in espintcp.
   From Sabrina Dubroca.

3) Fix UDP GRO handling for ESPINUDP.
   From Tobias Brunner.

4) Fix ipcomp truesize computation on the receive path.
   From Sabrina Dubroca.

5) Sanitize marks before policy/state insertation.
   From Paul Chaignon.

* tag 'ipsec-2025-05-21' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec:
  xfrm: Sanitize marks before insert
  xfrm: ipcomp: fix truesize computation on receive
  xfrm: Fix UDP GRO handling for some corner cases
  espintcp: remove encap socket caching to avoid reference leak
  espintcp: fix skb leaks
====================

Link: https://patch.msgid.link/20250521054348.4057269-1-steffen.klassert@secunet.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents e2790246 0b91fda3
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -236,7 +236,6 @@ struct xfrm_state {

	/* Data for encapsulator */
	struct xfrm_encap_tmpl	*encap;
	struct sock __rcu	*encap_sk;

	/* NAT keepalive */
	u32			nat_keepalive_interval; /* seconds */
+7 −46
Original line number Diff line number Diff line
@@ -120,47 +120,16 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
}

#ifdef CONFIG_INET_ESPINTCP
struct esp_tcp_sk {
	struct sock *sk;
	struct rcu_head rcu;
};

static void esp_free_tcp_sk(struct rcu_head *head)
{
	struct esp_tcp_sk *esk = container_of(head, struct esp_tcp_sk, rcu);

	sock_put(esk->sk);
	kfree(esk);
}

static struct sock *esp_find_tcp_sk(struct xfrm_state *x)
{
	struct xfrm_encap_tmpl *encap = x->encap;
	struct net *net = xs_net(x);
	struct esp_tcp_sk *esk;
	__be16 sport, dport;
	struct sock *nsk;
	struct sock *sk;

	sk = rcu_dereference(x->encap_sk);
	if (sk && sk->sk_state == TCP_ESTABLISHED)
		return sk;

	spin_lock_bh(&x->lock);
	sport = encap->encap_sport;
	dport = encap->encap_dport;
	nsk = rcu_dereference_protected(x->encap_sk,
					lockdep_is_held(&x->lock));
	if (sk && sk == nsk) {
		esk = kmalloc(sizeof(*esk), GFP_ATOMIC);
		if (!esk) {
			spin_unlock_bh(&x->lock);
			return ERR_PTR(-ENOMEM);
		}
		RCU_INIT_POINTER(x->encap_sk, NULL);
		esk->sk = sk;
		call_rcu(&esk->rcu, esp_free_tcp_sk);
	}
	spin_unlock_bh(&x->lock);

	sk = inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, x->id.daddr.a4,
@@ -173,20 +142,6 @@ static struct sock *esp_find_tcp_sk(struct xfrm_state *x)
		return ERR_PTR(-EINVAL);
	}

	spin_lock_bh(&x->lock);
	nsk = rcu_dereference_protected(x->encap_sk,
					lockdep_is_held(&x->lock));
	if (encap->encap_sport != sport ||
	    encap->encap_dport != dport) {
		sock_put(sk);
		sk = nsk ?: ERR_PTR(-EREMCHG);
	} else if (sk == nsk) {
		sock_put(sk);
	} else {
		rcu_assign_pointer(x->encap_sk, sk);
	}
	spin_unlock_bh(&x->lock);

	return sk;
}

@@ -199,8 +154,10 @@ static int esp_output_tcp_finish(struct xfrm_state *x, struct sk_buff *skb)

	sk = esp_find_tcp_sk(x);
	err = PTR_ERR_OR_ZERO(sk);
	if (err)
	if (err) {
		kfree_skb(skb);
		goto out;
	}

	bh_lock_sock(sk);
	if (sock_owned_by_user(sk))
@@ -209,6 +166,8 @@ static int esp_output_tcp_finish(struct xfrm_state *x, struct sk_buff *skb)
		err = espintcp_push_skb(sk, skb);
	bh_unlock_sock(sk);

	sock_put(sk);

out:
	rcu_read_unlock();
	return err;
@@ -392,6 +351,8 @@ static struct ip_esp_hdr *esp_output_tcp_encap(struct xfrm_state *x,
	if (IS_ERR(sk))
		return ERR_CAST(sk);

	sock_put(sk);

	*lenp = htons(len);
	esph = (struct ip_esp_hdr *)(lenp + 1);

+10 −8
Original line number Diff line number Diff line
@@ -182,11 +182,15 @@ struct sk_buff *xfrm4_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
	int offset = skb_gro_offset(skb);
	const struct net_offload *ops;
	struct sk_buff *pp = NULL;
	int ret;

	offset = offset - sizeof(struct udphdr);
	int len, dlen;
	__u8 *udpdata;
	__be32 *udpdata32;

	if (!pskb_pull(skb, offset))
	len = skb->len - offset;
	dlen = offset + min(len, 8);
	udpdata = skb_gro_header(skb, dlen, offset);
	udpdata32 = (__be32 *)udpdata;
	if (unlikely(!udpdata))
		return NULL;

	rcu_read_lock();
@@ -194,11 +198,10 @@ struct sk_buff *xfrm4_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
	if (!ops || !ops->callbacks.gro_receive)
		goto out;

	ret = __xfrm4_udp_encap_rcv(sk, skb, false);
	if (ret)
	/* check if it is a keepalive or IKE packet */
	if (len <= sizeof(struct ip_esp_hdr) || udpdata32[0] == 0)
		goto out;

	skb_push(skb, offset);
	NAPI_GRO_CB(skb)->proto = IPPROTO_UDP;

	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
@@ -208,7 +211,6 @@ struct sk_buff *xfrm4_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,

out:
	rcu_read_unlock();
	skb_push(skb, offset);
	NAPI_GRO_CB(skb)->same_flow = 0;
	NAPI_GRO_CB(skb)->flush = 1;

+7 −46
Original line number Diff line number Diff line
@@ -137,47 +137,16 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
}

#ifdef CONFIG_INET6_ESPINTCP
struct esp_tcp_sk {
	struct sock *sk;
	struct rcu_head rcu;
};

static void esp_free_tcp_sk(struct rcu_head *head)
{
	struct esp_tcp_sk *esk = container_of(head, struct esp_tcp_sk, rcu);

	sock_put(esk->sk);
	kfree(esk);
}

static struct sock *esp6_find_tcp_sk(struct xfrm_state *x)
{
	struct xfrm_encap_tmpl *encap = x->encap;
	struct net *net = xs_net(x);
	struct esp_tcp_sk *esk;
	__be16 sport, dport;
	struct sock *nsk;
	struct sock *sk;

	sk = rcu_dereference(x->encap_sk);
	if (sk && sk->sk_state == TCP_ESTABLISHED)
		return sk;

	spin_lock_bh(&x->lock);
	sport = encap->encap_sport;
	dport = encap->encap_dport;
	nsk = rcu_dereference_protected(x->encap_sk,
					lockdep_is_held(&x->lock));
	if (sk && sk == nsk) {
		esk = kmalloc(sizeof(*esk), GFP_ATOMIC);
		if (!esk) {
			spin_unlock_bh(&x->lock);
			return ERR_PTR(-ENOMEM);
		}
		RCU_INIT_POINTER(x->encap_sk, NULL);
		esk->sk = sk;
		call_rcu(&esk->rcu, esp_free_tcp_sk);
	}
	spin_unlock_bh(&x->lock);

	sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, &x->id.daddr.in6,
@@ -190,20 +159,6 @@ static struct sock *esp6_find_tcp_sk(struct xfrm_state *x)
		return ERR_PTR(-EINVAL);
	}

	spin_lock_bh(&x->lock);
	nsk = rcu_dereference_protected(x->encap_sk,
					lockdep_is_held(&x->lock));
	if (encap->encap_sport != sport ||
	    encap->encap_dport != dport) {
		sock_put(sk);
		sk = nsk ?: ERR_PTR(-EREMCHG);
	} else if (sk == nsk) {
		sock_put(sk);
	} else {
		rcu_assign_pointer(x->encap_sk, sk);
	}
	spin_unlock_bh(&x->lock);

	return sk;
}

@@ -216,8 +171,10 @@ static int esp_output_tcp_finish(struct xfrm_state *x, struct sk_buff *skb)

	sk = esp6_find_tcp_sk(x);
	err = PTR_ERR_OR_ZERO(sk);
	if (err)
	if (err) {
		kfree_skb(skb);
		goto out;
	}

	bh_lock_sock(sk);
	if (sock_owned_by_user(sk))
@@ -226,6 +183,8 @@ static int esp_output_tcp_finish(struct xfrm_state *x, struct sk_buff *skb)
		err = espintcp_push_skb(sk, skb);
	bh_unlock_sock(sk);

	sock_put(sk);

out:
	rcu_read_unlock();
	return err;
@@ -422,6 +381,8 @@ static struct ip_esp_hdr *esp6_output_tcp_encap(struct xfrm_state *x,
	if (IS_ERR(sk))
		return ERR_CAST(sk);

	sock_put(sk);

	*lenp = htons(len);
	esph = (struct ip_esp_hdr *)(lenp + 1);

+10 −8
Original line number Diff line number Diff line
@@ -179,14 +179,18 @@ struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
	int offset = skb_gro_offset(skb);
	const struct net_offload *ops;
	struct sk_buff *pp = NULL;
	int ret;
	int len, dlen;
	__u8 *udpdata;
	__be32 *udpdata32;

	if (skb->protocol == htons(ETH_P_IP))
		return xfrm4_gro_udp_encap_rcv(sk, head, skb);

	offset = offset - sizeof(struct udphdr);

	if (!pskb_pull(skb, offset))
	len = skb->len - offset;
	dlen = offset + min(len, 8);
	udpdata = skb_gro_header(skb, dlen, offset);
	udpdata32 = (__be32 *)udpdata;
	if (unlikely(!udpdata))
		return NULL;

	rcu_read_lock();
@@ -194,11 +198,10 @@ struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
	if (!ops || !ops->callbacks.gro_receive)
		goto out;

	ret = __xfrm6_udp_encap_rcv(sk, skb, false);
	if (ret)
	/* check if it is a keepalive or IKE packet */
	if (len <= sizeof(struct ip_esp_hdr) || udpdata32[0] == 0)
		goto out;

	skb_push(skb, offset);
	NAPI_GRO_CB(skb)->proto = IPPROTO_UDP;

	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
@@ -208,7 +211,6 @@ struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,

out:
	rcu_read_unlock();
	skb_push(skb, offset);
	NAPI_GRO_CB(skb)->same_flow = 0;
	NAPI_GRO_CB(skb)->flush = 1;

Loading