Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deadlock when LDAP server sends unexpected response packet #68

Closed
jlhawn opened this issue Jun 16, 2016 · 1 comment · Fixed by #69
Closed

Deadlock when LDAP server sends unexpected response packet #68

jlhawn opened this issue Jun 16, 2016 · 1 comment · Fixed by #69

Comments

@jlhawn
Copy link
Contributor

jlhawn commented Jun 16, 2016

I have encountered an issue where a Bind Request to a particular LDAP server response with more than one response packet for the corresponding request messageID. The Bind() method only expects a single response which it reads and then returns (running a deferred l.finishMessage(messageID)).

In the meantime, the processMessages() goroutine is blocking on sending this unexpected extra response packet to the result channel for the Bind request, but the Bind() method never receives.

Going back to the deferred l.finishMessage(messageID): The Bind() method wont return until this deferred call finishes but it is blocked on sending the MessageFinish message packet to the processMessages() goroutine which in turn is blocked because the Bind() method never receives from its channel. This is the deadlock.

It seems that this design for the proccesMessages() goroutine would, in general, always end up in deadlock if the server responds with more request packets than a request handler is expecting. This may be in violation of the protocol but I think that the client should be more resilient than this in handling a misbehaving server.

One solution that I've come up with is to make it so that calls to finishMessage() also signal to the processMessages() goroutine to stop blocking on an attempt to send a response packet for that message ID. Currently, the connection has a chanResults map which holds results channels which correspond to specific message IDs. We could replace this with a mapping of message IDs to context objects for that message. At the very least, this context would contain 2 items:

type messageContext struct {
    done      chan struct{}
    responses chan *PacketResponse
}

The job of finishMessage(messageID) would change to acquiring a lock on this message context mapping, closing the done channel, then send the MessageFinish packet. In processMessages() we will have to change the MessageResponse handler to have a select statement which attempts to either send the response packet or receive from the done channel. If this done channel is closed it will not block and return instantly. The value is not important, but this signals that the request handler is done and we can abandon sending response packets to it.

@jlhawn
Copy link
Contributor Author

jlhawn commented Jun 17, 2016

Please see #69 for a patch which implements this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant