skb_headlen() is len - data_len in unsigned arithmetic, so it underflows to about 4GB for an skb whose data_len is bigger than its len. skb_copy_and_csum_dev() compares the checksum start against skb_headlen(), so its checks don't catch such an skb: - Without CHECKSUM_PARTIAL the checksum start is skb_headlen() itself. On 64-bit the 4GB value becomes the length of the linear copy. - With CHECKSUM_PARTIAL any checksum start below len passes, whatever the linear area really holds. The BUG_ON() that commit 56453199d28f ("net: skbuff: don't BUG() on a bad csum_start in skb_copy_and_csum_dev()") replaced compared against the same value, so this isn't new. No path is known to build such an skb. Check for data_len > len before anything uses skb_headlen(), and zero the frame. Link: https://lore.kernel.org/all/179273112823.3134.12241973533522574538@kernel.org/ Signed-off-by: Josef Bacik --- net/core/skbuff.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index e400990595f8..b74d8a6ef0f9 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4165,6 +4165,15 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) __wsum csum; long csstart; + /* skb_headlen() underflows if data_len is bigger than len, and then + * the checks below don't bound the copy from the linear area. + */ + if (unlikely(skb->data_len > skb->len)) { + DEBUG_NET_WARN_ON_ONCE(1); + memset(to, 0, skb->len); + return; + } + if (skb->ip_summed == CHECKSUM_PARTIAL) csstart = skb_checksum_start_offset(skb); else -- 2.55.0