mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge tag 'ipsec-2026-06-10' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
Steffen Klassert says: ==================== pull request (net): ipsec 2026-06-10 1) xfrm: iptfs: preserve shared-frag marker in iptfs_consume_frags() Propagate SKBFL_SHARED_FRAG when paged fragments are moved between skbs so ESP can decide whether in-place crypto is safe. 2) xfrm: iptfs: fix use-after-free on first_skb in __input_process_payload Replace the unlocked read of xtfs->ra_newskb with a local flag so a concurrent reassembly can no longer free first_skb between spin_unlock and the post-loop check. 3) xfrm: policy: fix use-after-free on inexact bin in xfrm_policy_bysel_ctx() Prune the inexact bin under xfrm_policy_lock so a concurrent xfrm_hash_rebuild() can no longer free it before xfrm_policy_kill() dereferences it. 4) xfrm: iptfs: fix ABBA deadlock in iptfs_destroy_state() Move hrtimer_cancel() for the output and drop timers ahead of their spinlocks, breaking the softirq/lock cycle that could deadlock against the timer callbacks on SMP. 5) xfrm: espintcp: do not reuse an in-progress partial send Fail a new send when espintcp_push_msgs() returns with emsg->len still set, so a blocking caller can no longer overwrite ctx->partial while a previous transfer still owns it. 6) esp: fix page frag reference leak on skb_to_sgvec failure Add a flag to esp_ssg_unref() to unconditionally unref the source scatterlist, releasing the old page references that are otherwise leaked when the second skb_to_sgvec() in esp_output_tail() fails. Please pull or let me know if there are problems. ipsec-2026-06-10 * tag 'ipsec-2026-06-10' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec: esp: fix page frag reference leak on skb_to_sgvec failure xfrm: espintcp: do not reuse an in-progress partial send xfrm: iptfs: fix ABBA deadlock in iptfs_destroy_state() xfrm: policy: fix use-after-free on inexact bin in xfrm_policy_bysel_ctx() xfrm: iptfs: fix use-after-free on first_skb in __input_process_payload xfrm: iptfs: preserve shared-frag marker in iptfs_consume_frags() ==================== Link: https://patch.msgid.link/20260610140800.2562818-1-steffen.klassert@secunet.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -96,7 +96,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
|
||||
__alignof__(struct scatterlist));
|
||||
}
|
||||
|
||||
static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
|
||||
static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb, bool already_unref)
|
||||
{
|
||||
struct crypto_aead *aead = x->data;
|
||||
int extralen = 0;
|
||||
@@ -113,10 +113,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
|
||||
/* Unref skb_frag_pages in the src scatterlist if necessary.
|
||||
* Skip the first sg which comes from skb->data.
|
||||
*/
|
||||
if (req->src != req->dst)
|
||||
for (sg = sg_next(req->src); sg; sg = sg_next(sg))
|
||||
if (already_unref || req->src != req->dst) {
|
||||
struct scatterlist *src = already_unref ? esp_req_sg(aead, req) : req->src;
|
||||
|
||||
for (sg = sg_next(src); sg; sg = sg_next(sg))
|
||||
skb_page_unref(page_to_netmem(sg_page(sg)),
|
||||
skb->pp_recycle);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INET_ESPINTCP
|
||||
@@ -220,7 +223,7 @@ static void esp_output_done(void *data, int err)
|
||||
}
|
||||
|
||||
tmp = ESP_SKB_CB(skb)->tmp;
|
||||
esp_ssg_unref(x, tmp, skb);
|
||||
esp_ssg_unref(x, tmp, skb, false);
|
||||
kfree(tmp);
|
||||
|
||||
if (xo && (xo->flags & XFRM_DEV_RESUME)) {
|
||||
@@ -569,8 +572,10 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
|
||||
err = skb_to_sgvec(skb, dsg,
|
||||
(unsigned char *)esph - skb->data,
|
||||
assoclen + ivlen + esp->clen + alen);
|
||||
if (unlikely(err < 0))
|
||||
if (unlikely(err < 0)) {
|
||||
esp_ssg_unref(x, tmp, skb, true);
|
||||
goto error_free;
|
||||
}
|
||||
}
|
||||
|
||||
if ((x->props.flags & XFRM_STATE_ESN))
|
||||
@@ -602,7 +607,7 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
|
||||
}
|
||||
|
||||
if (sg != dsg)
|
||||
esp_ssg_unref(x, tmp, skb);
|
||||
esp_ssg_unref(x, tmp, skb, false);
|
||||
|
||||
if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP)
|
||||
err = esp_output_tail_tcp(x, skb);
|
||||
|
||||
@@ -113,7 +113,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
|
||||
__alignof__(struct scatterlist));
|
||||
}
|
||||
|
||||
static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
|
||||
static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb, bool already_unref)
|
||||
{
|
||||
struct crypto_aead *aead = x->data;
|
||||
int extralen = 0;
|
||||
@@ -130,10 +130,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb)
|
||||
/* Unref skb_frag_pages in the src scatterlist if necessary.
|
||||
* Skip the first sg which comes from skb->data.
|
||||
*/
|
||||
if (req->src != req->dst)
|
||||
for (sg = sg_next(req->src); sg; sg = sg_next(sg))
|
||||
if (already_unref || req->src != req->dst) {
|
||||
struct scatterlist *src = already_unref ? esp_req_sg(aead, req) : req->src;
|
||||
|
||||
for (sg = sg_next(src); sg; sg = sg_next(sg))
|
||||
skb_page_unref(page_to_netmem(sg_page(sg)),
|
||||
skb->pp_recycle);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INET6_ESPINTCP
|
||||
@@ -254,7 +257,7 @@ static void esp_output_done(void *data, int err)
|
||||
}
|
||||
|
||||
tmp = ESP_SKB_CB(skb)->tmp;
|
||||
esp_ssg_unref(x, tmp, skb);
|
||||
esp_ssg_unref(x, tmp, skb, false);
|
||||
kfree(tmp);
|
||||
|
||||
esp_output_encap_csum(skb);
|
||||
@@ -600,8 +603,10 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
|
||||
err = skb_to_sgvec(skb, dsg,
|
||||
(unsigned char *)esph - skb->data,
|
||||
assoclen + ivlen + esp->clen + alen);
|
||||
if (unlikely(err < 0))
|
||||
if (unlikely(err < 0)) {
|
||||
esp_ssg_unref(x, tmp, skb, true);
|
||||
goto error_free;
|
||||
}
|
||||
}
|
||||
|
||||
if ((x->props.flags & XFRM_STATE_ESN))
|
||||
@@ -634,7 +639,7 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
|
||||
}
|
||||
|
||||
if (sg != dsg)
|
||||
esp_ssg_unref(x, tmp, skb);
|
||||
esp_ssg_unref(x, tmp, skb, false);
|
||||
|
||||
if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP)
|
||||
err = esp_output_tail_tcp(x, skb);
|
||||
|
||||
@@ -346,6 +346,10 @@ static int espintcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
|
||||
err = -ENOBUFS;
|
||||
goto unlock;
|
||||
}
|
||||
if (emsg->len) {
|
||||
err = -ENOBUFS;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
sk_msg_init(&emsg->skmsg);
|
||||
while (1) {
|
||||
|
||||
@@ -954,6 +954,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||
u32 first_iplen, iphlen, iplen, remaining, tail;
|
||||
u32 capturelen;
|
||||
u64 seq;
|
||||
bool first_skb_partial = false;
|
||||
|
||||
xtfs = x->mode_data;
|
||||
net = xs_net(x);
|
||||
@@ -1161,6 +1162,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||
|
||||
spin_unlock(&xtfs->drop_lock);
|
||||
|
||||
first_skb_partial = (first_skb == skb);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1172,7 +1174,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||
/* this should not happen from the above code */
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINIPTFSERROR);
|
||||
|
||||
if (first_skb && first_iplen && !defer && first_skb != xtfs->ra_newskb) {
|
||||
if (first_skb && first_iplen && !defer && !first_skb_partial) {
|
||||
/* first_skb is queued b/c !defer and not partial */
|
||||
if (pskb_trim(first_skb, first_iplen)) {
|
||||
/* error trimming */
|
||||
@@ -2168,6 +2170,8 @@ static void iptfs_consume_frags(struct sk_buff *to, struct sk_buff *from)
|
||||
memcpy(&toi->frags[toi->nr_frags], fromi->frags,
|
||||
sizeof(fromi->frags[0]) * fromi->nr_frags);
|
||||
toi->nr_frags += fromi->nr_frags;
|
||||
if (fromi->nr_frags)
|
||||
toi->flags |= fromi->flags & SKBFL_SHARED_FRAG;
|
||||
fromi->nr_frags = 0;
|
||||
from->data_len = 0;
|
||||
from->len = 0;
|
||||
@@ -2726,8 +2730,9 @@ static void iptfs_destroy_state(struct xfrm_state *x)
|
||||
if (!xtfs)
|
||||
return;
|
||||
|
||||
spin_lock_bh(&xtfs->x->lock);
|
||||
hrtimer_cancel(&xtfs->iptfs_timer);
|
||||
|
||||
spin_lock_bh(&xtfs->x->lock);
|
||||
__skb_queue_head_init(&list);
|
||||
skb_queue_splice_init(&xtfs->queue, &list);
|
||||
spin_unlock_bh(&xtfs->x->lock);
|
||||
@@ -2735,9 +2740,7 @@ static void iptfs_destroy_state(struct xfrm_state *x)
|
||||
while ((skb = __skb_dequeue(&list)))
|
||||
kfree_skb(skb);
|
||||
|
||||
spin_lock_bh(&xtfs->drop_lock);
|
||||
hrtimer_cancel(&xtfs->drop_timer);
|
||||
spin_unlock_bh(&xtfs->drop_lock);
|
||||
|
||||
if (xtfs->ra_newskb)
|
||||
kfree_skb(xtfs->ra_newskb);
|
||||
|
||||
@@ -1156,15 +1156,6 @@ static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool
|
||||
}
|
||||
}
|
||||
|
||||
static void xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b)
|
||||
{
|
||||
struct net *net = read_pnet(&b->k.net);
|
||||
|
||||
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
||||
__xfrm_policy_inexact_prune_bin(b, false);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
||||
}
|
||||
|
||||
static void __xfrm_policy_inexact_flush(struct net *net)
|
||||
{
|
||||
struct xfrm_pol_inexact_bin *bin, *t;
|
||||
@@ -1707,12 +1698,12 @@ xfrm_policy_bysel_ctx(struct net *net, const struct xfrm_mark *mark, u32 if_id,
|
||||
}
|
||||
ret = pol;
|
||||
}
|
||||
if (bin && delete)
|
||||
__xfrm_policy_inexact_prune_bin(bin, false);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
||||
|
||||
if (ret && delete)
|
||||
xfrm_policy_kill(ret);
|
||||
if (bin && delete)
|
||||
xfrm_policy_inexact_prune_bin(bin);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
|
||||
|
||||
Reference in New Issue
Block a user