mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
ipsec-2026-07-10
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEH7ZpcWbFyOOp6OJbrB3Eaf9PW7cFAmpQtBkACgkQrB3Eaf9P
W7fEwQ//ZGlVHLizm9aK0bcoIGOuUBCASQOmtk19n5vYP97612LbMeaQD9jEBCd2
c/gLK2P9hn9dnH25lvOXPwrWG+gje7lpPypeTjvzvW/andg/2DWGMvmhSv1M8a5q
J6NUKRsCJ2jwwkpILw6ziD+xskEh9t2hxXSc1Xk6K0W5MHJlx07ieoMrL/r/HbvJ
lDiHfg87Hlcp8K6W/aecN8NcliXEuo3KTSYCsBZM48R+Oar3VpqMC2QiLxvYZDqQ
IEVshugVQ8FQCgSp37hDxQLqsGdAfzAcjE8SjAGPJk12IODO8MKLsSsDJWt3MX4a
sjbIiQjEr0nDxtnwtPOBYN5ko5cpeU/A0mvAIHCTcJ36SFucii32i5hBABRV/WKh
gKAppFmXb4TL63heupYff3jlSWRSZKvlXc4R4ybMcIHHXVslK/9DGheEAYvVxTgh
NlV6wmj89cx/Xvzk/ODS4TlsnypIjcE+TINA5lGscRSNwtWYAeqBOxCK8fkSabka
aau0PTkoGaLHQWp2HwjIiJFrat/ZMJfsHWUvISeaKQ5ZJFtq++rE0alkgMpnfVmp
EOtvMdxrxYMPmeDVewH2XQseYWLFgdGzAf3i7MG1dHxEunqaoFCxSqzSnrKuFZLA
tbkHCjm2m2/oW3A0FdqmIIKrMLIDoBYjsEG5PXVYNqOnKpx/PEw=
=k1zg
-----END PGP SIGNATURE-----
Merge tag 'ipsec-2026-07-10' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
Steffen Klassert says:
====================
pull request (net): ipsec 2026-07-10
1) xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
Return -EINPROGRESS from xfrm_output_one when validate_xmit_xfrm
requeues the packet asynchronously, so the caller doesn't treat it
as a real error and free the skb.
2) xfrm: fix stale skb->prev after async crypto steals a GSO segment
Re-derive skb->prev from the fragment list after async crypto splits
a GSO skb, keeping the linked-list pointers validi.
3) xfrm: nat_keepalive: avoid double free on send error
Hold a state ref while the nat_keepalive timer is active and drop the
timer before freeing the state, preventing a re-entered free on send
error.
4) xfrm: fix sk_dst_cache double-free in xfrm_user_policy()
Null the skb dst cache before freeing the policy so a later skb
destructor doesn't double-free it.
5) xfrm: cache the offload ifindex for netlink dumps
Cache the device ifindex at state-add time and use it for netlink
dumps instead of dereferencing dst->dev, which may have changed by
the time the dump runs.
6) xfrm: reject optional IPTFS templates in outbound policies
Reject outbound policies with an optional IPTFS template,
IPTFS must always be used if configured.
7) xfrm: clear mode callbacks after failed mode setup
Clear the mode->init_flags and init_state callbacks on the error path
after xfrm_init_mode fails, so a partially-initialised mode isn't
reused in xfrm_state_construct.
8) xfrm: iptfs: propagate SKBFL_SHARED_FRAG in iptfs_skb_add_frags()
Propagate SKBFL_SHARED_FRAG from the original skb to fragments
allocated by iptfs_skb_add_frags, keeping shared-fragment accounting
correct after IPTFS reassembly.
9) xfrm6: clear dst.dev on error to avoid double netdev_put in xfrm6_fill_dst()
Clear dst->dev on the error path of xfrm6_fill_dst() so the caller
doesn't release the netdev reference twice via dst_release.
10) xfrm: policy: preallocate inexact bins before xfrm_hash_rebuild reinsert
Preallocate all inexact hash bins before existing entries are
reinserted during xfrm_hash_rebuild, so reinsertion always hits an
existing bin.
Please pull or let me know if there are problems.
ipsec-2026-07-10
* tag 'ipsec-2026-07-10' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec:
xfrm: policy: preallocate inexact bins before xfrm_hash_rebuild reinsert
xfrm6: clear dst.dev on error to avoid double netdev_put in xfrm6_fill_dst()
xfrm: iptfs: propagate SKBFL_SHARED_FRAG in iptfs_skb_add_frags()
xfrm: clear mode callbacks after failed mode setup
xfrm: reject optional IPTFS templates in outbound policies
xfrm: cache the offload ifindex for netlink dumps
xfrm: fix sk_dst_cache double-free in xfrm_user_policy()
xfrm: nat_keepalive: avoid double free on send error
xfrm: fix stale skb->prev after async crypto steals a GSO segment
xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
====================
Link: https://patch.msgid.link/20260710090349.343389-1-steffen.klassert@secunet.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
389704eb51
|
|
@ -162,6 +162,8 @@ struct xfrm_dev_offload {
|
|||
*/
|
||||
struct net_device *real_dev;
|
||||
unsigned long offload_handle;
|
||||
/* Snapshot the attached device index for dump paths. */
|
||||
int ifindex;
|
||||
u8 dir : 2;
|
||||
u8 type : 2;
|
||||
u8 flags : 2;
|
||||
|
|
|
|||
|
|
@ -4018,6 +4018,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
|
||||
* if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
|
||||
*/
|
||||
static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
|
||||
{
|
||||
netdev_features_t features;
|
||||
|
|
@ -4089,7 +4092,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
|
|||
skb->prev = skb;
|
||||
|
||||
skb = validate_xmit_skb(skb, dev, again);
|
||||
if (!skb)
|
||||
if (IS_ERR_OR_NULL(skb))
|
||||
continue;
|
||||
|
||||
if (!head)
|
||||
|
|
@ -4860,8 +4863,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
|
|||
goto recursion_alert;
|
||||
|
||||
skb = validate_xmit_skb(skb, dev, &again);
|
||||
if (!skb)
|
||||
if (IS_ERR_OR_NULL(skb)) {
|
||||
if (PTR_ERR(skb) == -EINPROGRESS)
|
||||
rc = NET_XMIT_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
HARD_TX_LOCK(dev, txq, cpu);
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
|
|||
xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
|
||||
if (!xdst->u.rt6.rt6i_idev) {
|
||||
netdev_put(dev, &xdst->u.dst.dev_tracker);
|
||||
xdst->u.dst.dev = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
|
|||
err = x->type_offload->xmit(x, skb, esp_features);
|
||||
if (err) {
|
||||
if (err == -EINPROGRESS)
|
||||
return NULL;
|
||||
return ERR_PTR(-EINPROGRESS);
|
||||
|
||||
XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
|
||||
kfree_skb(skb);
|
||||
|
|
@ -224,7 +224,15 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
|
|||
pskb = skb2;
|
||||
}
|
||||
|
||||
return skb;
|
||||
/* skb_gso_segment() set skb->prev to the last segment, but async
|
||||
* crypto may have stolen it above without updating ->prev. Repoint
|
||||
* it at the last retained segment so validate_xmit_skb_list() does
|
||||
* not chain onto a segment now owned by the crypto engine.
|
||||
*/
|
||||
if (skb)
|
||||
skb->prev = pskb;
|
||||
|
||||
return skb ? skb : ERR_PTR(-EINPROGRESS);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
|
||||
|
||||
|
|
@ -313,6 +321,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
|||
}
|
||||
|
||||
xso->dev = dev;
|
||||
xso->ifindex = dev->ifindex;
|
||||
netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC);
|
||||
|
||||
if (xuo->flags & XFRM_OFFLOAD_INBOUND)
|
||||
|
|
|
|||
|
|
@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb,
|
|||
}
|
||||
__skb_frag_ref(tofrag);
|
||||
shinfo->nr_frags++;
|
||||
shinfo->flags |= SKBFL_SHARED_FRAG;
|
||||
|
||||
/* see if we are done */
|
||||
fraglen = tofrag->len;
|
||||
|
|
|
|||
|
|
@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb,
|
|||
ka->encap_sport, sock_net_uid(net, NULL));
|
||||
|
||||
rt = ip_route_output_key(net, &fl4);
|
||||
if (IS_ERR(rt))
|
||||
if (IS_ERR(rt)) {
|
||||
kfree_skb(skb);
|
||||
return PTR_ERR(rt);
|
||||
}
|
||||
|
||||
skb_dst_set(skb, &rt->dst);
|
||||
|
||||
|
|
@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb,
|
|||
dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL);
|
||||
if (IS_ERR(dst)) {
|
||||
local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock);
|
||||
kfree_skb(skb);
|
||||
return PTR_ERR(dst);
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
|
|||
sizeof(struct ipv6hdr)) +
|
||||
sizeof(struct udphdr);
|
||||
const u8 nat_ka_payload = 0xFF;
|
||||
int err = -EAFNOSUPPORT;
|
||||
struct sk_buff *skb;
|
||||
struct udphdr *uh;
|
||||
|
||||
|
|
@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
|
|||
|
||||
switch (ka->family) {
|
||||
case AF_INET:
|
||||
err = nat_keepalive_send_ipv4(skb, ka);
|
||||
nat_keepalive_send_ipv4(skb, ka);
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case AF_INET6:
|
||||
err = nat_keepalive_send_ipv6(skb, ka, uh);
|
||||
nat_keepalive_send_ipv6(skb, ka, uh);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
if (err)
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct nat_keepalive_work_ctx {
|
||||
|
|
|
|||
|
|
@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
if (policy->selector.prefixlen_d < dbits ||
|
||||
policy->selector.prefixlen_s < sbits)
|
||||
if (policy->selector.prefixlen_d >= dbits &&
|
||||
policy->selector.prefixlen_s >= sbits)
|
||||
continue;
|
||||
|
||||
bin = xfrm_policy_inexact_alloc_bin(policy, dir);
|
||||
|
|
|
|||
|
|
@ -1547,6 +1547,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
|||
xso->type = XFRM_DEV_OFFLOAD_PACKET;
|
||||
xso->dir = xdo->dir;
|
||||
xso->dev = dev;
|
||||
xso->ifindex = dev->ifindex;
|
||||
xso->flags = XFRM_DEV_OFFLOAD_FLAG_ACQ;
|
||||
netdev_hold(dev, &xso->dev_tracker, GFP_ATOMIC);
|
||||
error = dev->xfrmdev_ops->xdo_dev_state_add(dev, x,
|
||||
|
|
@ -2071,8 +2072,11 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
|||
|
||||
x->mode_cbs = orig->mode_cbs;
|
||||
if (x->mode_cbs && x->mode_cbs->clone_state) {
|
||||
if (x->mode_cbs->clone_state(x, orig))
|
||||
if (x->mode_cbs->clone_state(x, orig)) {
|
||||
if (!x->mode_data)
|
||||
x->mode_cbs = NULL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
x->props.reqid = m->new_reqid;
|
||||
|
|
@ -3010,7 +3014,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
|
|||
if (sockptr_is_null(optval) && !optlen) {
|
||||
xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
|
||||
xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
|
||||
__sk_dst_reset(sk);
|
||||
sk_dst_reset(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3050,7 +3054,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
|
|||
if (err >= 0) {
|
||||
xfrm_sk_policy_insert(sk, err, pol);
|
||||
xfrm_pol_put(pol);
|
||||
__sk_dst_reset(sk);
|
||||
sk_dst_reset(sk);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
|
|
@ -3291,6 +3295,8 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
|
|||
if (x->mode_cbs->init_state)
|
||||
err = x->mode_cbs->init_state(x);
|
||||
module_put(x->mode_cbs->owner);
|
||||
if (err && !x->mode_data)
|
||||
x->mode_cbs = NULL;
|
||||
}
|
||||
error:
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -1201,17 +1201,26 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void xso_to_xuo(const struct xfrm_dev_offload *xso,
|
||||
struct xfrm_user_offload *xuo)
|
||||
static void xso_to_xuo_ifindex(const struct xfrm_dev_offload *xso, int ifindex,
|
||||
struct xfrm_user_offload *xuo)
|
||||
{
|
||||
xuo->ifindex = xso->dev->ifindex;
|
||||
xuo->ifindex = ifindex;
|
||||
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
|
||||
xuo->flags = XFRM_OFFLOAD_INBOUND;
|
||||
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
|
||||
xuo->flags |= XFRM_OFFLOAD_PACKET;
|
||||
}
|
||||
|
||||
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
|
||||
#ifdef CONFIG_XFRM_MIGRATE
|
||||
static void xso_to_xuo(const struct xfrm_dev_offload *xso,
|
||||
struct xfrm_user_offload *xuo)
|
||||
{
|
||||
xso_to_xuo_ifindex(xso, xso->dev->ifindex, xuo);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int copy_user_offload_ifindex(const struct xfrm_dev_offload *xso,
|
||||
int ifindex, struct sk_buff *skb)
|
||||
{
|
||||
struct xfrm_user_offload *xuo;
|
||||
struct nlattr *attr;
|
||||
|
|
@ -1222,11 +1231,22 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
|
|||
|
||||
xuo = nla_data(attr);
|
||||
memset(xuo, 0, sizeof(*xuo));
|
||||
xso_to_xuo(xso, xuo);
|
||||
xso_to_xuo_ifindex(xso, ifindex, xuo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
|
||||
{
|
||||
return copy_user_offload_ifindex(xso, xso->dev->ifindex, skb);
|
||||
}
|
||||
|
||||
static int copy_user_state_offload(const struct xfrm_dev_offload *xso,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return copy_user_offload_ifindex(xso, READ_ONCE(xso->ifindex), skb);
|
||||
}
|
||||
|
||||
static bool xfrm_redact(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_SECURITY) &&
|
||||
|
|
@ -1433,8 +1453,8 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
|
|||
&x->replay);
|
||||
if (ret)
|
||||
goto out;
|
||||
if(x->xso.dev)
|
||||
ret = copy_user_offload(&x->xso, skb);
|
||||
if (READ_ONCE(x->xso.dev))
|
||||
ret = copy_user_state_offload(&x->xso, skb);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (x->if_id) {
|
||||
|
|
@ -2104,13 +2124,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family,
|
|||
switch (ut[i].mode) {
|
||||
case XFRM_MODE_TUNNEL:
|
||||
case XFRM_MODE_BEET:
|
||||
case XFRM_MODE_IPTFS:
|
||||
if (ut[i].optional && dir == XFRM_POLICY_OUT) {
|
||||
NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case XFRM_MODE_IPTFS:
|
||||
break;
|
||||
default:
|
||||
if (ut[i].family != prev_family) {
|
||||
NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change");
|
||||
|
|
@ -4046,8 +4065,8 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
|
|||
l += nla_total_size(sizeof(*x->coaddr));
|
||||
if (x->props.extra_flags)
|
||||
l += nla_total_size(sizeof(x->props.extra_flags));
|
||||
if (x->xso.dev)
|
||||
l += nla_total_size(sizeof(struct xfrm_user_offload));
|
||||
if (READ_ONCE(x->xso.dev))
|
||||
l += nla_total_size(sizeof(struct xfrm_user_offload));
|
||||
if (x->props.smark.v | x->props.smark.m) {
|
||||
l += nla_total_size(sizeof(x->props.smark.v));
|
||||
l += nla_total_size(sizeof(x->props.smark.m));
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user