summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 12:55:54 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 12:55:54 +0100
commit0d25bb4b61942da29f8ce7ec3e1cdcb9bb397769 (patch)
tree8b3f4bd4ae897aae6f0ccfd2ffcc1e5d4ceff0ab
parent889a52b97ff1a0c8c8e9b52bc1acce33158e12f5 (diff)
parent96f01b53c2d05e003b040892256de54a586e8529 (diff)
downloadlinux-next-0d25bb4b61942da29f8ce7ec3e1cdcb9bb397769.tar.gz
linux-next-0d25bb4b61942da29f8ce7ec3e1cdcb9bb397769.zip
Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec.git
-rw-r--r--net/ipv4/esp4.c6
-rw-r--r--net/ipv6/esp6.c6
-rw-r--r--net/ipv6/xfrm6_output.c10
-rw-r--r--net/xfrm/espintcp.c6
-rw-r--r--net/xfrm/xfrm_input.c22
-rw-r--r--net/xfrm/xfrm_iptfs.c12
-rw-r--r--net/xfrm/xfrm_policy.c20
-rw-r--r--net/xfrm/xfrm_state.c9
-rw-r--r--net/xfrm/xfrm_user.c18
9 files changed, 81 insertions, 28 deletions
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index a6c18aea7498..e76db5817e78 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -441,6 +441,12 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
esp->inplace = false;
+ /* Take real page refs and clear SKBFL_MANAGED_FRAG_REFS before
+ * we mutate the frag array, so the per-frag unref stays balanced
+ * for zerocopy managed frags (see __ip_append_data()).
+ */
+ skb_zcopy_downgrade_managed(skb);
+
allocsize = ALIGN(tailen, L1_CACHE_BYTES);
spin_lock_bh(&x->lock);
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 72ec0d7d1120..b1c9b36f76dc 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -471,6 +471,12 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
esp->inplace = false;
+ /* Take real page refs and clear SKBFL_MANAGED_FRAG_REFS before
+ * we mutate the frag array, so the per-frag unref stays balanced
+ * for zerocopy managed frags (see __ip_append_data()).
+ */
+ skb_zcopy_downgrade_managed(skb);
+
allocsize = ALIGN(tailen, L1_CACHE_BYTES);
spin_lock_bh(&x->lock);
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 512bdaf13699..44b221a09a0c 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -19,7 +19,10 @@
void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
{
struct flowi6 fl6;
- struct sock *sk = skb->sk;
+ struct sock *sk = skb_to_full_sk(skb);
+
+ if (!sk)
+ return;
fl6.flowi6_oif = sk->sk_bound_dev_if;
fl6.daddr = ipv6_hdr(skb)->daddr;
@@ -31,7 +34,10 @@ void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
{
struct flowi6 fl6;
const struct ipv6hdr *hdr;
- struct sock *sk = skb->sk;
+ struct sock *sk = skb_to_full_sk(skb);
+
+ if (!sk)
+ return;
hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
fl6.fl6_dport = inet_sk(sk)->inet_dport;
diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c
index 674aedc5af5a..3e72b9f067b9 100644
--- a/net/xfrm/espintcp.c
+++ b/net/xfrm/espintcp.c
@@ -30,7 +30,11 @@ static void handle_esp(struct sk_buff *skb, struct sock *sk)
{
struct tcp_skb_cb *tcp_cb = (struct tcp_skb_cb *)skb->cb;
- skb_reset_transport_header(skb);
+ if (!skb_reset_transport_header_careful(skb)) {
+ XFRM_INC_STATS(sock_net(sk), LINUX_MIB_XFRMINERROR);
+ kfree_skb(skb);
+ return;
+ }
/* restore IP CB, we need at least IP6CB->nhoff */
memmove(skb->cb, &tcp_cb->header, sizeof(tcp_cb->header));
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index eecab337bd0a..5ed87d51392a 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -474,6 +474,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
struct xfrm_state *x = NULL;
xfrm_address_t *daddr;
u32 mark = skb->mark;
+ u8 xfrm_proto = nexthdr;
unsigned int family = AF_UNSPEC;
int decaps = 0;
int async = 0;
@@ -485,6 +486,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
if (encap_type < 0 || (xo && (xo->flags & XFRM_GRO || encap_type == 0 ||
encap_type == UDP_ENCAP_ESPINUDP))) {
x = xfrm_input_state(skb);
+ xfrm_proto = x->type ? x->type->proto : nexthdr;
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
if (x->km.state == XFRM_STATE_ACQ)
@@ -592,11 +594,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
x = xfrm_input_state_lookup(net, mark, daddr, spi, nexthdr, family);
if (x == NULL) {
+ xfrm_proto = nexthdr;
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
xfrm_audit_state_notfound(skb, family, spi, seq);
goto drop;
}
+ xfrm_proto = x->type ? x->type->proto : nexthdr;
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
secpath_reset(skb);
@@ -604,6 +608,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
xfrm_audit_state_notfound(skb, family, spi, seq);
xfrm_state_put(x);
x = NULL;
+ xfrm_proto = nexthdr;
goto drop;
}
@@ -728,7 +733,7 @@ resume_decapped:
} while (!err);
rcu_read_lock();
- err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
+ err = xfrm_rcv_cb(skb, family, xfrm_proto, 0);
if (err) {
rcu_read_unlock();
goto drop;
@@ -753,7 +758,7 @@ resume_decapped:
xfrm_gro = xo->flags & XFRM_GRO;
err = -EAFNOSUPPORT;
- afinfo = xfrm_state_afinfo_get_rcu(x->props.family);
+ afinfo = xfrm_state_afinfo_get_rcu(family);
if (likely(afinfo))
err = afinfo->transport_finish(skb, xfrm_gro || async);
if (xfrm_gro) {
@@ -776,7 +781,7 @@ drop_unlock:
drop:
if (async)
dev_put(dev);
- xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
+ xfrm_rcv_cb(skb, family, xfrm_proto, -1);
kfree_skb(skb);
return 0;
}
@@ -800,12 +805,17 @@ static void xfrm_trans_reinject(struct work_struct *work)
spin_unlock_bh(&trans->queue_lock);
local_bh_disable();
+ rcu_read_lock();
while ((skb = __skb_dequeue(&queue))) {
struct net *net = XFRM_TRANS_SKB_CB(skb)->net;
+ struct net_device *dev = skb->dev;
XFRM_TRANS_SKB_CB(skb)->finish(net, NULL, skb);
+ if (dev)
+ dev_put(dev);
put_net(net);
}
+ rcu_read_unlock();
local_bh_enable();
}
@@ -821,12 +831,18 @@ int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,
if (skb_queue_len(&trans->queue) >= READ_ONCE(net_hotdata.max_backlog))
return -ENOBUFS;
+ if (skb_dst(skb) && !skb_dst_force(skb))
+ return -EHOSTUNREACH;
+
BUILD_BUG_ON(sizeof(struct xfrm_trans_cb) > sizeof(skb->cb));
hold_net = maybe_get_net(net);
if (!hold_net)
return -ENODEV;
+ if (skb->dev)
+ dev_hold(skb->dev);
+
XFRM_TRANS_SKB_CB(skb)->finish = finish;
XFRM_TRANS_SKB_CB(skb)->net = hold_net;
spin_lock_bh(&trans->queue_lock);
diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
index 597aedeac26e..6920940a35b4 100644
--- a/net/xfrm/xfrm_iptfs.c
+++ b/net/xfrm/xfrm_iptfs.c
@@ -416,6 +416,14 @@ static bool iptfs_skb_can_add_frags(const struct sk_buff *skb,
if (skb_has_frag_list(skb) || skb->pp_recycle != walk->pp_recycle)
return false;
+ /* Reject an @offset that is at or beyond the end of the walk's data
+ * before calling iptfs_skb_reset_frag_walk(), whose fragment-advance
+ * loop is otherwise unbounded and would index past walk->frags[].
+ * This mirrors the guard already present in iptfs_skb_add_frags().
+ */
+ if (!walk->nr_frags || offset >= walk->total + walk->initial_offset)
+ return false;
+
/* Make offset relative to current frag after setting that */
offset = iptfs_skb_reset_frag_walk(walk, offset);
@@ -820,8 +828,8 @@ static u32 iptfs_reassem_cont(struct xfrm_iptfs_data *xtfs, u64 seq,
* allocate an in progress skb
*/
ipremain = __iptfs_iplen(xtfs->ra_runt);
- if (ipremain < sizeof(xtfs->ra_runt)) {
- /* length has to be at least runtsize large */
+ if (ipremain < __iptfs_iphlen(xtfs->ra_runt)) {
+ /* length has to be at least the IP header size */
XFRM_INC_STATS(xs_net(xtfs->x),
LINUX_MIB_XFRMINIPTFSERROR);
goto abandon;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 932a313b9460..513c9f228334 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2770,9 +2770,12 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
xdst0->path = dst;
err = -ENODEV;
- dev = dst->dev;
- if (!dev)
+ rcu_read_lock();
+ dev = dst_dev_rcu(dst);
+ if (!dev) {
+ rcu_read_unlock();
goto free_dst;
+ }
xfrm_init_path(xdst0, dst, nfheader_len);
xfrm_init_pmtu(bundle, nx);
@@ -2780,8 +2783,10 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
for (xdst_prev = xdst0; xdst_prev != (struct xfrm_dst *)dst;
xdst_prev = (struct xfrm_dst *) xfrm_dst_child(&xdst_prev->u.dst)) {
err = xfrm_fill_dst(xdst_prev, dev, fl);
- if (err)
+ if (err) {
+ rcu_read_unlock();
goto free_dst;
+ }
xdst_prev->u.dst.header_len = header_len;
xdst_prev->u.dst.trailer_len = trailer_len;
@@ -2789,6 +2794,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
trailer_len -= xdst_prev->u.dst.xfrm->props.trailer_len;
}
+ rcu_read_unlock();
return &xdst0->u.dst;
put_states:
@@ -3058,11 +3064,15 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
xfrm_init_path((struct xfrm_dst *)dst1, dst, 0);
err = -ENODEV;
- dev = dst->dev;
- if (!dev)
+ rcu_read_lock();
+ dev = dst_dev_rcu(dst);
+ if (!dev) {
+ rcu_read_unlock();
goto free_dst;
+ }
err = xfrm_fill_dst(xdst, dev, fl);
+ rcu_read_unlock();
if (err)
goto free_dst;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 36a4f6793ede..e45aa1ed5b96 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -226,6 +226,7 @@ static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO];
static DEFINE_SPINLOCK(xfrm_state_gc_lock);
static DEFINE_SPINLOCK(xfrm_state_dev_gc_lock);
+static DEFINE_MUTEX(xfrm_state_gc_mutex);
int __xfrm_state_delete(struct xfrm_state *x);
@@ -632,8 +633,10 @@ static void xfrm_state_gc_task(struct work_struct *work)
synchronize_rcu();
+ mutex_lock(&xfrm_state_gc_mutex);
hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
xfrm_state_gc_destroy(x);
+ mutex_unlock(&xfrm_state_gc_mutex);
}
static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
@@ -823,9 +826,9 @@ int __xfrm_state_delete(struct xfrm_state *x)
if (!hlist_unhashed(&x->byseq))
hlist_del_init_rcu(&x->byseq);
if (!hlist_unhashed(&x->state_cache))
- hlist_del_rcu(&x->state_cache);
+ hlist_del_init_rcu(&x->state_cache);
if (!hlist_unhashed(&x->state_cache_input))
- hlist_del_rcu(&x->state_cache_input);
+ hlist_del_init_rcu(&x->state_cache_input);
if (!hlist_unhashed(&x->byspi))
hlist_del_init_rcu(&x->byspi);
@@ -1000,6 +1003,7 @@ restart:
out:
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ mutex_lock(&xfrm_state_gc_mutex);
spin_lock_bh(&xfrm_state_dev_gc_lock);
restart_gc:
hlist_for_each_entry_safe(x, tmp, &xfrm_state_dev_gc_list, dev_gclist) {
@@ -1014,6 +1018,7 @@ restart_gc:
}
spin_unlock_bh(&xfrm_state_dev_gc_lock);
+ mutex_unlock(&xfrm_state_gc_mutex);
xfrm_flush_gc();
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 6266a92cf302..a2587c7e796b 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1877,7 +1877,6 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
struct xfrm_userspi_info *p;
- struct xfrm_translator *xtr;
struct sk_buff *resp_skb;
xfrm_address_t *daddr;
int family;
@@ -1943,17 +1942,6 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
}
- xtr = xfrm_get_translator();
- if (xtr) {
- err = xtr->alloc_compat(skb, nlmsg_hdr(skb));
-
- xfrm_put_translator(xtr);
- if (err) {
- kfree_skb(resp_skb);
- goto out;
- }
- }
-
err = nlmsg_unicast(xfrm_net_nlsk(net, skb), resp_skb, NETLINK_CB(skb).portid);
out:
@@ -3337,7 +3325,11 @@ static int xfrm_send_migrate_state(struct net *net,
return err;
}
- return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
+ rcu_read_lock();
+ err = xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
+ rcu_read_unlock();
+
+ return err;
}
static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,