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/ (local)
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
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