apple / swift-nio-ssl

TLS Support for SwiftNIO, based on BoringSSL.
https://swiftpackageindex.com/apple/swift-nio-ssl/main/documentation/niossl
Apache License 2.0
385 stars 139 forks source link

100% CPU spin loop forever when `writeAndFlush` gets run from `channelActive` #467

Open weissi opened 2 weeks ago

weissi commented 2 weeks ago

NIOSSL has the following loop

            bufferedActionsLoop: while bufferedActions.hasMark {         // JW: <<<----- loop
                let element = bufferedActions.first!
                switch element {
                case .write(let bufferedWrite):
                    var data = bufferedWrite.data
                    let writeSuccessful = try self._encodeSingleWrite(buf: &data)
                    if writeSuccessful {      // JW: <<<----- `writeSuccessful` will be `false`
                        didWrite = true
                        if let promise = bufferedWrite.promise { promises.append(promise) }
                        _ = bufferedActions.removeFirst()
                    }
                case .closeOutput:
                    [...]
            }  // JW: <<<----- this will spin around again and again and again

Precisely what happens is this:

weissi commented 2 weeks ago

Thanks @glbrntt , this could be a reentrancy bug

    public func channelActive(context: ChannelHandlerContext) {
        // We fire this a bit early, entirely on purpose. This is because
        // in doHandshakeStep we may end up closing the channel again, and
        // if we do we want to make sure that the channelInactive message received
        // by later channel handlers makes sense.
        context.fireChannelActive()
        doHandshakeStep(context: context)
    }

So we're calling out before calling doHandshakeStep. This intern then triggers the implicit handshake in BoringSSL. Maybe we think that we'd never trigger the implicit handshake.

Possible that reversing those two lines will fix it because we never do the implicit handshake in BoringSSL.

weissi commented 2 weeks ago

NIOSSLHandler sees the following channel events:

Obviously those channel events are wrong. That's a bug in NIO too.