tun: windows: mitigate infinite loop in Flush()

It's possible that for whatever reason, we keep returning EOF, resulting
in repeated close/open/write operations, except with empty packets.
This commit is contained in:
Jason A. Donenfeld 2019-05-31 15:40:08 +02:00
parent d9f995209c
commit da61947ec3

View file

@ -8,6 +8,7 @@ package tun
import ( import (
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"sync" "sync"
"time" "time"
@ -230,6 +231,7 @@ func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
default: default:
} }
retries := 1000
for { for {
if tun.rdBuff.offset+packetExchangeAlignment <= tun.rdBuff.avail { if tun.rdBuff.offset+packetExchangeAlignment <= tun.rdBuff.avail {
// Get packet from the exchange buffer. // Get packet from the exchange buffer.
@ -255,31 +257,31 @@ func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
return 0, err return 0, err
} }
// Fill queue. n, err := file.Read(tun.rdBuff.data[:])
retries := 1000 if err != nil {
for {
n, err := file.Read(tun.rdBuff.data[:])
if err != nil {
tun.rdBuff.offset = 0
tun.rdBuff.avail = 0
pe, ok := err.(*os.PathError)
if tun.close {
return 0, os.ErrClosed
}
if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED {
retries--
continue
}
if ok && pe.Err == windows.ERROR_HANDLE_EOF {
tun.closeTUN()
break
}
return 0, err
}
tun.rdBuff.offset = 0 tun.rdBuff.offset = 0
tun.rdBuff.avail = uint32(n) tun.rdBuff.avail = 0
break pe, ok := err.(*os.PathError)
if tun.close {
return 0, os.ErrClosed
}
if retries > 0 && ok && (pe.Err == windows.ERROR_HANDLE_EOF || pe.Err == windows.ERROR_OPERATION_ABORTED) {
retries--
tun.closeTUN()
time.Sleep(time.Millisecond * 2)
continue
}
return 0, err
} }
if n == 0 {
if retries == 0 {
return 0, io.ErrShortBuffer
}
retries--
continue
}
tun.rdBuff.offset = 0
tun.rdBuff.avail = uint32(n)
} }
} }
@ -289,6 +291,11 @@ func (tun *NativeTun) Flush() error {
if tun.wrBuff.offset == 0 { if tun.wrBuff.offset == 0 {
return nil return nil
} }
defer func() {
tun.wrBuff.packetNum = 0
tun.wrBuff.offset = 0
}()
retries := 1000
for { for {
// Get TUN data pipe. // Get TUN data pipe.
@ -297,23 +304,22 @@ func (tun *NativeTun) Flush() error {
return err return err
} }
// Flush write buffer.
retries := 1000
for { for {
_, err = file.Write(tun.wrBuff.data[:tun.wrBuff.offset]) _, err = file.Write(tun.wrBuff.data[:tun.wrBuff.offset])
tun.wrBuff.packetNum = 0
tun.wrBuff.offset = 0
if err != nil { if err != nil {
pe, ok := err.(*os.PathError) pe, ok := err.(*os.PathError)
if tun.close { if tun.close {
return os.ErrClosed return os.ErrClosed
} }
if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED { if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED { // Adapter is paused or in low-power state.
retries-- retries--
time.Sleep(time.Millisecond * 2)
continue continue
} }
if ok && pe.Err == windows.ERROR_HANDLE_EOF { if retries > 0 && ok && pe.Err == windows.ERROR_HANDLE_EOF { // Adapter is going down.
retries--
tun.closeTUN() tun.closeTUN()
time.Sleep(time.Millisecond * 2)
break break
} }
return err return err