Skip to content

Commit 389704e

Browse files
author
Paolo Abeni
committed
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>
2 parents 1cd23ca + f38f8cc commit 389704e

9 files changed

Lines changed: 73 additions & 26 deletions

File tree

include/net/xfrm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ struct xfrm_dev_offload {
162162
*/
163163
struct net_device *real_dev;
164164
unsigned long offload_handle;
165+
/* Snapshot the attached device index for dump paths. */
166+
int ifindex;
165167
u8 dir : 2;
166168
u8 type : 2;
167169
u8 flags : 2;

net/core/dev.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4018,6 +4018,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
40184018
return NULL;
40194019
}
40204020

4021+
/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
4022+
* if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
4023+
*/
40214024
static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
40224025
{
40234026
netdev_features_t features;
@@ -4089,7 +4092,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
40894092
skb->prev = skb;
40904093

40914094
skb = validate_xmit_skb(skb, dev, again);
4092-
if (!skb)
4095+
if (IS_ERR_OR_NULL(skb))
40934096
continue;
40944097

40954098
if (!head)
@@ -4860,8 +4863,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
48604863
goto recursion_alert;
48614864

48624865
skb = validate_xmit_skb(skb, dev, &again);
4863-
if (!skb)
4866+
if (IS_ERR_OR_NULL(skb)) {
4867+
if (PTR_ERR(skb) == -EINPROGRESS)
4868+
rc = NET_XMIT_SUCCESS;
48644869
goto out;
4870+
}
48654871

48664872
HARD_TX_LOCK(dev, txq, cpu);
48674873

net/ipv6/xfrm6_policy.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
8888
xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
8989
if (!xdst->u.rt6.rt6i_idev) {
9090
netdev_put(dev, &xdst->u.dst.dev_tracker);
91+
xdst->u.dst.dev = NULL;
9192
return -ENODEV;
9293
}
9394

net/xfrm/xfrm_device.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
182182
err = x->type_offload->xmit(x, skb, esp_features);
183183
if (err) {
184184
if (err == -EINPROGRESS)
185-
return NULL;
185+
return ERR_PTR(-EINPROGRESS);
186186

187187
XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
188188
kfree_skb(skb);
@@ -224,7 +224,15 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
224224
pskb = skb2;
225225
}
226226

227-
return skb;
227+
/* skb_gso_segment() set skb->prev to the last segment, but async
228+
* crypto may have stolen it above without updating ->prev. Repoint
229+
* it at the last retained segment so validate_xmit_skb_list() does
230+
* not chain onto a segment now owned by the crypto engine.
231+
*/
232+
if (skb)
233+
skb->prev = pskb;
234+
235+
return skb ? skb : ERR_PTR(-EINPROGRESS);
228236
}
229237
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
230238

@@ -313,6 +321,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
313321
}
314322

315323
xso->dev = dev;
324+
xso->ifindex = dev->ifindex;
316325
netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC);
317326

318327
if (xuo->flags & XFRM_OFFLOAD_INBOUND)

net/xfrm/xfrm_iptfs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb,
480480
}
481481
__skb_frag_ref(tofrag);
482482
shinfo->nr_frags++;
483+
shinfo->flags |= SKBFL_SHARED_FRAG;
483484

484485
/* see if we are done */
485486
fraglen = tofrag->len;

net/xfrm/xfrm_nat_keepalive.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb,
5555
ka->encap_sport, sock_net_uid(net, NULL));
5656

5757
rt = ip_route_output_key(net, &fl4);
58-
if (IS_ERR(rt))
58+
if (IS_ERR(rt)) {
59+
kfree_skb(skb);
5960
return PTR_ERR(rt);
61+
}
6062

6163
skb_dst_set(skb, &rt->dst);
6264

@@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb,
101103
dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL);
102104
if (IS_ERR(dst)) {
103105
local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock);
106+
kfree_skb(skb);
104107
return PTR_ERR(dst);
105108
}
106109

@@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
118121
sizeof(struct ipv6hdr)) +
119122
sizeof(struct udphdr);
120123
const u8 nat_ka_payload = 0xFF;
121-
int err = -EAFNOSUPPORT;
122124
struct sk_buff *skb;
123125
struct udphdr *uh;
124126

@@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
140142

141143
switch (ka->family) {
142144
case AF_INET:
143-
err = nat_keepalive_send_ipv4(skb, ka);
145+
nat_keepalive_send_ipv4(skb, ka);
144146
break;
145147
#if IS_ENABLED(CONFIG_IPV6)
146148
case AF_INET6:
147-
err = nat_keepalive_send_ipv6(skb, ka, uh);
149+
nat_keepalive_send_ipv6(skb, ka, uh);
148150
break;
149151
#endif
150-
}
151-
if (err)
152+
default:
152153
kfree_skb(skb);
154+
break;
155+
}
153156
}
154157

155158
struct nat_keepalive_work_ctx {

net/xfrm/xfrm_policy.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
13291329
}
13301330
}
13311331

1332-
if (policy->selector.prefixlen_d < dbits ||
1333-
policy->selector.prefixlen_s < sbits)
1332+
if (policy->selector.prefixlen_d >= dbits &&
1333+
policy->selector.prefixlen_s >= sbits)
13341334
continue;
13351335

13361336
bin = xfrm_policy_inexact_alloc_bin(policy, dir);

net/xfrm/xfrm_state.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
15471547
xso->type = XFRM_DEV_OFFLOAD_PACKET;
15481548
xso->dir = xdo->dir;
15491549
xso->dev = dev;
1550+
xso->ifindex = dev->ifindex;
15501551
xso->flags = XFRM_DEV_OFFLOAD_FLAG_ACQ;
15511552
netdev_hold(dev, &xso->dev_tracker, GFP_ATOMIC);
15521553
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,
20712072

20722073
x->mode_cbs = orig->mode_cbs;
20732074
if (x->mode_cbs && x->mode_cbs->clone_state) {
2074-
if (x->mode_cbs->clone_state(x, orig))
2075+
if (x->mode_cbs->clone_state(x, orig)) {
2076+
if (!x->mode_data)
2077+
x->mode_cbs = NULL;
20752078
goto error;
2079+
}
20762080
}
20772081

20782082
x->props.reqid = m->new_reqid;
@@ -3010,7 +3014,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
30103014
if (sockptr_is_null(optval) && !optlen) {
30113015
xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
30123016
xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
3013-
__sk_dst_reset(sk);
3017+
sk_dst_reset(sk);
30143018
return 0;
30153019
}
30163020

@@ -3050,7 +3054,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
30503054
if (err >= 0) {
30513055
xfrm_sk_policy_insert(sk, err, pol);
30523056
xfrm_pol_put(pol);
3053-
__sk_dst_reset(sk);
3057+
sk_dst_reset(sk);
30543058
err = 0;
30553059
}
30563060

@@ -3291,6 +3295,8 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
32913295
if (x->mode_cbs->init_state)
32923296
err = x->mode_cbs->init_state(x);
32933297
module_put(x->mode_cbs->owner);
3298+
if (err && !x->mode_data)
3299+
x->mode_cbs = NULL;
32943300
}
32953301
error:
32963302
return err;

net/xfrm/xfrm_user.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,17 +1201,26 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
12011201
return 0;
12021202
}
12031203

1204-
static void xso_to_xuo(const struct xfrm_dev_offload *xso,
1205-
struct xfrm_user_offload *xuo)
1204+
static void xso_to_xuo_ifindex(const struct xfrm_dev_offload *xso, int ifindex,
1205+
struct xfrm_user_offload *xuo)
12061206
{
1207-
xuo->ifindex = xso->dev->ifindex;
1207+
xuo->ifindex = ifindex;
12081208
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
12091209
xuo->flags = XFRM_OFFLOAD_INBOUND;
12101210
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
12111211
xuo->flags |= XFRM_OFFLOAD_PACKET;
12121212
}
12131213

1214-
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
1214+
#ifdef CONFIG_XFRM_MIGRATE
1215+
static void xso_to_xuo(const struct xfrm_dev_offload *xso,
1216+
struct xfrm_user_offload *xuo)
1217+
{
1218+
xso_to_xuo_ifindex(xso, xso->dev->ifindex, xuo);
1219+
}
1220+
#endif
1221+
1222+
static int copy_user_offload_ifindex(const struct xfrm_dev_offload *xso,
1223+
int ifindex, struct sk_buff *skb)
12151224
{
12161225
struct xfrm_user_offload *xuo;
12171226
struct nlattr *attr;
@@ -1222,11 +1231,22 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
12221231

12231232
xuo = nla_data(attr);
12241233
memset(xuo, 0, sizeof(*xuo));
1225-
xso_to_xuo(xso, xuo);
1234+
xso_to_xuo_ifindex(xso, ifindex, xuo);
12261235

12271236
return 0;
12281237
}
12291238

1239+
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
1240+
{
1241+
return copy_user_offload_ifindex(xso, xso->dev->ifindex, skb);
1242+
}
1243+
1244+
static int copy_user_state_offload(const struct xfrm_dev_offload *xso,
1245+
struct sk_buff *skb)
1246+
{
1247+
return copy_user_offload_ifindex(xso, READ_ONCE(xso->ifindex), skb);
1248+
}
1249+
12301250
static bool xfrm_redact(void)
12311251
{
12321252
return IS_ENABLED(CONFIG_SECURITY) &&
@@ -1433,8 +1453,8 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
14331453
&x->replay);
14341454
if (ret)
14351455
goto out;
1436-
if(x->xso.dev)
1437-
ret = copy_user_offload(&x->xso, skb);
1456+
if (READ_ONCE(x->xso.dev))
1457+
ret = copy_user_state_offload(&x->xso, skb);
14381458
if (ret)
14391459
goto out;
14401460
if (x->if_id) {
@@ -2104,13 +2124,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family,
21042124
switch (ut[i].mode) {
21052125
case XFRM_MODE_TUNNEL:
21062126
case XFRM_MODE_BEET:
2127+
case XFRM_MODE_IPTFS:
21072128
if (ut[i].optional && dir == XFRM_POLICY_OUT) {
21082129
NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy");
21092130
return -EINVAL;
21102131
}
21112132
break;
2112-
case XFRM_MODE_IPTFS:
2113-
break;
21142133
default:
21152134
if (ut[i].family != prev_family) {
21162135
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)
40464065
l += nla_total_size(sizeof(*x->coaddr));
40474066
if (x->props.extra_flags)
40484067
l += nla_total_size(sizeof(x->props.extra_flags));
4049-
if (x->xso.dev)
4050-
l += nla_total_size(sizeof(struct xfrm_user_offload));
4068+
if (READ_ONCE(x->xso.dev))
4069+
l += nla_total_size(sizeof(struct xfrm_user_offload));
40514070
if (x->props.smark.v | x->props.smark.m) {
40524071
l += nla_total_size(sizeof(x->props.smark.v));
40534072
l += nla_total_size(sizeof(x->props.smark.m));

0 commit comments

Comments
 (0)