mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +00:00
wgengine/magicsock: fix data race in ReceiveIPv4.
The UDP reader goroutine was clobbering `n` and `err` from the main goroutine, whose accesses are not synchronized the way `b` is. Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
77354d4617
commit
f265603110
@ -830,8 +830,7 @@ func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr
|
|||||||
go func() {
|
go func() {
|
||||||
// Read a packet, and process any STUN packets before returning.
|
// Read a packet, and process any STUN packets before returning.
|
||||||
for {
|
for {
|
||||||
var pAddr net.Addr
|
n, pAddr, err := c.pconn.ReadFrom(b)
|
||||||
n, pAddr, err = c.pconn.ReadFrom(b)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
case c.udpRecvCh <- udpReadResult{err: err}:
|
case c.udpRecvCh <- udpReadResult{err: err}:
|
||||||
@ -854,6 +853,10 @@ func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Once the above goroutine has started, it owns b until it writes
|
||||||
|
// to udpRecvCh. The code below must not access b until it's
|
||||||
|
// completed a successful receive on udpRecvCh.
|
||||||
|
|
||||||
var addrSet *AddrSet
|
var addrSet *AddrSet
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@ -863,13 +866,18 @@ func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr
|
|||||||
select {
|
select {
|
||||||
case <-c.udpRecvCh:
|
case <-c.udpRecvCh:
|
||||||
// It's likely an error, since we just canceled the read.
|
// It's likely an error, since we just canceled the read.
|
||||||
// But there's a small window where the pconn.ReadFrom could've
|
// But there's a small window where the pconn.ReadFrom
|
||||||
// succeeded but not yet sent, and we got into the derp recv path
|
// could've succeeded but not yet sent, and we got into
|
||||||
// first. In that case this udpReadResult is a real non-err packet
|
// the derp recv path first. In that case this
|
||||||
// and we need to choose which to use. Currently, arbitrarily, we currently
|
// udpReadResult is a real non-err packet and we need to
|
||||||
// select DERP and discard this result entirely.
|
// choose which to use. Currently, arbitrarily, we
|
||||||
// The main point of this receive, though, is to make sure that the goroutine
|
// currently select DERP and discard this result entirely.
|
||||||
// is done with our b []byte buf.
|
//
|
||||||
|
// TODO(danderson): don't just discard packets here, it
|
||||||
|
// makes the stack unreliable and harder to test.
|
||||||
|
//
|
||||||
|
// The main point of this receive, though, is to make sure
|
||||||
|
// that the goroutine is done with our b []byte buf.
|
||||||
c.pconn.SetReadDeadline(time.Time{})
|
c.pconn.SetReadDeadline(time.Time{})
|
||||||
case <-c.donec():
|
case <-c.donec():
|
||||||
return 0, nil, nil, errors.New("Conn closed")
|
return 0, nil, nil, errors.New("Conn closed")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user