fix deadlock when receiving two packets with diversification nonces

This commit is contained in:
Marten Seemann
2018-04-24 19:56:29 +09:00
parent d6e9e3faa2
commit 0c014c0aff
5 changed files with 58 additions and 37 deletions

View File

@@ -38,7 +38,7 @@ type cryptoSetupClient struct {
lastSentCHLO []byte
certManager crypto.CertManager
divNonceChan <-chan []byte
divNonceChan chan struct{}
diversificationNonce []byte
clientHelloCounter int
@@ -79,12 +79,12 @@ func NewCryptoSetupClient(
initialVersion protocol.VersionNumber,
negotiatedVersions []protocol.VersionNumber,
logger utils.Logger,
) (CryptoSetup, chan<- []byte, error) {
) (CryptoSetup, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, version)
if err != nil {
return nil, nil, err
return nil, err
}
divNonceChan := make(chan []byte)
divNonceChan := make(chan struct{})
cs := &cryptoSetupClient{
cryptoStream: cryptoStream,
hostname: hostname,
@@ -101,7 +101,7 @@ func NewCryptoSetupClient(
divNonceChan: divNonceChan,
logger: logger,
}
return cs, divNonceChan, nil
return cs, nil
}
func (h *cryptoSetupClient) HandleCryptoStream() error {
@@ -120,33 +120,26 @@ func (h *cryptoSetupClient) HandleCryptoStream() error {
}()
for {
err := h.maybeUpgradeCrypto()
if err != nil {
if err := h.maybeUpgradeCrypto(); err != nil {
return err
}
h.mutex.RLock()
sendCHLO := h.secureAEAD == nil
h.mutex.RUnlock()
if sendCHLO {
err = h.sendCHLO()
if err != nil {
if err := h.sendCHLO(); err != nil {
return err
}
}
var message HandshakeMessage
select {
case divNonce := <-h.divNonceChan:
if len(h.diversificationNonce) != 0 && !bytes.Equal(h.diversificationNonce, divNonce) {
return errConflictingDiversificationNonces
}
h.diversificationNonce = divNonce
case <-h.divNonceChan:
// there's no message to process, but we should try upgrading the crypto again
continue
case message = <-messageChan:
case err = <-errorChan:
case err := <-errorChan:
return err
}
@@ -386,6 +379,21 @@ func (h *cryptoSetupClient) ConnectionState() ConnectionState {
}
}
func (h *cryptoSetupClient) SetDiversificationNonce(divNonce []byte) error {
h.mutex.Lock()
if len(h.diversificationNonce) > 0 {
defer h.mutex.Unlock()
if !bytes.Equal(h.diversificationNonce, divNonce) {
return errConflictingDiversificationNonces
}
return nil
}
h.diversificationNonce = divNonce
h.mutex.Unlock()
h.divNonceChan <- struct{}{}
return nil
}
func (h *cryptoSetupClient) sendCHLO() error {
h.clientHelloCounter++
if h.clientHelloCounter > protocol.MaxClientHellos {

View File

@@ -91,7 +91,6 @@ var _ = Describe("Client Crypto Setup", func() {
shloMap map[Tag][]byte
handshakeEvent chan struct{}
paramsChan chan TransportParameters
divNonceChan chan<- []byte
)
BeforeEach(func() {
@@ -120,7 +119,7 @@ var _ = Describe("Client Crypto Setup", func() {
// use a buffered channel here, so that we can parse a SHLO without having to receive the TransportParameters to avoid blocking
paramsChan = make(chan TransportParameters, 1)
handshakeEvent = make(chan struct{}, 2)
csInt, dnc, err := NewCryptoSetupClient(
csInt, err := NewCryptoSetupClient(
stream,
"hostname",
protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
@@ -139,7 +138,6 @@ var _ = Describe("Client Crypto Setup", func() {
cs.keyDerivation = keyDerivation
cs.nullAEAD = mockcrypto.NewMockAEAD(mockCtrl)
cs.cryptoStream = stream
divNonceChan = dnc
})
Context("Reading REJ", func() {
@@ -717,16 +715,16 @@ var _ = Describe("Client Crypto Setup", func() {
It("tries to escalate the crypto after receiving a diversification nonce", func() {
done := make(chan struct{})
cs.diversificationNonce = nil
cs.serverVerified = true
go func() {
defer GinkgoRecover()
err := cs.HandleCryptoStream()
Expect(err).To(MatchError(qerr.Error(qerr.HandshakeFailed, errMockStreamClosing.Error())))
close(done)
}()
cs.diversificationNonce = nil
cs.serverVerified = true
Expect(cs.secureAEAD).To(BeNil())
divNonceChan <- []byte("div")
Expect(cs.SetDiversificationNonce([]byte("div"))).To(Succeed())
Eventually(handshakeEvent).Should(Receive())
Expect(cs.secureAEAD).ToNot(BeNil())
Expect(handshakeEvent).ToNot(Receive())
@@ -926,7 +924,7 @@ var _ = Describe("Client Crypto Setup", func() {
close(done)
}()
nonce := []byte("foobar")
divNonceChan <- nonce
Expect(cs.SetDiversificationNonce(nonce)).To(Succeed())
Eventually(func() []byte { return cs.diversificationNonce }).Should(Equal(nonce))
// make the go routine return
stream.close()
@@ -942,8 +940,8 @@ var _ = Describe("Client Crypto Setup", func() {
close(done)
}()
nonce := []byte("foobar")
divNonceChan <- nonce
divNonceChan <- nonce
Expect(cs.SetDiversificationNonce(nonce)).To(Succeed())
Expect(cs.SetDiversificationNonce(nonce)).To(Succeed())
Eventually(func() []byte { return cs.diversificationNonce }).Should(Equal(nonce))
// make the go routine return
stream.close()
@@ -955,13 +953,17 @@ var _ = Describe("Client Crypto Setup", func() {
go func() {
defer GinkgoRecover()
err := cs.HandleCryptoStream()
Expect(err).To(MatchError(errConflictingDiversificationNonces))
Expect(err).To(MatchError(qerr.Error(qerr.HandshakeFailed, errMockStreamClosing.Error())))
close(done)
}()
nonce1 := []byte("foobar")
nonce2 := []byte("raboof")
divNonceChan <- nonce1
divNonceChan <- nonce2
err := cs.SetDiversificationNonce(nonce1)
Expect(err).ToNot(HaveOccurred())
err = cs.SetDiversificationNonce(nonce2)
Expect(err).To(MatchError(errConflictingDiversificationNonces))
// make the go routine return
stream.close()
Eventually(done).Should(BeClosed())
})
})