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

[keyvault/azsecrets] make azsecrets.Client thread-safe #24032

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

strager
Copy link

@strager strager commented Jan 31, 2025

azsecrets.Client uses NewKeyVaultChallengePolicy. This policy is not goroutine-safe, violating the documented requirement that policies are goroutine-safe [1]. This leads to data races which are reported by Go's race detector.

Fix NewKeyVaultChallengePolicy to be goroutine-safe using a mutex. This can lead to redundant preflight requests, but at least Go's race detector no longer complains.

Test plan:

$ cd sdk/security/keyvault/internal/
$ go test -race

Fixes #24031.

[1] https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-core-concepts

  • The purpose of this PR is explained in this or a referenced issue.
  • The PR does not update generated files.
  • Tests are included and/or updated for code changes.
  • Updates to module CHANGELOG.md are included.
  • MIT license headers are included in each file.

@github-actions github-actions bot added Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. KeyVault labels Jan 31, 2025
Copy link

Thank you for your contribution @strager! We will review the pull request and get back to you soon.

Copy link
Author

@strager strager left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few comments for maintainers.

serverAuthenticateRequests := atomic.Int32{}
serverAuthenticatedRequests := atomic.Int32{}
var srv *httptest.Server
srv = httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other tests use mock.Server, but here I used httptest.Server for two reasons:

  1. mock.Server is documented to not be safe when used from multiple goroutines.
  2. Even if mock.Server was safe at a surface level (e.g. by adding locks), its interface doesn't allow me to handle out-of-order requests.

httptest.Server has its own issues, but it seemed like the least amount of code to get the test working.

require.GreaterOrEqual(t, int(serverAuthenticateRequests.Load()), 1, "client should have sent at least one preflight request")
require.LessOrEqual(t, int(serverAuthenticateRequests.Load()), concurrentRequestCount, "client should have sent no more preflight requests than client requests")
require.EqualValues(t, concurrentRequestCount, serverAuthenticatedRequests.Load(), "client preflight request count should equal server preflight request count")
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The real assertion is the -race flag of go test. The three assertions in the test just make sure the test issued the requests, so they're not doing too much. Should I make the assertions more sophisticated? Is there a way to document that this test is designed to work with -race?

@@ -7,6 +7,7 @@
### Breaking Changes

### Bugs Fixed
* Fixed data race when using Client from multiple goroutines concurrently.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we reference issue numbers here?

azsecrets.Client uses NewKeyVaultChallengePolicy. This policy is not
goroutine-safe, violating the documented requirement that policies are
goroutine-safe [1]. This leads to data races which are reported by Go's race
detector.

Fix NewKeyVaultChallengePolicy to be goroutine-safe using a mutex. This can lead
to redundant preflight requests, but at least Go's race detector no longer
complains.

Test plan:

    $ cd sdk/security/keyvault/internal/
    $ go test -race

[1] https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-core-concepts
@strager strager force-pushed the keyvault-thread-safety branch from 3d3cf5c to 7adba6e Compare February 3, 2025 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. KeyVault
Projects
Status: Untriaged
Development

Successfully merging this pull request may close these issues.

KeyVaultChallengePolicy violates guarantees because it is not goroutine-safe
3 participants