Revert "inet: inet_defrag: prevent sk release while still in use"

This reverts commit d2e0105e54.
This commit is contained in:
Ksawlii 2024-11-24 00:23:32 +01:00
parent 92c7fd94e3
commit efd33de913
5 changed files with 19 additions and 72 deletions

View file

@ -744,7 +744,10 @@ struct sk_buff {
struct list_head list;
};
struct sock *sk;
union {
struct sock *sk;
int ip_defrag_offset;
};
union {
ktime_t tstamp;

View file

@ -1,12 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _NET_CORE_SOCK_DESTRUCTOR_H
#define _NET_CORE_SOCK_DESTRUCTOR_H
#include <net/tcp.h>
static inline bool is_skb_wmem(const struct sk_buff *skb)
{
return skb->destructor == sock_wfree ||
skb->destructor == __sock_wfree ||
(IS_ENABLED(CONFIG_INET) && skb->destructor == tcp_wfree);
}
#endif

View file

@ -24,8 +24,6 @@
#include <net/ip.h>
#include <net/ipv6.h>
#include "../core/sock_destructor.h"
/* Use skb->cb to track consecutive/adjacent fragments coming at
* the end of the queue. Nodes in the rb-tree queue will
* contain "runs" of one or more adjacent fragments.
@ -41,7 +39,6 @@ struct ipfrag_skb_cb {
};
struct sk_buff *next_frag;
int frag_run_len;
int ip_defrag_offset;
};
#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
@ -362,12 +359,12 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
*/
if (!last)
fragrun_create(q, skb); /* First fragment. */
else if (FRAG_CB(last)->ip_defrag_offset + last->len < end) {
else if (last->ip_defrag_offset + last->len < end) {
/* This is the common case: skb goes to the end. */
/* Detect and discard overlaps. */
if (offset < FRAG_CB(last)->ip_defrag_offset + last->len)
if (offset < last->ip_defrag_offset + last->len)
return IPFRAG_OVERLAP;
if (offset == FRAG_CB(last)->ip_defrag_offset + last->len)
if (offset == last->ip_defrag_offset + last->len)
fragrun_append_to_last(q, skb);
else
fragrun_create(q, skb);
@ -384,13 +381,13 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
parent = *rbn;
curr = rb_to_skb(parent);
curr_run_end = FRAG_CB(curr)->ip_defrag_offset +
curr_run_end = curr->ip_defrag_offset +
FRAG_CB(curr)->frag_run_len;
if (end <= FRAG_CB(curr)->ip_defrag_offset)
if (end <= curr->ip_defrag_offset)
rbn = &parent->rb_left;
else if (offset >= curr_run_end)
rbn = &parent->rb_right;
else if (offset >= FRAG_CB(curr)->ip_defrag_offset &&
else if (offset >= curr->ip_defrag_offset &&
end <= curr_run_end)
return IPFRAG_DUP;
else
@ -404,7 +401,7 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
rb_insert_color(&skb->rbnode, &q->rb_fragments);
}
FRAG_CB(skb)->ip_defrag_offset = offset;
skb->ip_defrag_offset = offset;
return IPFRAG_OK;
}
@ -414,28 +411,13 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
struct sk_buff *parent)
{
struct sk_buff *fp, *head = skb_rb_first(&q->rb_fragments);
void (*destructor)(struct sk_buff *);
unsigned int orig_truesize = 0;
struct sk_buff **nextp = NULL;
struct sock *sk = skb->sk;
struct sk_buff **nextp;
int delta;
if (sk && is_skb_wmem(skb)) {
/* TX: skb->sk might have been passed as argument to
* dst->output and must remain valid until tx completes.
*
* Move sk to reassembled skb and fix up wmem accounting.
*/
orig_truesize = skb->truesize;
destructor = skb->destructor;
}
if (head != skb) {
fp = skb_clone(skb, GFP_ATOMIC);
if (!fp) {
head = skb;
goto out_restore_sk;
}
if (!fp)
return NULL;
FRAG_CB(fp)->next_frag = FRAG_CB(skb)->next_frag;
if (RB_EMPTY_NODE(&skb->rbnode))
FRAG_CB(parent)->next_frag = fp;
@ -444,12 +426,6 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
&q->rb_fragments);
if (q->fragments_tail == skb)
q->fragments_tail = fp;
if (orig_truesize) {
/* prevent skb_morph from releasing sk */
skb->sk = NULL;
skb->destructor = NULL;
}
skb_morph(skb, head);
FRAG_CB(skb)->next_frag = FRAG_CB(head)->next_frag;
rb_replace_node(&head->rbnode, &skb->rbnode,
@ -457,13 +433,13 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
consume_skb(head);
head = skb;
}
WARN_ON(FRAG_CB(head)->ip_defrag_offset != 0);
WARN_ON(head->ip_defrag_offset != 0);
delta = -head->truesize;
/* Head of list must not be cloned. */
if (skb_unclone(head, GFP_ATOMIC))
goto out_restore_sk;
return NULL;
delta += head->truesize;
if (delta)
@ -479,7 +455,7 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
clone = alloc_skb(0, GFP_ATOMIC);
if (!clone)
goto out_restore_sk;
return NULL;
skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
skb_frag_list_init(head);
for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
@ -496,21 +472,6 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
nextp = &skb_shinfo(head)->frag_list;
}
out_restore_sk:
if (orig_truesize) {
int ts_delta = head->truesize - orig_truesize;
/* if this reassembled skb is fragmented later,
* fraglist skbs will get skb->sk assigned from head->sk,
* and each frag skb will be released via sock_wfree.
*
* Update sk_wmem_alloc.
*/
head->sk = sk;
head->destructor = destructor;
refcount_add(ts_delta, &sk->sk_wmem_alloc);
}
return nextp;
}
EXPORT_SYMBOL(inet_frag_reasm_prepare);
@ -518,8 +479,6 @@ EXPORT_SYMBOL(inet_frag_reasm_prepare);
void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head,
void *reasm_data, bool try_coalesce)
{
struct sock *sk = is_skb_wmem(head) ? head->sk : NULL;
const unsigned int head_truesize = head->truesize;
struct sk_buff **nextp = (struct sk_buff **)reasm_data;
struct rb_node *rbn;
struct sk_buff *fp;
@ -582,9 +541,6 @@ void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head,
skb_mark_not_on_list(head);
head->prev = NULL;
head->tstamp = q->stamp;
if (sk)
refcount_add(sum_truesize - head_truesize, &sk->sk_wmem_alloc);
}
EXPORT_SYMBOL(inet_frag_reasm_finish);

View file

@ -377,7 +377,6 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
}
skb_dst_drop(skb);
skb_orphan(skb);
return -EINPROGRESS;
insert_error:
@ -480,6 +479,7 @@ int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
struct ipq *qp;
__IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS);
skb_orphan(skb);
/* Lookup (or create) queue header */
qp = ip_find(net, ip_hdr(skb), user, vif);

View file

@ -296,7 +296,6 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
}
skb_dst_drop(skb);
skb_orphan(skb);
return -EINPROGRESS;
insert_error:
@ -472,6 +471,7 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
hdr = ipv6_hdr(skb);
fhdr = (struct frag_hdr *)skb_transport_header(skb);
skb_orphan(skb);
fq = fq_find(net, fhdr->identification, user, hdr,
skb->dev ? skb->dev->ifindex : 0);
if (fq == NULL) {