-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
api/client.go#clone() re-claims a non-re-entrant lock #22393
Comments
Thanks for the report! Very good bug report and a real, identified bug. I'm going to look into a fix for this once I free up. |
Hello there, I have created a PR the both reproduced the problem and fixed the problem. |
It seems we almost had the same idea! I have a PR too, here: #22410 I fixed up a few other areas and moved a little more locking out of the top level functions. If it's okay with you, I think it might be better to close your PR and merge mine, as I have a little extra clean-up and a closer similarity to naming we have around other similar methods (e.g. Thanks a tonne for the report! Great description and easy to reproduce. Apologies that we both worked on the PR at the same time, I should have been clearer! |
Remove recursive locking from CloneWithHeaders() in client.go golang doesn't support reentrant locks in general. hashicorp#22393 has all the details.
Describe the bug
In api/client.go, clone() claims the same read lock twice. The second time is via Headers().
The doc on RWMutex says in here and here that re-entrance is not allowed.
We ran into this problem when a goroutine called CloneWithHeaders() and another called SetCloneToken().
After CloneWithHeaders() acquired the first read lock, SetCloneToken() tried to claim a write lock. As a result,
no new read lock could be claimed and goroutine one couldn't release the existing read lock. As a result,
goroutine two couldn't acquire the write lock. Below are the stacktraces.
To Reproduce
Steps to reproduce the behavior:
To reproduce this issue, you need to have a small piece of code that has the following operations.
We had them back-to-back.
Then you need to execute this small piece of code with multiple threads non-stop.
You will see the problem if you are (un)lucky enough.
Note that regardless of the reproduction, please consider Go's documentation on RWMutex.
We use pprof to spy on the goroutines.
Expected behavior
No deadlocking.
Environment:
vault status
): 1.12.0vault version
): v1.8.3Vault server configuration file(s):
n/a
Additional context
A problem I can see that makes client.go risky is that top-level functions can invoke each other.
I think if you structure the code so that only top-level functions can claim locks and that top-level
functions cannot invoke each other, then it's unlikely for lock-related issues to take place.
See this post on why golang doesn't offer re-entrant locks in the first place.
The text was updated successfully, but these errors were encountered: