Commit 646be03e authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Steffen Klassert says:

====================
ipsec 2023-02-08

1) Fix policy checks for nested IPsec tunnels when using
   xfrm interfaces. From Benedict Wong.

2) Fix netlink message expression on 32=>64-bit
   messages translators. From Anastasia Belova.

3) Prevent potential spectre v1 gadget in xfrm_xlate32_attr.
   From Eric Dumazet.

4) Always consistently use time64_t in xfrm_timer_handler.
   From Eric Dumazet.

5) Fix KCSAN reported bug: Multiple cpus can update use_time
   at the same time. From Eric Dumazet.

6) Fix SCP copy from IPv4 to IPv6 on interfamily tunnel.
   From Christian Hopps.

* tag 'ipsec-2023-02-08' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec:
  xfrm: fix bug with DSCP copy to v6 from v4 tunnel
  xfrm: annotate data-race around use_time
  xfrm: consistently use time64_t in xfrm_timer_handler()
  xfrm/compat: prevent potential spectre v1 gadget in xfrm_xlate32_attr()
  xfrm: compat: change expression for switch in xfrm_xlate64
  Fix XFRM-I support for nested ESP tunnels
====================

Link: https://lore.kernel.org/r/20230208114322.266510-1-steffen.klassert@secunet.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 363d7c22 6028da3f
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 * Based on code and translator idea by: Florian Westphal <fw@strlen.de>
 */
#include <linux/compat.h>
#include <linux/nospec.h>
#include <linux/xfrm.h>
#include <net/xfrm.h>

@@ -302,7 +303,7 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src)
	nla_for_each_attr(nla, attrs, len, remaining) {
		int err;

		switch (type) {
		switch (nlh_src->nlmsg_type) {
		case XFRM_MSG_NEWSPDINFO:
			err = xfrm_nla_cpy(dst, nla, nla_len(nla));
			break;
@@ -437,6 +438,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
		NL_SET_ERR_MSG(extack, "Bad attribute");
		return -EOPNOTSUPP;
	}
	type = array_index_nospec(type, XFRMA_MAX + 1);
	if (nla_len(nla) < compat_policy[type].len) {
		NL_SET_ERR_MSG(extack, "Attribute bad length");
		return -EOPNOTSUPP;
+1 −2
Original line number Diff line number Diff line
@@ -279,8 +279,7 @@ static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb)
		goto out;

	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)),
			       ipipv6_hdr(skb));
		ipv6_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipipv6_hdr(skb));
	if (!(x->props.flags & XFRM_STATE_NOECN))
		ipip6_ecn_decapsulate(skb);

+50 −4
Original line number Diff line number Diff line
@@ -310,6 +310,52 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
	skb->mark = 0;
}

static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi,
		       int encap_type, unsigned short family)
{
	struct sec_path *sp;

	sp = skb_sec_path(skb);
	if (sp && (sp->len || sp->olen) &&
	    !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family))
		goto discard;

	XFRM_SPI_SKB_CB(skb)->family = family;
	if (family == AF_INET) {
		XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
		XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
	} else {
		XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
		XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
	}

	return xfrm_input(skb, nexthdr, spi, encap_type);
discard:
	kfree_skb(skb);
	return 0;
}

static int xfrmi4_rcv(struct sk_buff *skb)
{
	return xfrmi_input(skb, ip_hdr(skb)->protocol, 0, 0, AF_INET);
}

static int xfrmi6_rcv(struct sk_buff *skb)
{
	return xfrmi_input(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],
			   0, 0, AF_INET6);
}

static int xfrmi4_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
	return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET);
}

static int xfrmi6_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
	return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET6);
}

static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
{
	const struct xfrm_mode *inner_mode;
@@ -945,8 +991,8 @@ static struct pernet_operations xfrmi_net_ops = {
};

static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = {
	.handler	=	xfrm6_rcv,
	.input_handler	=	xfrm_input,
	.handler	=	xfrmi6_rcv,
	.input_handler	=	xfrmi6_input,
	.cb_handler	=	xfrmi_rcv_cb,
	.err_handler	=	xfrmi6_err,
	.priority	=	10,
@@ -996,8 +1042,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = {
#endif

static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = {
	.handler	=	xfrm4_rcv,
	.input_handler	=	xfrm_input,
	.handler	=	xfrmi4_rcv,
	.input_handler	=	xfrmi4_input,
	.cb_handler	=	xfrmi_rcv_cb,
	.err_handler	=	xfrmi4_err,
	.priority	=	10,
+10 −4
Original line number Diff line number Diff line
@@ -336,7 +336,7 @@ static void xfrm_policy_timer(struct timer_list *t)
	}
	if (xp->lft.hard_use_expires_seconds) {
		time64_t tmo = xp->lft.hard_use_expires_seconds +
			(xp->curlft.use_time ? : xp->curlft.add_time) - now;
			(READ_ONCE(xp->curlft.use_time) ? : xp->curlft.add_time) - now;
		if (tmo <= 0)
			goto expired;
		if (tmo < next)
@@ -354,7 +354,7 @@ static void xfrm_policy_timer(struct timer_list *t)
	}
	if (xp->lft.soft_use_expires_seconds) {
		time64_t tmo = xp->lft.soft_use_expires_seconds +
			(xp->curlft.use_time ? : xp->curlft.add_time) - now;
			(READ_ONCE(xp->curlft.use_time) ? : xp->curlft.add_time) - now;
		if (tmo <= 0) {
			warn = 1;
			tmo = XFRM_KM_TIMEOUT;
@@ -3661,7 +3661,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
		return 1;
	}

	pol->curlft.use_time = ktime_get_real_seconds();
	/* This lockless write can happen from different cpus. */
	WRITE_ONCE(pol->curlft.use_time, ktime_get_real_seconds());

	pols[0] = pol;
	npols++;
@@ -3676,7 +3677,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
				xfrm_pol_put(pols[0]);
				return 0;
			}
			pols[1]->curlft.use_time = ktime_get_real_seconds();
			/* This write can happen from different cpus. */
			WRITE_ONCE(pols[1]->curlft.use_time,
				   ktime_get_real_seconds());
			npols++;
		}
	}
@@ -3742,6 +3745,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
			goto reject;
		}

		if (if_id)
			secpath_reset(skb);

		xfrm_pols_put(pols, npols);
		return 1;
	}
+9 −9
Original line number Diff line number Diff line
@@ -577,7 +577,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
	if (x->km.state == XFRM_STATE_EXPIRED)
		goto expired;
	if (x->lft.hard_add_expires_seconds) {
		long tmo = x->lft.hard_add_expires_seconds +
		time64_t tmo = x->lft.hard_add_expires_seconds +
			x->curlft.add_time - now;
		if (tmo <= 0) {
			if (x->xflags & XFRM_SOFT_EXPIRE) {
@@ -594,8 +594,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
			next = tmo;
	}
	if (x->lft.hard_use_expires_seconds) {
		long tmo = x->lft.hard_use_expires_seconds +
			(x->curlft.use_time ? : now) - now;
		time64_t tmo = x->lft.hard_use_expires_seconds +
			(READ_ONCE(x->curlft.use_time) ? : now) - now;
		if (tmo <= 0)
			goto expired;
		if (tmo < next)
@@ -604,7 +604,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
	if (x->km.dying)
		goto resched;
	if (x->lft.soft_add_expires_seconds) {
		long tmo = x->lft.soft_add_expires_seconds +
		time64_t tmo = x->lft.soft_add_expires_seconds +
			x->curlft.add_time - now;
		if (tmo <= 0) {
			warn = 1;
@@ -616,8 +616,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
		}
	}
	if (x->lft.soft_use_expires_seconds) {
		long tmo = x->lft.soft_use_expires_seconds +
			(x->curlft.use_time ? : now) - now;
		time64_t tmo = x->lft.soft_use_expires_seconds +
			(READ_ONCE(x->curlft.use_time) ? : now) - now;
		if (tmo <= 0)
			warn = 1;
		else if (tmo < next)
@@ -1906,7 +1906,7 @@ int xfrm_state_update(struct xfrm_state *x)

		hrtimer_start(&x1->mtimer, ktime_set(1, 0),
			      HRTIMER_MODE_REL_SOFT);
		if (x1->curlft.use_time)
		if (READ_ONCE(x1->curlft.use_time))
			xfrm_state_check_expire(x1);

		if (x->props.smark.m || x->props.smark.v || x->if_id) {
@@ -1940,8 +1940,8 @@ int xfrm_state_check_expire(struct xfrm_state *x)
{
	xfrm_dev_state_update_curlft(x);

	if (!x->curlft.use_time)
		x->curlft.use_time = ktime_get_real_seconds();
	if (!READ_ONCE(x->curlft.use_time))
		WRITE_ONCE(x->curlft.use_time, ktime_get_real_seconds());

	if (x->curlft.bytes >= x->lft.hard_byte_limit ||
	    x->curlft.packets >= x->lft.hard_packet_limit) {