mirror of
https://github.com/NixOS/nix.git
synced 2025-11-08 19:46:02 +01:00
libstore: Fix reentrancy in AwsCredentialProviderImpl::getCredentialsRaw
Old code would do very much incorrect reentrancy crimes (trying to do an
erase inside the emplace callback). This would fail miserably with an assertion
in Boost:
terminating due to unexpected unrecoverable internal error: Assertion '(!find(px))&&("reentrancy not allowed")' failed in boost::unordered::detail::foa::entry_trace::entry_trace(const void *) at include/boost/unordered/detail/foa/reentrancy_check.hpp:33
This is trivially reproduced by using any S3 URL with a non-empty profile:
nix-prefetch-url "s3://happy/crash?profile=default"
This commit is contained in:
parent
3c03050cd6
commit
c663f7ec79
1 changed files with 36 additions and 46 deletions
|
|
@ -96,67 +96,57 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> createProviderForProfile(const std::string & profile);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Aws::Crt::ApiHandle apiHandle;
|
Aws::Crt::ApiHandle apiHandle;
|
||||||
boost::concurrent_flat_map<std::string, std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider>>
|
boost::concurrent_flat_map<std::string, std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider>>
|
||||||
credentialProviderCache;
|
credentialProviderCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
AwsCredentials AwsCredentialProviderImpl::getCredentialsRaw(const std::string & profile)
|
std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider>
|
||||||
|
AwsCredentialProviderImpl::createProviderForProfile(const std::string & profile)
|
||||||
{
|
{
|
||||||
// Get or create credential provider with caching
|
|
||||||
std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> provider;
|
|
||||||
|
|
||||||
// Use try_emplace_and_cvisit for atomic get-or-create
|
|
||||||
// This prevents race conditions where multiple threads create providers
|
|
||||||
credentialProviderCache.try_emplace_and_cvisit(
|
|
||||||
profile,
|
|
||||||
nullptr, // Placeholder - will be replaced in f1 before any thread can see it
|
|
||||||
[&](auto & kv) {
|
|
||||||
// f1: Called atomically during insertion with non-const reference
|
|
||||||
// Other threads are blocked until we finish, so nullptr is never visible
|
|
||||||
debug(
|
debug(
|
||||||
"[pid=%d] creating new AWS credential provider for profile '%s'",
|
"[pid=%d] creating new AWS credential provider for profile '%s'",
|
||||||
getpid(),
|
getpid(),
|
||||||
profile.empty() ? "(default)" : profile.c_str());
|
profile.empty() ? "(default)" : profile.c_str());
|
||||||
|
|
||||||
try {
|
|
||||||
if (profile.empty()) {
|
if (profile.empty()) {
|
||||||
Aws::Crt::Auth::CredentialsProviderChainDefaultConfig config;
|
Aws::Crt::Auth::CredentialsProviderChainDefaultConfig config;
|
||||||
config.Bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
config.Bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
||||||
kv.second = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderChainDefault(config);
|
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderChainDefault(config);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
Aws::Crt::Auth::CredentialsProviderProfileConfig config;
|
Aws::Crt::Auth::CredentialsProviderProfileConfig config;
|
||||||
config.Bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
config.Bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
||||||
// This is safe because the underlying C library will copy this string
|
// This is safe because the underlying C library will copy this string
|
||||||
// c.f. https://github.com/awslabs/aws-c-auth/blob/main/source/credentials_provider_profile.c#L220
|
// c.f. https://github.com/awslabs/aws-c-auth/blob/main/source/credentials_provider_profile.c#L220
|
||||||
config.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile.c_str());
|
config.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile.c_str());
|
||||||
kv.second = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderProfile(config);
|
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderProfile(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AwsCredentials AwsCredentialProviderImpl::getCredentialsRaw(const std::string & profile)
|
||||||
|
{
|
||||||
|
std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> provider;
|
||||||
|
|
||||||
|
credentialProviderCache.try_emplace_and_cvisit(
|
||||||
|
profile,
|
||||||
|
nullptr,
|
||||||
|
[&](auto & kv) { provider = kv.second = createProviderForProfile(profile); },
|
||||||
|
[&](const auto & kv) { provider = kv.second; });
|
||||||
|
|
||||||
|
if (!provider) {
|
||||||
|
credentialProviderCache.erase_if(profile, [](const auto & kv) {
|
||||||
|
[[maybe_unused]] auto [_, provider] = kv;
|
||||||
|
return !provider;
|
||||||
|
});
|
||||||
|
|
||||||
if (!kv.second) {
|
|
||||||
throw AwsAuthError(
|
throw AwsAuthError(
|
||||||
"Failed to create AWS credentials provider for %s",
|
"Failed to create AWS credentials provider for %s",
|
||||||
profile.empty() ? "default profile" : fmt("profile '%s'", profile));
|
profile.empty() ? "default profile" : fmt("profile '%s'", profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
provider = kv.second;
|
|
||||||
} catch (Error & e) {
|
|
||||||
// Exception during creation - remove the entry to allow retry
|
|
||||||
credentialProviderCache.erase(profile);
|
|
||||||
e.addTrace({}, "for AWS profile: %s", profile.empty() ? "(default)" : profile);
|
|
||||||
throw;
|
|
||||||
} catch (...) {
|
|
||||||
// Non-Error exception - still need to clean up
|
|
||||||
credentialProviderCache.erase(profile);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[&](const auto & kv) {
|
|
||||||
// f2: Called if key already exists (const reference)
|
|
||||||
provider = kv.second;
|
|
||||||
});
|
|
||||||
|
|
||||||
return getCredentialsFromProvider(provider);
|
return getCredentialsFromProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue