Commit 646be03e authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'ipsec-2023-02-08' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec

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.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 363d7c22 6028da3f
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Based on code and translator idea by: Florian Westphal <fw@strlen.de> * Based on code and translator idea by: Florian Westphal <fw@strlen.de>
*/ */
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/nospec.h>
#include <linux/xfrm.h> #include <linux/xfrm.h>
#include <net/xfrm.h> #include <net/xfrm.h>
...@@ -302,7 +303,7 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) ...@@ -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) { nla_for_each_attr(nla, attrs, len, remaining) {
int err; int err;
switch (type) { switch (nlh_src->nlmsg_type) {
case XFRM_MSG_NEWSPDINFO: case XFRM_MSG_NEWSPDINFO:
err = xfrm_nla_cpy(dst, nla, nla_len(nla)); err = xfrm_nla_cpy(dst, nla, nla_len(nla));
break; break;
...@@ -437,6 +438,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, ...@@ -437,6 +438,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
NL_SET_ERR_MSG(extack, "Bad attribute"); NL_SET_ERR_MSG(extack, "Bad attribute");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
type = array_index_nospec(type, XFRMA_MAX + 1);
if (nla_len(nla) < compat_policy[type].len) { if (nla_len(nla) < compat_policy[type].len) {
NL_SET_ERR_MSG(extack, "Attribute bad length"); NL_SET_ERR_MSG(extack, "Attribute bad length");
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -279,8 +279,7 @@ static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb) ...@@ -279,8 +279,7 @@ static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb)
goto out; goto out;
if (x->props.flags & XFRM_STATE_DECAP_DSCP) if (x->props.flags & XFRM_STATE_DECAP_DSCP)
ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)), ipv6_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipipv6_hdr(skb));
ipipv6_hdr(skb));
if (!(x->props.flags & XFRM_STATE_NOECN)) if (!(x->props.flags & XFRM_STATE_NOECN))
ipip6_ecn_decapsulate(skb); ipip6_ecn_decapsulate(skb);
......
...@@ -310,6 +310,52 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) ...@@ -310,6 +310,52 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
skb->mark = 0; 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) static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
{ {
const struct xfrm_mode *inner_mode; const struct xfrm_mode *inner_mode;
...@@ -945,8 +991,8 @@ static struct pernet_operations xfrmi_net_ops = { ...@@ -945,8 +991,8 @@ static struct pernet_operations xfrmi_net_ops = {
}; };
static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = {
.handler = xfrm6_rcv, .handler = xfrmi6_rcv,
.input_handler = xfrm_input, .input_handler = xfrmi6_input,
.cb_handler = xfrmi_rcv_cb, .cb_handler = xfrmi_rcv_cb,
.err_handler = xfrmi6_err, .err_handler = xfrmi6_err,
.priority = 10, .priority = 10,
...@@ -996,8 +1042,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { ...@@ -996,8 +1042,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = {
#endif #endif
static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = {
.handler = xfrm4_rcv, .handler = xfrmi4_rcv,
.input_handler = xfrm_input, .input_handler = xfrmi4_input,
.cb_handler = xfrmi_rcv_cb, .cb_handler = xfrmi_rcv_cb,
.err_handler = xfrmi4_err, .err_handler = xfrmi4_err,
.priority = 10, .priority = 10,
......
...@@ -336,7 +336,7 @@ static void xfrm_policy_timer(struct timer_list *t) ...@@ -336,7 +336,7 @@ static void xfrm_policy_timer(struct timer_list *t)
} }
if (xp->lft.hard_use_expires_seconds) { if (xp->lft.hard_use_expires_seconds) {
time64_t tmo = 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) if (tmo <= 0)
goto expired; goto expired;
if (tmo < next) if (tmo < next)
...@@ -354,7 +354,7 @@ static void xfrm_policy_timer(struct timer_list *t) ...@@ -354,7 +354,7 @@ static void xfrm_policy_timer(struct timer_list *t)
} }
if (xp->lft.soft_use_expires_seconds) { if (xp->lft.soft_use_expires_seconds) {
time64_t tmo = 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) { if (tmo <= 0) {
warn = 1; warn = 1;
tmo = XFRM_KM_TIMEOUT; tmo = XFRM_KM_TIMEOUT;
...@@ -3661,7 +3661,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -3661,7 +3661,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
return 1; 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; pols[0] = pol;
npols++; npols++;
...@@ -3676,7 +3677,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -3676,7 +3677,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
xfrm_pol_put(pols[0]); xfrm_pol_put(pols[0]);
return 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++; npols++;
} }
} }
...@@ -3742,6 +3745,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -3742,6 +3745,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
goto reject; goto reject;
} }
if (if_id)
secpath_reset(skb);
xfrm_pols_put(pols, npols); xfrm_pols_put(pols, npols);
return 1; return 1;
} }
......
...@@ -577,7 +577,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) ...@@ -577,7 +577,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
if (x->km.state == XFRM_STATE_EXPIRED) if (x->km.state == XFRM_STATE_EXPIRED)
goto expired; goto expired;
if (x->lft.hard_add_expires_seconds) { 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; x->curlft.add_time - now;
if (tmo <= 0) { if (tmo <= 0) {
if (x->xflags & XFRM_SOFT_EXPIRE) { if (x->xflags & XFRM_SOFT_EXPIRE) {
...@@ -594,8 +594,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) ...@@ -594,8 +594,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
next = tmo; next = tmo;
} }
if (x->lft.hard_use_expires_seconds) { if (x->lft.hard_use_expires_seconds) {
long tmo = x->lft.hard_use_expires_seconds + time64_t tmo = x->lft.hard_use_expires_seconds +
(x->curlft.use_time ? : now) - now; (READ_ONCE(x->curlft.use_time) ? : now) - now;
if (tmo <= 0) if (tmo <= 0)
goto expired; goto expired;
if (tmo < next) if (tmo < next)
...@@ -604,7 +604,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) ...@@ -604,7 +604,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
if (x->km.dying) if (x->km.dying)
goto resched; goto resched;
if (x->lft.soft_add_expires_seconds) { 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; x->curlft.add_time - now;
if (tmo <= 0) { if (tmo <= 0) {
warn = 1; warn = 1;
...@@ -616,8 +616,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) ...@@ -616,8 +616,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
} }
} }
if (x->lft.soft_use_expires_seconds) { if (x->lft.soft_use_expires_seconds) {
long tmo = x->lft.soft_use_expires_seconds + time64_t tmo = x->lft.soft_use_expires_seconds +
(x->curlft.use_time ? : now) - now; (READ_ONCE(x->curlft.use_time) ? : now) - now;
if (tmo <= 0) if (tmo <= 0)
warn = 1; warn = 1;
else if (tmo < next) else if (tmo < next)
...@@ -1906,7 +1906,7 @@ int xfrm_state_update(struct xfrm_state *x) ...@@ -1906,7 +1906,7 @@ int xfrm_state_update(struct xfrm_state *x)
hrtimer_start(&x1->mtimer, ktime_set(1, 0), hrtimer_start(&x1->mtimer, ktime_set(1, 0),
HRTIMER_MODE_REL_SOFT); HRTIMER_MODE_REL_SOFT);
if (x1->curlft.use_time) if (READ_ONCE(x1->curlft.use_time))
xfrm_state_check_expire(x1); xfrm_state_check_expire(x1);
if (x->props.smark.m || x->props.smark.v || x->if_id) { 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) ...@@ -1940,8 +1940,8 @@ int xfrm_state_check_expire(struct xfrm_state *x)
{ {
xfrm_dev_state_update_curlft(x); xfrm_dev_state_update_curlft(x);
if (!x->curlft.use_time) if (!READ_ONCE(x->curlft.use_time))
x->curlft.use_time = ktime_get_real_seconds(); WRITE_ONCE(x->curlft.use_time, ktime_get_real_seconds());
if (x->curlft.bytes >= x->lft.hard_byte_limit || if (x->curlft.bytes >= x->lft.hard_byte_limit ||
x->curlft.packets >= x->lft.hard_packet_limit) { x->curlft.packets >= x->lft.hard_packet_limit) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment