ipsec-2025-01-27
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEH7ZpcWbFyOOp6OJbrB3Eaf9PW7cFAmeXIF4ACgkQrB3Eaf9P W7fRMA/+Js2x0HNA3+6SMb5nJzY6lywi1BIRAzstyfd6EsxbHlgfdWYCCpixboA0 /ZfDe7yPND/ewPIQLT9eO6hk9YzuAVhYUkdIcDC5jdFDNbh9dDqBdyu5P/5spsi9 9SdFEucoOsKBP4ejmSvtwGsVNIf/1vB8hFqYxB+vh8+d/g8PHrI3xxk+2b7KkIGS ms+IyDCoVdCGQUOp4BGtEQbzXtx67diH5dcfwg8/DJpSMbfqO3ZFRG7gPu8C5Igt cxVSCW67rv/zzPkGPv8B+nczAdVUZ3OFXgEWxdDCN/mUbFKwxUcIDxZVJMfBBAUP lcjsbzmNfj2PNMLZFe/5LuU6o+sFEZdxmTPmvbb+lSYrRHx2oz2/Jb871gEj8rTC vNZ+1Lu1k7QRjEPiO1fe85vWdmU4G81+WAzC88nD0KYLDUN4c+MmxUFQkKbAxf6p e6VCihcKqi5Sa6R73Ohm87iyiSuv8WvkyVSM0XgQrkXWDFy5Jp2Bo25pW0QgVxK+ l/aHhDA+YHFEOZTcjZsh/EdKlQRIxBNJ3ualITkjd2T+A1WyWm0A3S+kYZQCKqiM WGGWM3oVNXkUAaRxvURNvmXqO+hPeKfIElDeVrOUjG8zQ+EktKcg4KpDQb2BGJCj s9ksFj0pplR4GHxUrFmkEPxJWYKpFqUYCZMJDnBnHFm1ykC7QGM= =pg+h -----END PGP SIGNATURE----- Merge tag 'ipsec-2025-01-27' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec Steffen Klassert says: ==================== pull request (net): ipsec 2025-01-27 1) Fix incrementing the upper 32 bit sequence numbers for GSO skbs. From Jianbo Liu. 2) Fix an out-of-bounds read on xfrm state lookup. From Florian Westphal. 3) Fix secpath handling on packet offload mode. From Alexandre Cassen. 4) Fix the usage of skb->sk in the xfrm layer. 5) Don't disable preemption while looking up cache state to fix PREEMPT_RT. From Sebastian Sewior. * tag 'ipsec-2025-01-27' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec: xfrm: Don't disable preemption while looking up cache state. xfrm: Fix the usage of skb->sk xfrm: delete intermediate secpath entry in packet offload mode xfrm: state: fix out-of-bounds read during lookup xfrm: replay: Fix the update of replay_esn->oseq_hi for GSO ==================== Link: https://patch.msgid.link/20250127060757.3946314-1-steffen.klassert@secunet.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
463ec95a16
|
@ -1268,9 +1268,19 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir,
|
|||
|
||||
if (xo) {
|
||||
x = xfrm_input_state(skb);
|
||||
if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
|
||||
return (xo->flags & CRYPTO_DONE) &&
|
||||
(xo->status & CRYPTO_SUCCESS);
|
||||
if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
|
||||
bool check = (xo->flags & CRYPTO_DONE) &&
|
||||
(xo->status & CRYPTO_SUCCESS);
|
||||
|
||||
/* The packets here are plain ones and secpath was
|
||||
* needed to indicate that hardware already handled
|
||||
* them and there is no need to do nothing in addition.
|
||||
*
|
||||
* Consume secpath which was set by drivers.
|
||||
*/
|
||||
secpath_reset(skb);
|
||||
return check;
|
||||
}
|
||||
}
|
||||
|
||||
return __xfrm_check_nopolicy(net, skb, dir) ||
|
||||
|
|
|
@ -279,7 +279,7 @@ static void esp_output_done(void *data, int err)
|
|||
x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP)
|
||||
esp_output_tail_tcp(x, skb);
|
||||
else
|
||||
xfrm_output_resume(skb->sk, skb, err);
|
||||
xfrm_output_resume(skb_to_full_sk(skb), skb, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -315,7 +315,7 @@ static void esp_output_done(void *data, int err)
|
|||
x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP)
|
||||
esp_output_tail_tcp(x, skb);
|
||||
else
|
||||
xfrm_output_resume(skb->sk, skb, err);
|
||||
xfrm_output_resume(skb_to_full_sk(skb), skb, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,14 +82,14 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|||
|
||||
toobig = skb->len > mtu && !skb_is_gso(skb);
|
||||
|
||||
if (toobig && xfrm6_local_dontfrag(skb->sk)) {
|
||||
if (toobig && xfrm6_local_dontfrag(sk)) {
|
||||
xfrm6_local_rxpmtu(skb, mtu);
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
} else if (toobig && xfrm6_noneed_fragment(skb)) {
|
||||
skb->ignore_df = 1;
|
||||
goto skip_frag;
|
||||
} else if (!skb->ignore_df && toobig && skb->sk) {
|
||||
} else if (!skb->ignore_df && toobig && sk) {
|
||||
xfrm_local_error(skb, mtu);
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
|
|
|
@ -506,7 +506,7 @@ xmit:
|
|||
skb_dst_set(skb, dst);
|
||||
skb->dev = tdev;
|
||||
|
||||
err = dst_output(xi->net, skb->sk, skb);
|
||||
err = dst_output(xi->net, skb_to_full_sk(skb), skb);
|
||||
if (net_xmit_eval(err) == 0) {
|
||||
dev_sw_netstats_tx_add(dev, 1, length);
|
||||
} else {
|
||||
|
|
|
@ -802,7 +802,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
|
|||
!skb_gso_validate_network_len(skb, ip_skb_dst_mtu(skb->sk, skb)))) {
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
|
||||
if (skb->sk)
|
||||
if (skb->sk && sk_fullsock(skb->sk))
|
||||
xfrm_local_error(skb, mtu);
|
||||
else
|
||||
icmp_send(skb, ICMP_DEST_UNREACH,
|
||||
|
@ -838,6 +838,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
|
|||
{
|
||||
int mtu, ret = 0;
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct sock *sk = skb_to_full_sk(skb);
|
||||
|
||||
if (skb->ignore_df)
|
||||
goto out;
|
||||
|
@ -852,9 +853,9 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
|
|||
skb->dev = dst->dev;
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
|
||||
if (xfrm6_local_dontfrag(skb->sk))
|
||||
if (xfrm6_local_dontfrag(sk))
|
||||
ipv6_stub->xfrm6_local_rxpmtu(skb, mtu);
|
||||
else if (skb->sk)
|
||||
else if (sk)
|
||||
xfrm_local_error(skb, mtu);
|
||||
else
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
|
||||
|
|
|
@ -2964,7 +2964,7 @@ static void xfrm_policy_queue_process(struct timer_list *t)
|
|||
skb_dst_drop(skb);
|
||||
skb_dst_set(skb, dst);
|
||||
|
||||
dst_output(net, skb->sk, skb);
|
||||
dst_output(net, skb_to_full_sk(skb), skb);
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
@ -714,10 +714,12 @@ static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff
|
|||
oseq += skb_shinfo(skb)->gso_segs;
|
||||
}
|
||||
|
||||
if (unlikely(xo->seq.low < replay_esn->oseq)) {
|
||||
XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi;
|
||||
xo->seq.hi = oseq_hi;
|
||||
replay_esn->oseq_hi = oseq_hi;
|
||||
if (unlikely(oseq < replay_esn->oseq)) {
|
||||
replay_esn->oseq_hi = ++oseq_hi;
|
||||
if (xo->seq.low < replay_esn->oseq) {
|
||||
XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
|
||||
xo->seq.hi = oseq_hi;
|
||||
}
|
||||
if (replay_esn->oseq_hi == 0) {
|
||||
replay_esn->oseq--;
|
||||
replay_esn->oseq_hi--;
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
#define xfrm_state_deref_prot(table, net) \
|
||||
rcu_dereference_protected((table), lockdep_is_held(&(net)->xfrm.xfrm_state_lock))
|
||||
#define xfrm_state_deref_check(table, net) \
|
||||
rcu_dereference_check((table), lockdep_is_held(&(net)->xfrm.xfrm_state_lock))
|
||||
|
||||
static void xfrm_state_gc_task(struct work_struct *work);
|
||||
|
||||
|
@ -62,6 +64,8 @@ static inline unsigned int xfrm_dst_hash(struct net *net,
|
|||
u32 reqid,
|
||||
unsigned short family)
|
||||
{
|
||||
lockdep_assert_held(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask);
|
||||
}
|
||||
|
||||
|
@ -70,6 +74,8 @@ static inline unsigned int xfrm_src_hash(struct net *net,
|
|||
const xfrm_address_t *saddr,
|
||||
unsigned short family)
|
||||
{
|
||||
lockdep_assert_held(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
|
||||
}
|
||||
|
||||
|
@ -77,11 +83,15 @@ static inline unsigned int
|
|||
xfrm_spi_hash(struct net *net, const xfrm_address_t *daddr,
|
||||
__be32 spi, u8 proto, unsigned short family)
|
||||
{
|
||||
lockdep_assert_held(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
|
||||
}
|
||||
|
||||
static unsigned int xfrm_seq_hash(struct net *net, u32 seq)
|
||||
{
|
||||
lockdep_assert_held(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
return __xfrm_seq_hash(seq, net->xfrm.state_hmask);
|
||||
}
|
||||
|
||||
|
@ -1108,16 +1118,38 @@ xfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl,
|
|||
x->props.family = tmpl->encap_family;
|
||||
}
|
||||
|
||||
static struct xfrm_state *__xfrm_state_lookup_all(struct net *net, u32 mark,
|
||||
struct xfrm_hash_state_ptrs {
|
||||
const struct hlist_head *bydst;
|
||||
const struct hlist_head *bysrc;
|
||||
const struct hlist_head *byspi;
|
||||
unsigned int hmask;
|
||||
};
|
||||
|
||||
static void xfrm_hash_ptrs_get(const struct net *net, struct xfrm_hash_state_ptrs *ptrs)
|
||||
{
|
||||
unsigned int sequence;
|
||||
|
||||
do {
|
||||
sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
|
||||
|
||||
ptrs->bydst = xfrm_state_deref_check(net->xfrm.state_bydst, net);
|
||||
ptrs->bysrc = xfrm_state_deref_check(net->xfrm.state_bysrc, net);
|
||||
ptrs->byspi = xfrm_state_deref_check(net->xfrm.state_byspi, net);
|
||||
ptrs->hmask = net->xfrm.state_hmask;
|
||||
} while (read_seqcount_retry(&net->xfrm.xfrm_state_hash_generation, sequence));
|
||||
}
|
||||
|
||||
static struct xfrm_state *__xfrm_state_lookup_all(const struct xfrm_hash_state_ptrs *state_ptrs,
|
||||
u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
__be32 spi, u8 proto,
|
||||
unsigned short family,
|
||||
struct xfrm_dev_offload *xdo)
|
||||
{
|
||||
unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
|
||||
unsigned int h = __xfrm_spi_hash(daddr, spi, proto, family, state_ptrs->hmask);
|
||||
struct xfrm_state *x;
|
||||
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) {
|
||||
hlist_for_each_entry_rcu(x, state_ptrs->byspi + h, byspi) {
|
||||
#ifdef CONFIG_XFRM_OFFLOAD
|
||||
if (xdo->type == XFRM_DEV_OFFLOAD_PACKET) {
|
||||
if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
|
||||
|
@ -1151,15 +1183,16 @@ static struct xfrm_state *__xfrm_state_lookup_all(struct net *net, u32 mark,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
|
||||
static struct xfrm_state *__xfrm_state_lookup(const struct xfrm_hash_state_ptrs *state_ptrs,
|
||||
u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
__be32 spi, u8 proto,
|
||||
unsigned short family)
|
||||
{
|
||||
unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
|
||||
unsigned int h = __xfrm_spi_hash(daddr, spi, proto, family, state_ptrs->hmask);
|
||||
struct xfrm_state *x;
|
||||
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) {
|
||||
hlist_for_each_entry_rcu(x, state_ptrs->byspi + h, byspi) {
|
||||
if (x->props.family != family ||
|
||||
x->id.spi != spi ||
|
||||
x->id.proto != proto ||
|
||||
|
@ -1181,11 +1214,11 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
|
|||
__be32 spi, u8 proto,
|
||||
unsigned short family)
|
||||
{
|
||||
struct xfrm_hash_state_ptrs state_ptrs;
|
||||
struct hlist_head *state_cache_input;
|
||||
struct xfrm_state *x = NULL;
|
||||
int cpu = get_cpu();
|
||||
|
||||
state_cache_input = per_cpu_ptr(net->xfrm.state_cache_input, cpu);
|
||||
state_cache_input = raw_cpu_ptr(net->xfrm.state_cache_input);
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) {
|
||||
|
@ -1202,7 +1235,9 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
|
|||
goto out;
|
||||
}
|
||||
|
||||
x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family);
|
||||
xfrm_hash_ptrs_get(net, &state_ptrs);
|
||||
|
||||
x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family);
|
||||
|
||||
if (x && x->km.state == XFRM_STATE_VALID) {
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
|
@ -1217,20 +1252,20 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
|
|||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
put_cpu();
|
||||
return x;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_input_state_lookup);
|
||||
|
||||
static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
||||
static struct xfrm_state *__xfrm_state_lookup_byaddr(const struct xfrm_hash_state_ptrs *state_ptrs,
|
||||
u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
u8 proto, unsigned short family)
|
||||
{
|
||||
unsigned int h = xfrm_src_hash(net, daddr, saddr, family);
|
||||
unsigned int h = __xfrm_src_hash(daddr, saddr, family, state_ptrs->hmask);
|
||||
struct xfrm_state *x;
|
||||
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_bysrc + h, bysrc) {
|
||||
hlist_for_each_entry_rcu(x, state_ptrs->bysrc + h, bysrc) {
|
||||
if (x->props.family != family ||
|
||||
x->id.proto != proto ||
|
||||
!xfrm_addr_equal(&x->id.daddr, daddr, family) ||
|
||||
|
@ -1250,14 +1285,17 @@ static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
|||
static inline struct xfrm_state *
|
||||
__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
|
||||
{
|
||||
struct xfrm_hash_state_ptrs state_ptrs;
|
||||
struct net *net = xs_net(x);
|
||||
u32 mark = x->mark.v & x->mark.m;
|
||||
|
||||
xfrm_hash_ptrs_get(net, &state_ptrs);
|
||||
|
||||
if (use_spi)
|
||||
return __xfrm_state_lookup(net, mark, &x->id.daddr,
|
||||
return __xfrm_state_lookup(&state_ptrs, mark, &x->id.daddr,
|
||||
x->id.spi, x->id.proto, family);
|
||||
else
|
||||
return __xfrm_state_lookup_byaddr(net, mark,
|
||||
return __xfrm_state_lookup_byaddr(&state_ptrs, mark,
|
||||
&x->id.daddr,
|
||||
&x->props.saddr,
|
||||
x->id.proto, family);
|
||||
|
@ -1331,6 +1369,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
|||
unsigned short family, u32 if_id)
|
||||
{
|
||||
static xfrm_address_t saddr_wildcard = { };
|
||||
struct xfrm_hash_state_ptrs state_ptrs;
|
||||
struct net *net = xp_net(pol);
|
||||
unsigned int h, h_wildcard;
|
||||
struct xfrm_state *x, *x0, *to_put;
|
||||
|
@ -1395,8 +1434,10 @@ cached:
|
|||
else if (acquire_in_progress) /* XXX: acquire_in_progress should not happen */
|
||||
WARN_ON(1);
|
||||
|
||||
h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
|
||||
xfrm_hash_ptrs_get(net, &state_ptrs);
|
||||
|
||||
h = __xfrm_dst_hash(daddr, saddr, tmpl->reqid, encap_family, state_ptrs.hmask);
|
||||
hlist_for_each_entry_rcu(x, state_ptrs.bydst + h, bydst) {
|
||||
#ifdef CONFIG_XFRM_OFFLOAD
|
||||
if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
|
||||
if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
|
||||
|
@ -1429,8 +1470,9 @@ cached:
|
|||
if (best || acquire_in_progress)
|
||||
goto found;
|
||||
|
||||
h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family);
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h_wildcard, bydst) {
|
||||
h_wildcard = __xfrm_dst_hash(daddr, &saddr_wildcard, tmpl->reqid,
|
||||
encap_family, state_ptrs.hmask);
|
||||
hlist_for_each_entry_rcu(x, state_ptrs.bydst + h_wildcard, bydst) {
|
||||
#ifdef CONFIG_XFRM_OFFLOAD
|
||||
if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
|
||||
if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
|
||||
|
@ -1468,7 +1510,7 @@ found:
|
|||
|
||||
if (!x && !error && !acquire_in_progress) {
|
||||
if (tmpl->id.spi &&
|
||||
(x0 = __xfrm_state_lookup_all(net, mark, daddr,
|
||||
(x0 = __xfrm_state_lookup_all(&state_ptrs, mark, daddr,
|
||||
tmpl->id.spi, tmpl->id.proto,
|
||||
encap_family,
|
||||
&pol->xdo)) != NULL) {
|
||||
|
@ -2253,10 +2295,13 @@ struct xfrm_state *
|
|||
xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi,
|
||||
u8 proto, unsigned short family)
|
||||
{
|
||||
struct xfrm_hash_state_ptrs state_ptrs;
|
||||
struct xfrm_state *x;
|
||||
|
||||
rcu_read_lock();
|
||||
x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family);
|
||||
xfrm_hash_ptrs_get(net, &state_ptrs);
|
||||
|
||||
x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family);
|
||||
rcu_read_unlock();
|
||||
return x;
|
||||
}
|
||||
|
@ -2267,10 +2312,14 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
|||
const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
u8 proto, unsigned short family)
|
||||
{
|
||||
struct xfrm_hash_state_ptrs state_ptrs;
|
||||
struct xfrm_state *x;
|
||||
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
x = __xfrm_state_lookup_byaddr(net, mark, daddr, saddr, proto, family);
|
||||
|
||||
xfrm_hash_ptrs_get(net, &state_ptrs);
|
||||
|
||||
x = __xfrm_state_lookup_byaddr(&state_ptrs, mark, daddr, saddr, proto, family);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
return x;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue