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


Steffen Klassert says:

====================
pull request (net): ipsec 2019-02-21

1) Don't do TX bytes accounting for the esp trailer when sending
   from a request socket as this will result in an out of bounds
   memory write. From Martin Willi.

2) Destroy xfrm_state synchronously on net exit path to
   avoid nested gc flush callbacks that may trigger a
   warning in xfrm6_tunnel_net_exit(). From Cong Wang.

3) Do an unconditionally clone in pfkey_broadcast_one()
   to avoid a race when freeing the skb.
   From Sean Tranchetti.

4) Fix inbound traffic via XFRM interfaces across network
   namespaces. We did the lookup for interfaces and policies
   in the wrong namespace. From Tobias Brunner.

Please pull or let me know if there are problems.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 31088cb5 660899dd
Loading
Loading
Loading
Loading
+9 −3
Original line number Original line Diff line number Diff line
@@ -853,7 +853,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
		xfrm_pol_put(pols[i]);
		xfrm_pol_put(pols[i]);
}
}


void __xfrm_state_destroy(struct xfrm_state *);
void __xfrm_state_destroy(struct xfrm_state *, bool);


static inline void __xfrm_state_put(struct xfrm_state *x)
static inline void __xfrm_state_put(struct xfrm_state *x)
{
{
@@ -863,7 +863,13 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
static inline void xfrm_state_put(struct xfrm_state *x)
static inline void xfrm_state_put(struct xfrm_state *x)
{
{
	if (refcount_dec_and_test(&x->refcnt))
	if (refcount_dec_and_test(&x->refcnt))
		__xfrm_state_destroy(x);
		__xfrm_state_destroy(x, false);
}

static inline void xfrm_state_put_sync(struct xfrm_state *x)
{
	if (refcount_dec_and_test(&x->refcnt))
		__xfrm_state_destroy(x, true);
}
}


static inline void xfrm_state_hold(struct xfrm_state *x)
static inline void xfrm_state_hold(struct xfrm_state *x)
@@ -1590,7 +1596,7 @@ struct xfrmk_spdinfo {


struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
int xfrm_state_delete(struct xfrm_state *x);
int xfrm_state_delete(struct xfrm_state *x);
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+1 −1
Original line number Original line Diff line number Diff line
@@ -328,7 +328,7 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
			skb->len += tailen;
			skb->len += tailen;
			skb->data_len += tailen;
			skb->data_len += tailen;
			skb->truesize += tailen;
			skb->truesize += tailen;
			if (sk)
			if (sk && sk_fullsock(sk))
				refcount_add(tailen, &sk->sk_wmem_alloc);
				refcount_add(tailen, &sk->sk_wmem_alloc);


			goto out;
			goto out;
+1 −1
Original line number Original line Diff line number Diff line
@@ -296,7 +296,7 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
			skb->len += tailen;
			skb->len += tailen;
			skb->data_len += tailen;
			skb->data_len += tailen;
			skb->truesize += tailen;
			skb->truesize += tailen;
			if (sk)
			if (sk && sk_fullsock(sk))
				refcount_add(tailen, &sk->sk_wmem_alloc);
				refcount_add(tailen, &sk->sk_wmem_alloc);


			goto out;
			goto out;
+1 −1
Original line number Original line Diff line number Diff line
@@ -344,8 +344,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
	unsigned int i;
	unsigned int i;


	xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
	xfrm_flush_gc();
	xfrm_flush_gc();
	xfrm_state_flush(net, IPSEC_PROTO_ANY, false, true);


	for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
	for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
		WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
		WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
+16 −26
Original line number Original line Diff line number Diff line
@@ -196,30 +196,22 @@ static int pfkey_release(struct socket *sock)
	return 0;
	return 0;
}
}


static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
static int pfkey_broadcast_one(struct sk_buff *skb, gfp_t allocation,
			       gfp_t allocation, struct sock *sk)
			       struct sock *sk)
{
{
	int err = -ENOBUFS;
	int err = -ENOBUFS;


	sock_hold(sk);
	if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf)
	if (*skb2 == NULL) {
		return err;
		if (refcount_read(&skb->users) != 1) {

			*skb2 = skb_clone(skb, allocation);
	skb = skb_clone(skb, allocation);
		} else {

			*skb2 = skb;
	if (skb) {
			refcount_inc(&skb->users);
		skb_set_owner_r(skb, sk);
		}
		skb_queue_tail(&sk->sk_receive_queue, skb);
	}
	if (*skb2 != NULL) {
		if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
			skb_set_owner_r(*skb2, sk);
			skb_queue_tail(&sk->sk_receive_queue, *skb2);
		sk->sk_data_ready(sk);
		sk->sk_data_ready(sk);
			*skb2 = NULL;
		err = 0;
		err = 0;
	}
	}
	}
	sock_put(sk);
	return err;
	return err;
}
}


@@ -234,7 +226,6 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
{
{
	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
	struct sock *sk;
	struct sock *sk;
	struct sk_buff *skb2 = NULL;
	int err = -ESRCH;
	int err = -ESRCH;


	/* XXX Do we need something like netlink_overrun?  I think
	/* XXX Do we need something like netlink_overrun?  I think
@@ -253,7 +244,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
		 * socket.
		 * socket.
		 */
		 */
		if (pfk->promisc)
		if (pfk->promisc)
			pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk);
			pfkey_broadcast_one(skb, GFP_ATOMIC, sk);


		/* the exact target will be processed later */
		/* the exact target will be processed later */
		if (sk == one_sk)
		if (sk == one_sk)
@@ -268,7 +259,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
				continue;
				continue;
		}
		}


		err2 = pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk);
		err2 = pfkey_broadcast_one(skb, GFP_ATOMIC, sk);


		/* Error is cleared after successful sending to at least one
		/* Error is cleared after successful sending to at least one
		 * registered KM */
		 * registered KM */
@@ -278,9 +269,8 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
	rcu_read_unlock();
	rcu_read_unlock();


	if (one_sk != NULL)
	if (one_sk != NULL)
		err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
		err = pfkey_broadcast_one(skb, allocation, one_sk);


	kfree_skb(skb2);
	kfree_skb(skb);
	kfree_skb(skb);
	return err;
	return err;
}
}
@@ -1783,7 +1773,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
	if (proto == 0)
	if (proto == 0)
		return -EINVAL;
		return -EINVAL;


	err = xfrm_state_flush(net, proto, true);
	err = xfrm_state_flush(net, proto, true, false);
	err2 = unicast_flush_resp(sk, hdr);
	err2 = unicast_flush_resp(sk, hdr);
	if (err || err2) {
	if (err || err2) {
		if (err == -ESRCH) /* empty table - go quietly */
		if (err == -ESRCH) /* empty table - go quietly */
Loading