tsnet: fix data race in chanTUN test device
The wireguard-go receive path could be in chanTUN.Write, selecting to send on the Inbound channel, while test cleanup called chanTUN.Close, which closed that same channel. The select on the closed channel in Write did not synchronize with Close, so the race detector flagged the send racing with the close. It could also have panicked with a send on a closed channel. Add a mutex serializing Write and Close. Write now checks for closed under the lock before doing a non-blocking send, so Close can't close Inbound mid-send. Fixes #20541 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: I8d8d10250cef0c1931753c78eebff6e8286f7201
This commit is contained in:
committed by
Brad Fitzpatrick
parent
246c82a658
commit
2dd5d82f56
+13
-2
@@ -2560,6 +2560,10 @@ type chanTUN struct {
|
|||||||
Outbound chan []byte // packets to read from TUN
|
Outbound chan []byte // packets to read from TUN
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
events chan tun.Event
|
events chan tun.Event
|
||||||
|
|
||||||
|
// wmu serializes Write and Close so a Write can't send on
|
||||||
|
// Inbound while Close is closing it.
|
||||||
|
wmu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newChanTUN() *chanTUN {
|
func newChanTUN() *chanTUN {
|
||||||
@@ -2576,6 +2580,8 @@ func newChanTUN() *chanTUN {
|
|||||||
func (t *chanTUN) File() *os.File { panic("not implemented") }
|
func (t *chanTUN) File() *os.File { panic("not implemented") }
|
||||||
|
|
||||||
func (t *chanTUN) Close() error {
|
func (t *chanTUN) Close() error {
|
||||||
|
t.wmu.Lock()
|
||||||
|
defer t.wmu.Unlock()
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
default:
|
default:
|
||||||
@@ -2596,14 +2602,19 @@ func (t *chanTUN) Read(bufs [][]byte, sizes []int, offset int) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *chanTUN) Write(bufs [][]byte, offset int) (int, error) {
|
func (t *chanTUN) Write(bufs [][]byte, offset int) (int, error) {
|
||||||
|
t.wmu.Lock()
|
||||||
|
defer t.wmu.Unlock()
|
||||||
|
select {
|
||||||
|
case <-t.closed:
|
||||||
|
return 0, errors.New("closed")
|
||||||
|
default:
|
||||||
|
}
|
||||||
for _, buf := range bufs {
|
for _, buf := range bufs {
|
||||||
pkt := buf[offset:]
|
pkt := buf[offset:]
|
||||||
if len(pkt) == 0 {
|
if len(pkt) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
|
||||||
return 0, errors.New("closed")
|
|
||||||
case t.Inbound <- slices.Clone(pkt):
|
case t.Inbound <- slices.Clone(pkt):
|
||||||
default:
|
default:
|
||||||
// Drop the packet if the channel is full, like a real
|
// Drop the packet if the channel is full, like a real
|
||||||
|
|||||||
Reference in New Issue
Block a user