peer: send messages in Start via writeMessage to bypass writeHandler

Without this the following could happen:

* InboundPeerConnected is called while we already have an inbound
connection with the peer. This calls removePeer which calls Disconnect.
* If the peer is starting up in Start, it may be sending messages
synchronously via SendMessage(true, ...). This eventually calls the
writeMessage function which will exit if disconnect is set to 1.
* Since Disconnect was called, disconnect will be 1 and writeMessage
will exit, causing writeHandler to exit.
* If there is more than 1 message being sent, later messages will
queue in queueHandler but be unable to get into sendQueue as the
writeHandler goroutine has exited.
* The synchronous sends will be waiting on the errChan indefinitely
and startReady will never get closed meaning Disconnect will never
proceed.

The end result is that the server's mutex will be held until shutdown.

Avoid this by using writeMessage to bypass the writeHandler goroutine.
This commit is contained in:
Eugene Siegel 2023-11-07 11:17:27 -05:00 committed by eugene
parent dacb86fdbc
commit 955a345153
No known key found for this signature in database
GPG Key ID: 118759E83439A9B1

View File

@ -722,9 +722,15 @@ func (p *Brontide) Start() error {
if len(msgs) > 0 {
p.log.Infof("Sending %d channel sync messages to peer after "+
"loading active channels", len(msgs))
if err := p.SendMessage(true, msgs...); err != nil {
p.log.Warnf("Failed sending channel sync "+
"messages to peer: %v", err)
// Send the messages directly via writeMessage and bypass the
// writeHandler goroutine to avoid cases where writeHandler
// may exit and cause a deadlock.
for _, msg := range msgs {
if err := p.writeMessage(msg); err != nil {
return fmt.Errorf("unable to send reestablish"+
"msg: %v", err)
}
}
}
@ -2088,11 +2094,6 @@ func (p *Brontide) logWireMessage(msg lnwire.Message, read bool) {
// with a nil message iff a timeout error is returned. This will continue to
// flush the pending message to the wire.
func (p *Brontide) writeMessage(msg lnwire.Message) error {
// Simply exit if we're shutting down.
if atomic.LoadInt32(&p.disconnect) != 0 {
return lnpeer.ErrPeerExiting
}
// Only log the message on the first attempt.
if msg != nil {
p.logWireMessage(msg, false)