fix race in getOrHandshake (#400)

We missed this race with #396 (and I think this is also the crash in
issue #226). We need to lock a little higher in the getOrHandshake
method, before we reset hostinfo.ConnectionInfo. Previously, two
routines could enter this section and confuse the handshake process.

This could result in the other side sending a recv_error that also has
a race with setting hostinfo.ConnectionInfo back to nil. So we make sure
to grab the lock in handleRecvError as well.

Neither of these code paths are in the hot path (handling packets
between two hosts over an active tunnel) so there should be no
performance concerns.
This commit is contained in:
Wade Simmons 2021-03-09 09:27:02 -05:00 committed by GitHub
parent 73a5ed90b2
commit 64d8035d09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 14 deletions

View File

@ -91,6 +91,17 @@ func (f *Interface) getOrHandshake(vpnIp uint32) *HostInfo {
return hostinfo
}
// Handshake is not ready, we need to grab the lock now before we start
// the handshake process
hostinfo.Lock()
defer hostinfo.Unlock()
// Double check, now that we have the lock
ci = hostinfo.ConnectionState
if ci != nil && ci.eKey != nil && ci.ready {
return hostinfo
}
if ci == nil {
// if we don't have a connection state, then send a handshake initiation
ci = f.newConnectionState(true, noise.HandshakeIX, []byte{}, 0)
@ -102,10 +113,6 @@ func (f *Interface) getOrHandshake(vpnIp uint32) *HostInfo {
}
// If we have already created the handshake packet, we don't want to call the function at all.
if !hostinfo.HandshakeReady {
hostinfo.Lock()
defer hostinfo.Unlock()
if !hostinfo.HandshakeReady {
ixHandshakeStage0(f, vpnIp, hostinfo)
// FIXME: Maybe make XX selectable, but probably not since psk makes it nearly pointless for us.
@ -120,7 +127,6 @@ func (f *Interface) getOrHandshake(vpnIp uint32) *HostInfo {
}
}
}
}
return hostinfo
}

View File

@ -329,6 +329,9 @@ func (f *Interface) handleRecvError(addr *udpAddr, h *Header) {
return
}
hostinfo.Lock()
defer hostinfo.Unlock()
if !hostinfo.RecvErrorExceeded() {
return
}