mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 20:54:03 +02:00
ipsec-2026-09-16
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEH7ZpcWbFyOOp6OJbrB3Eaf9PW7cFAmqqagcACgkQrB3Eaf9P
W7ervw//ValStkQ37oXUHXQkevKs+rAFhleUHogbxG+C/45nx7ToVgtL0oRFs/ss
WmxjClmthosPdcgz9uLyIi/xiIWmwa9iYsmq/A7FF5bewz8ha0/jzkxi6EZvGFxK
oHJWIe11pcDV3NoEjA33Z6k63vZcVhD3QVlIk4mg4b4leEDgTw6y8z/k5sv6/QTc
xP7lykI6VeKPyBydSAdwHonJZ3BWLE0/Y2uiQtVVZAFT9Ln2cpqAPpwYGDFKpgM/
H8NYqB4X3fhdxc/rQhqdgS0mdhEwukulyYg/znwUI/DYb5Dt4Eh51y0fpIcjFX2y
Gp31bMXih7j66SuOwEXx48dSNgxHEXwbUdUoeQReVmFam0epbOyPoBgT5ZLskWfo
JbdeZR7kAugCnX/XpTmI8pk3G52i6LvCY1GDPyIYTFGcVHg9Y9plw1WEs88pqGNC
9+pkRapF04gdaxjl4FioKdtoKXTyYiVxEB/nWkBfUE9Q5iS16/G9Lrcd4Dw3aN8f
imsTf1yW6kJ6RbF9QKS4kuzVJnPFwxg/EmmHFQtpZOkH23BwZEU7obqIwpNYgnoG
/A9vnwQdAxGU4ar6n2wSPXABzFYlsJAwyMlOGrOkzWC9t/TAA5NHjkyskO5pY8Mt
0bhWCiNfRYzbSF94UEltYsKZKrx9kV6c2lCltKCfR89KhY5Dorw=
=skHa
-----END PGP SIGNATURE-----
Merge tag 'ipsec-2026-09-16' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
Steffen Klassert says:
====================
pull request (net): ipsec 2026-09-16
1) xfrm: iptfs: fix stack OOB read in iptfs_skb_reset_frag_walk()
Add the up-front nr_frags guard iptfs_skb_add_frags() already has,
so an out-of-range offset can't walk past the on-stack frags[] array.
2) xfrm: serialize state GC with device state flush
Serialize xfrm_state destruction against the deferred-device pass
with a dedicated mutex, since the device GC list doesn't hold a state
reference and the two paths could free the same state.
3) xfrm: add missing RCU read lock in xfrm_send_migrate_state()
Hold the RCU read lock around xfrm_nlmsg_multicast() so the
rcu_dereference() of net->xfrm.nlsk doesn't warn.
4) xfrm: iptfs: fix runt reassembly panic from short inner tot_len
Require the runt length to cover at least the minimum IP header,
so a tot_len in [6, 19] (IPv4) can't write past the declared length
and trip skb_over_panic().
5) ipv6: xfrm: use full sockets in local error paths
Use skb_to_full_sk() in xfrm6_local_rxpmtu() and xfrm6_local_error()
and bail out without a full socket, so a TCP_NEW_SYN_RECV request_sock
isn't miscast as a full inet/IPv6 socket.
6) xfrm: fix compat ALLOCSPI request use-after-free
Drop the redundant alloc_compat() in xfrm_alloc_userspi() so the
compat translator no longer reads past the payload and publishes a
child a multicast clone can still see after xfrm_user_rcv_msg() frees.
7) xfrm: add missing rcu_read_lock(), skb_dst_force() and dev_hold() for xfrm_trans_reinject()
Force the dst before queuing, hold dev across the workqueue deferral,
and take rcu_read_lock() around the finish() loop, so transport-mode
reinjection doesn't deref non-refcounted dst/dev under workqueue.
8) xfrm: use hlist_del_init_rcu for state_cache and state_cache_input
Switch to hlist_del_init_rcu() so a second __xfrm_state_delete() is
a no-op instead of writing through LIST_POISON2, closing the UAFs.
9) esp: downgrade zerocopy managed frags before mutating skb frags
Call skb_zcopy_downgrade_managed() before ESP rewrites the skb frag
array, so per-frag unrefs in esp_ssg_unref() and skb_release_data()
stay balanced for ubuf-owned managed frags.
10) xfrm: hold net_device reference under RCU in bundle creation
Read dst->dev via dst_dev_rcu() and keep RCU active through
xfrm_fill_dst(), so a concurrent RTM_DELLINK can't free dev
under bundle creation.
11) xfrm: save input state data before secpath resets
Save the state protocol on the stack while it's still valid and
use the saved address family for transport_finish(), so post-reset
dereferences (VTI, XFRM if, MAX_DEPTH error) can't UAF the state.
12) net: xfrm: reject unrepresentable espintcp transport headers
Use the careful transport-header helper and drop the skb through
the XFRM error path when the offset can't be represented, instead
of silently truncating it.
* tag 'ipsec-2026-09-16' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec:
net: xfrm: reject unrepresentable espintcp transport headers
xfrm: save input state data before secpath resets
xfrm: hold net_device reference under RCU in bundle creation
esp: downgrade zerocopy managed frags before mutating skb frags
xfrm: use hlist_del_init_rcu for state_cache and state_cache_input
xfrm: add missing rcu_read_lock(), skb_dst_force() and dev_hold() for xfrm_trans_reinject()
xfrm: fix compat ALLOCSPI request use-after-free
ipv6: xfrm: use full sockets in local error paths
xfrm: iptfs: fix runt reassembly panic from short inner tot_len
xfrm: add missing RCU read lock in xfrm_send_migrate_state()
xfrm: serialize state GC with device state flush
xfrm: iptfs: fix stack OOB read in iptfs_skb_reset_frag_walk()
====================
Link: https://patch.msgid.link/20260916101938.118628-1-steffen.klassert@secunet.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
7c7d5e9d7e
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
|||
} 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 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
|||
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 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
|
|||
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 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
|
|||
|
||||
}
|
||||
spin_unlock_bh(&xfrm_state_dev_gc_lock);
|
||||
mutex_unlock(&xfrm_state_gc_mutex);
|
||||
|
||||
xfrm_flush_gc();
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user