From 8246d251ea9b9cbef07082bd69280a8f988cec0c Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 26 Apr 2021 22:07:03 -0400 Subject: [PATCH] conn: windows: do not error out when receiving UDP jumbogram If we receive a large UDP packet, don't return an error to receive.go, which then terminates the receive loop. Instead, simply retry. Considering Winsock's general finickiness, we might consider other places where an attacker on the wire can generate error conditions like this. Reported-by: Sascha Dierberg Signed-off-by: Jason A. Donenfeld --- conn/bind_windows.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/conn/bind_windows.go b/conn/bind_windows.go index fdd1c24..42208b1 100644 --- a/conn/bind_windows.go +++ b/conn/bind_windows.go @@ -352,8 +352,11 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e } bind.rx.mu.Lock() defer bind.rx.mu.Unlock() + + var err error var count uint32 var results [1]winrio.Result +retry: for tries := 0; count == 0 && tries < receiveSpins; tries++ { if tries > 0 { if atomic.LoadUint32(isOpen) != 1 { @@ -364,7 +367,7 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e count = winrio.DequeueCompletion(bind.rx.cq, results[:]) } if count == 0 { - err := winrio.Notify(bind.rx.cq) + err = winrio.Notify(bind.rx.cq) if err != nil { return 0, nil, err } @@ -385,10 +388,19 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e } } bind.rx.Return(1) - err := bind.InsertReceiveRequest() + err = bind.InsertReceiveRequest() if err != nil { return 0, nil, err } + // We limit the MTU well below the 65k max for practicality, but this means a remote host can still send us + // huge packets. Just try again when this happens. The infinite loop this could cause is still limited to + // attacker bandwidth, just like the rest of the receive path. + if windows.Errno(results[0].Status) == windows.WSAEMSGSIZE { + if atomic.LoadUint32(isOpen) != 1 { + return 0, nil, net.ErrClosed + } + goto retry + } if results[0].Status != 0 { return 0, nil, windows.Errno(results[0].Status) }