[RHEL7,COMMIT] ms/net: make skb_partial_csum_set() more robust against overflows

Submitted by Vasily Averin on Dec. 15, 2020, 9:24 a.m.

Details

Message ID 202012150924.0BF9OfpM003089@vz7build.vvs.sw.ru
State New
Series "Series without cover letter"
Headers show

Commit Message

Vasily Averin Dec. 15, 2020, 9:24 a.m.
The commit is pushed to "branch-rh7-3.10.0-1160.6.1.vz7.171.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1160.6.1.vz7.171.4
------>
commit b18c22895955bd9f3153784fa4018c1ff5a5996f
Author: Eric Dumazet <edumazet@google.com>
Date:   Tue Dec 15 12:24:41 2020 +0300

    ms/net: make skb_partial_csum_set() more robust against overflows
    
    syzbot managed to crash in skb_checksum_help() [1] :
    
            BUG_ON(offset + sizeof(__sum16) > skb_headlen(skb));
    
    Root cause is the following check in skb_partial_csum_set()
    
    	if (unlikely(start > skb_headlen(skb)) ||
    	    unlikely((int)start + off > skb_headlen(skb) - 2))
    		return false;
    
    If skb_headlen(skb) is 1, then (skb_headlen(skb) - 2) becomes 0xffffffff
    and the check fails to detect that ((int)start + off) is off the limit,
    since the compare is unsigned.
    
    When we fix that, then the first condition (start > skb_headlen(skb))
    becomes obsolete.
    
    Then we should also check that (skb_headroom(skb) + start) wont
    overflow 16bit field.
    
    [1]
    kernel BUG at net/core/dev.c:2880!
    invalid opcode: 0000 [#1] PREEMPT SMP KASAN
    CPU: 1 PID: 7330 Comm: syz-executor4 Not tainted 4.19.0-rc6+ #253
    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
    RIP: 0010:skb_checksum_help+0x9e3/0xbb0 net/core/dev.c:2880
    Code: 85 00 ff ff ff 48 c1 e8 03 42 80 3c 28 00 0f 84 09 fb ff ff 48 8b bd 00 ff ff ff e8 97 a8 b9 fb e9 f8 fa ff ff e8 2d 09 76 fb <0f> 0b 48 8b bd 28 ff ff ff e8 1f a8 b9 fb e9 b1 f6 ff ff 48 89 cf
    RSP: 0018:ffff8801d83a6f60 EFLAGS: 00010293
    RAX: ffff8801b9834380 RBX: ffff8801b9f8d8c0 RCX: ffffffff8608c6d7
    RDX: 0000000000000000 RSI: ffffffff8608cc63 RDI: 0000000000000006
    RBP: ffff8801d83a7068 R08: ffff8801b9834380 R09: 0000000000000000
    R10: ffff8801d83a76d8 R11: 0000000000000000 R12: 0000000000000001
    R13: 0000000000010001 R14: 000000000000ffff R15: 00000000000000a8
    FS:  00007f1a66db5700(0000) GS:ffff8801daf00000(0000) knlGS:0000000000000000
    CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
    CR2: 00007f7d77f091b0 CR3: 00000001ba252000 CR4: 00000000001406e0
    DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
    DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
    Call Trace:
     skb_csum_hwoffload_help+0x8f/0xe0 net/core/dev.c:3269
     validate_xmit_skb+0xa2a/0xf30 net/core/dev.c:3312
     __dev_queue_xmit+0xc2f/0x3950 net/core/dev.c:3797
     dev_queue_xmit+0x17/0x20 net/core/dev.c:3838
     packet_snd net/packet/af_packet.c:2928 [inline]
     packet_sendmsg+0x422d/0x64c0 net/packet/af_packet.c:2953
    
    Fixes: 5ff8dda3035d ("net: Ensure partial checksum offset is inside the skb head")
    Signed-off-by: Eric Dumazet <edumazet@google.com>
    Cc: Herbert Xu <herbert@gondor.apana.org.au>
    Reported-by: syzbot <syzkaller@googlegroups.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    
    (cherry-picked from commit 52b5d6f5dcf0e5201392f7d417148ccb537dbf6f)
    https://jira.sw.ru/browse/PSBM-123062
    Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
---
 net/core/skbuff.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index fa5ba0d..eef4100 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3961,14 +3961,16 @@  EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
  */
 bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)
 {
-	if (unlikely(start > skb_headlen(skb)) ||
-	    unlikely((int)start + off > skb_headlen(skb) - 2)) {
-		net_warn_ratelimited("bad partial csum: csum=%u/%u len=%u\n",
-				     start, off, skb_headlen(skb));
+	u32 csum_end = (u32)start + (u32)off + sizeof(__sum16);
+	u32 csum_start = skb_headroom(skb) + (u32)start;
+
+	if (unlikely(csum_start > U16_MAX || csum_end > skb_headlen(skb))) {
+		net_warn_ratelimited("bad partial csum: csum=%u/%u headroom=%u headlen=%u\n",
+				     start, off, skb_headroom(skb), skb_headlen(skb));
 		return false;
 	}
 	skb->ip_summed = CHECKSUM_PARTIAL;
-	skb->csum_start = skb_headroom(skb) + start;
+	skb->csum_start = csum_start;
 	skb->csum_offset = off;
 	skb_set_transport_header(skb, start);
 	return true;