Unverified Commit bf10066d authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!6683 v2 CVE-2024-26921

Merge Pull Request from: @ci-robot 
 
PR sync from: Ziyang Xuan <william.xuanziyang@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/B34HJJWWAL5OZVMK3QXDZODJBIAKHPL6/ 
Patchset of CVE-2024-26921.

v2:
  - Fix comment head for KABI bugfix patch.

Florian Westphal (1):
  inet: inet_defrag: prevent sk release while still in use

Vasily Averin (2):
  skbuff: introduce skb_expand_head()
  skb_expand_head() adjust skb->truesize incorrectly

Ziyang Xuan (2):
  net: Fix KABI break for introducing is_skb_wmem()
  sk_buff: Fix KABI break for the modification of struct sk_buff


-- 
2.25.1
 
https://gitee.com/src-openeuler/kernel/issues/I9HVTH 
 
Link:https://gitee.com/openeuler/kernel/pulls/6683

 

Reviewed-by: default avatarYue Haibing <yuehaibing@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parents 76dda4fa d2ab07e9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1181,6 +1181,7 @@ static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom,
int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask);
struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
				     unsigned int headroom);
struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom);
struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom,
				int newtailroom, gfp_t priority);
int __must_check skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
+1 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include <net/inet_ecn.h>
#include <net/dst.h>
#include <net/mptcp.h>
#include <net/tcp_ext.h>

#include <linux/seq_file.h>
#include <linux/memcontrol.h>
@@ -331,7 +332,6 @@ int tcp_send_mss(struct sock *sk, int *size_goal, int flags);
void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle,
	      int size_goal);
void tcp_release_cb(struct sock *sk);
void tcp_wfree(struct sk_buff *skb);
void tcp_write_timer_handler(struct sock *sk);
void tcp_delack_timer_handler(struct sock *sk);
int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);

include/net/tcp_ext.h

0 → 100644
+14 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-or-later */

#ifndef _TCP_EXT_H
#define _TCP_EXT_H

void tcp_wfree(struct sk_buff *skb);

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 /* _TCP_EXT_H */
+52 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@
#include <net/mpls.h>
#include <net/mptcp.h>
#include <net/page_pool.h>
#include <net/tcp_ext.h>

#include <linux/uaccess.h>
#include <trace/events/skb.h>
@@ -1773,6 +1774,57 @@ int __skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri)
	return 0;
}

/**
 *	skb_expand_head - reallocate header of &sk_buff
 *	@skb: buffer to reallocate
 *	@headroom: needed headroom
 *
 *	Unlike skb_realloc_headroom, this one does not allocate a new skb
 *	if possible; copies skb->sk to new skb as needed
 *	and frees original skb in case of failures.
 *
 *	It expect increased headroom and generates warning otherwise.
 */

struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom)
{
	int delta = headroom - skb_headroom(skb);
	int osize = skb_end_offset(skb);
	struct sock *sk = skb->sk;

	if (WARN_ONCE(delta <= 0,
		      "%s is expecting an increase in the headroom", __func__))
		return skb;

	delta = SKB_DATA_ALIGN(delta);
	/* pskb_expand_head() might crash, if skb is shared. */
	if (skb_shared(skb) || !is_skb_wmem(skb)) {
		struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);

		if (unlikely(!nskb))
			goto fail;

		if (sk)
			skb_set_owner_w(nskb, sk);
		consume_skb(skb);
		skb = nskb;
	}
	if (pskb_expand_head(skb, delta, 0, GFP_ATOMIC))
		goto fail;

	if (sk && is_skb_wmem(skb)) {
		delta = skb_end_offset(skb) - osize;
		refcount_add(delta, &sk->sk_wmem_alloc);
		skb->truesize += delta;
	}
	return skb;

fail:
	kfree_skb(skb);
	return NULL;
}
EXPORT_SYMBOL(skb_expand_head);

/**
 *	skb_copy_expand	-	copy and expand sk_buff
 *	@skb: buffer to copy
+57 −13
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
#include <net/ip.h>
#include <net/ipv6.h>

#include <net/tcp_ext.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.
@@ -39,6 +41,7 @@ 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))
@@ -359,12 +362,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 (last->ip_defrag_offset + last->len < end) {
	else if (FRAG_CB(last)->ip_defrag_offset + last->len < end) {
		/* This is the common case: skb goes to the end. */
		/* Detect and discard overlaps. */
		if (offset < last->ip_defrag_offset + last->len)
		if (offset < FRAG_CB(last)->ip_defrag_offset + last->len)
			return IPFRAG_OVERLAP;
		if (offset == last->ip_defrag_offset + last->len)
		if (offset == FRAG_CB(last)->ip_defrag_offset + last->len)
			fragrun_append_to_last(q, skb);
		else
			fragrun_create(q, skb);
@@ -381,13 +384,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 = curr->ip_defrag_offset +
			curr_run_end = FRAG_CB(curr)->ip_defrag_offset +
					FRAG_CB(curr)->frag_run_len;
			if (end <= curr->ip_defrag_offset)
			if (end <= FRAG_CB(curr)->ip_defrag_offset)
				rbn = &parent->rb_left;
			else if (offset >= curr_run_end)
				rbn = &parent->rb_right;
			else if (offset >= curr->ip_defrag_offset &&
			else if (offset >= FRAG_CB(curr)->ip_defrag_offset &&
				 end <= curr_run_end)
				return IPFRAG_DUP;
			else
@@ -401,7 +404,7 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
		rb_insert_color(&skb->rbnode, &q->rb_fragments);
	}

	skb->ip_defrag_offset = offset;
	FRAG_CB(skb)->ip_defrag_offset = offset;

	return IPFRAG_OK;
}
@@ -411,13 +414,28 @@ 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);
	struct sk_buff **nextp;
	void (*destructor)(struct sk_buff *);
	unsigned int orig_truesize = 0;
	struct sk_buff **nextp = NULL;
	struct sock *sk = skb->sk;
	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)
			return NULL;
		if (!fp) {
			head = skb;
			goto out_restore_sk;
		}
		FRAG_CB(fp)->next_frag = FRAG_CB(skb)->next_frag;
		if (RB_EMPTY_NODE(&skb->rbnode))
			FRAG_CB(parent)->next_frag = fp;
@@ -426,6 +444,12 @@ 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,
@@ -433,13 +457,13 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
		consume_skb(head);
		head = skb;
	}
	WARN_ON(head->ip_defrag_offset != 0);
	WARN_ON(FRAG_CB(head)->ip_defrag_offset != 0);

	delta = -head->truesize;

	/* Head of list must not be cloned. */
	if (skb_unclone(head, GFP_ATOMIC))
		return NULL;
		goto out_restore_sk;

	delta += head->truesize;
	if (delta)
@@ -455,7 +479,7 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,

		clone = alloc_skb(0, GFP_ATOMIC);
		if (!clone)
			return NULL;
			goto out_restore_sk;
		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++)
@@ -472,6 +496,21 @@ 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);
@@ -479,6 +518,8 @@ 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;
@@ -541,6 +582,9 @@ 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);

Loading