skb_copy_and_csum_dev() copies everything up to the checksum start out of the linear area, and BUG()s if the checksum start is past the end of the linear area. It misses the other direction: a CHECKSUM_PARTIAL skb that has been pulled past its csum_start gives a negative offset, which gets past the check and becomes a ~4GB copy. It also trusts csum_offset when it stores the folded checksum. So far skb_copy_and_csum_bits() BUG()ing on a short skb has covered for that, but once it returns instead, a bad csum_offset would write past the end of the caller's buffer. The function returns void and its callers are drivers copying a frame into a bounce buffer just before handing it to the hardware. There's nothing for them to back out of, so check both ends of csum_start and that the checksum field fits in the frame, warn once and copy the whole frame with skb_copy_bits() without filling in the checksum. The frame goes out with a bad checksum and is dropped by the receiver. If even that copy fails, zero the buffer so the driver doesn't send stale bytes. Assisted-by: LLM Signed-off-by: Josef Bacik --- net/core/skbuff.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4070e0c25f63..c8c2c0319b87 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3971,7 +3971,17 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) else csstart = skb_headlen(skb); - BUG_ON(csstart > skb_headlen(skb)); + if (WARN_ON_ONCE(csstart < 0 || csstart > skb_headlen(skb) || + (skb->ip_summed == CHECKSUM_PARTIAL && + csstart + skb->csum_offset + sizeof(__sum16) > + skb->len))) { + /* Send the frame without the checksum filled in, or send + * zeroes if we can't even copy it. + */ + if (skb_copy_bits(skb, 0, to, skb->len)) + memset(to, 0, skb->len); + return; + } skb_copy_from_linear_data(skb, to, csstart); -- 2.55.0