Fix nested locks in ValidatorList:

* Found several functions called under lock that take a lock. Refactor
  to require a lock as a parameter instead.
* Found several functions called under lock that don't take a lock, but
  should. Refactored those as well to require a lock as a parameter.
This commit is contained in:
Edward Hennis
2020-12-10 15:27:47 -05:00
committed by Nik Bougalis
parent 8173d1f643
commit 6eb93e9f8a
2 changed files with 107 additions and 28 deletions

View File

@@ -141,6 +141,8 @@ class ValidatorList
boost::filesystem::path const dataPath_; boost::filesystem::path const dataPath_;
beast::Journal const j_; beast::Journal const j_;
boost::shared_mutex mutable mutex_; boost::shared_mutex mutable mutex_;
using unique_lock = std::unique_lock<boost::shared_mutex>;
using shared_lock = std::shared_lock<boost::shared_mutex>;
std::atomic<std::size_t> quorum_; std::atomic<std::size_t> quorum_;
boost::optional<std::size_t> minimumQuorum_; boost::optional<std::size_t> minimumQuorum_;
@@ -506,7 +508,7 @@ public:
QuorumKeys QuorumKeys
getQuorumKeys() const getQuorumKeys() const
{ {
std::shared_lock read_lock{mutex_}; shared_lock read_lock{mutex_};
return {quorum_, trustedSigningKeys_}; return {quorum_, trustedSigningKeys_};
} }
@@ -542,15 +544,58 @@ public:
std::vector<std::shared_ptr<STValidation>>&& validations) const; std::vector<std::shared_ptr<STValidation>>&& validations) const;
private: private:
/** Return the number of configured validator list sites. */
std::size_t
count(shared_lock const&) const;
/** Returns `true` if public key is trusted
@param identity Validation public key
@par Thread Safety
May be called concurrently
*/
bool
trusted(shared_lock const&, PublicKey const& identity) const;
/** Returns master public key if public key is trusted
@param identity Validation public key
@return `boost::none` if key is not trusted
@par Thread Safety
May be called concurrently
*/
boost::optional<PublicKey>
getTrustedKey(shared_lock const&, PublicKey const& identity) const;
/** Return the time when the validator list will expire
@note This may be a time in the past if a published list has not
been updated since its expiration. It will be boost::none if any
configured published list has not been fetched.
@par Thread Safety
May be called concurrently
*/
boost::optional<TimeKeeper::time_point>
expires(shared_lock const&) const;
/** Get the filename used for caching UNLs /** Get the filename used for caching UNLs
*/ */
boost::filesystem::path boost::filesystem::path
GetCacheFileName(PublicKey const& pubKey); GetCacheFileName(unique_lock const&, PublicKey const& pubKey);
/** Write a JSON UNL to a cache file /** Write a JSON UNL to a cache file
*/ */
void void
CacheValidatorFile(PublicKey const& pubKey, PublisherList const& publisher); CacheValidatorFile(
unique_lock const& lock,
PublicKey const& pubKey,
PublisherList const& publisher);
/** Check response for trusted valid published list /** Check response for trusted valid published list
@@ -562,6 +607,7 @@ private:
*/ */
ListDisposition ListDisposition
verify( verify(
unique_lock const&,
Json::Value& list, Json::Value& list,
PublicKey& pubKey, PublicKey& pubKey,
std::string const& manifest, std::string const& manifest,
@@ -579,7 +625,7 @@ private:
Calling public member function is expected to lock mutex Calling public member function is expected to lock mutex
*/ */
bool bool
removePublisherList(PublicKey const& publisherKey); removePublisherList(unique_lock const&, PublicKey const& publisherKey);
/** Return quorum for trusted validator set /** Return quorum for trusted validator set

View File

@@ -195,20 +195,23 @@ ValidatorList::load(
} }
boost::filesystem::path boost::filesystem::path
ValidatorList::GetCacheFileName(PublicKey const& pubKey) ValidatorList::GetCacheFileName(
ValidatorList::unique_lock const&,
PublicKey const& pubKey)
{ {
return dataPath_ / (filePrefix_ + strHex(pubKey)); return dataPath_ / (filePrefix_ + strHex(pubKey));
} }
void void
ValidatorList::CacheValidatorFile( ValidatorList::CacheValidatorFile(
ValidatorList::unique_lock const& lock,
PublicKey const& pubKey, PublicKey const& pubKey,
PublisherList const& publisher) PublisherList const& publisher)
{ {
if (dataPath_.empty()) if (dataPath_.empty())
return; return;
boost::filesystem::path const filename = GetCacheFileName(pubKey); boost::filesystem::path const filename = GetCacheFileName(lock, pubKey);
boost::system::error_code ec; boost::system::error_code ec;
@@ -310,7 +313,7 @@ ValidatorList::applyList(
Json::Value list; Json::Value list;
PublicKey pubKey; PublicKey pubKey;
auto const result = verify(list, pubKey, manifest, blob, signature); auto const result = verify(lock, list, pubKey, manifest, blob, signature);
if (result != ListDisposition::accepted) if (result != ListDisposition::accepted)
{ {
if (result == ListDisposition::same_sequence && if (result == ListDisposition::same_sequence &&
@@ -429,7 +432,7 @@ ValidatorList::applyList(
} }
// Cache the validator list in a file // Cache the validator list in a file
CacheValidatorFile(pubKey, publisher); CacheValidatorFile(lock, pubKey, publisher);
return applyResult; return applyResult;
} }
@@ -452,7 +455,7 @@ ValidatorList::loadLists()
if (publisher.available) if (publisher.available)
continue; continue;
boost::filesystem::path const filename = GetCacheFileName(pubKey); boost::filesystem::path const filename = GetCacheFileName(lock, pubKey);
auto const fullPath{canonical(filename, ec)}; auto const fullPath{canonical(filename, ec)};
if (ec) if (ec)
@@ -489,6 +492,7 @@ ValidatorList::loadLists()
ListDisposition ListDisposition
ValidatorList::verify( ValidatorList::verify(
ValidatorList::unique_lock const& lock,
Json::Value& list, Json::Value& list,
PublicKey& pubKey, PublicKey& pubKey,
std::string const& manifest, std::string const& manifest,
@@ -507,7 +511,7 @@ ValidatorList::verify(
if (revoked && result == ManifestDisposition::accepted) if (revoked && result == ManifestDisposition::accepted)
{ {
removePublisherList(pubKey); removePublisherList(lock, pubKey);
publisherLists_.erase(pubKey); publisherLists_.erase(pubKey);
} }
@@ -557,13 +561,20 @@ ValidatorList::listed(PublicKey const& identity) const
return keyListings_.find(pubKey) != keyListings_.end(); return keyListings_.find(pubKey) != keyListings_.end();
} }
bool
ValidatorList::trusted(
ValidatorList::shared_lock const&,
PublicKey const& identity) const
{
auto const pubKey = validatorManifests_.getMasterKey(identity);
return trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end();
}
bool bool
ValidatorList::trusted(PublicKey const& identity) const ValidatorList::trusted(PublicKey const& identity) const
{ {
std::shared_lock read_lock{mutex_}; std::shared_lock read_lock{mutex_};
return trusted(read_lock, identity);
auto const pubKey = validatorManifests_.getMasterKey(identity);
return trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end();
} }
boost::optional<PublicKey> boost::optional<PublicKey>
@@ -578,16 +589,24 @@ ValidatorList::getListedKey(PublicKey const& identity) const
} }
boost::optional<PublicKey> boost::optional<PublicKey>
ValidatorList::getTrustedKey(PublicKey const& identity) const ValidatorList::getTrustedKey(
ValidatorList::shared_lock const&,
PublicKey const& identity) const
{ {
std::shared_lock read_lock{mutex_};
auto const pubKey = validatorManifests_.getMasterKey(identity); auto const pubKey = validatorManifests_.getMasterKey(identity);
if (trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end()) if (trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end())
return pubKey; return pubKey;
return boost::none; return boost::none;
} }
boost::optional<PublicKey>
ValidatorList::getTrustedKey(PublicKey const& identity) const
{
std::shared_lock read_lock{mutex_};
return getTrustedKey(read_lock, identity);
}
bool bool
ValidatorList::trustedPublisher(PublicKey const& identity) const ValidatorList::trustedPublisher(PublicKey const& identity) const
{ {
@@ -603,7 +622,9 @@ ValidatorList::localPublicKey() const
} }
bool bool
ValidatorList::removePublisherList(PublicKey const& publisherKey) ValidatorList::removePublisherList(
ValidatorList::unique_lock const&,
PublicKey const& publisherKey)
{ {
auto const iList = publisherLists_.find(publisherKey); auto const iList = publisherLists_.find(publisherKey);
if (iList == publisherLists_.end()) if (iList == publisherLists_.end())
@@ -631,16 +652,21 @@ ValidatorList::removePublisherList(PublicKey const& publisherKey)
} }
std::size_t std::size_t
ValidatorList::count() const ValidatorList::count(ValidatorList::shared_lock const&) const
{ {
std::shared_lock read_lock{mutex_};
return publisherLists_.size(); return publisherLists_.size();
} }
boost::optional<TimeKeeper::time_point> std::size_t
ValidatorList::expires() const ValidatorList::count() const
{ {
std::shared_lock read_lock{mutex_}; std::shared_lock read_lock{mutex_};
return count(read_lock);
}
boost::optional<TimeKeeper::time_point>
ValidatorList::expires(ValidatorList::shared_lock const&) const
{
boost::optional<TimeKeeper::time_point> res{boost::none}; boost::optional<TimeKeeper::time_point> res{boost::none};
for (auto const& p : publisherLists_) for (auto const& p : publisherLists_)
{ {
@@ -655,6 +681,13 @@ ValidatorList::expires() const
return res; return res;
} }
boost::optional<TimeKeeper::time_point>
ValidatorList::expires() const
{
std::shared_lock read_lock{mutex_};
return expires(read_lock);
}
Json::Value Json::Value
ValidatorList::getJson() const ValidatorList::getJson() const
{ {
@@ -662,14 +695,14 @@ ValidatorList::getJson() const
std::shared_lock read_lock{mutex_}; std::shared_lock read_lock{mutex_};
res[jss::validation_quorum] = static_cast<Json::UInt>(quorum()); res[jss::validation_quorum] = static_cast<Json::UInt>(quorum_);
{ {
auto& x = (res[jss::validator_list] = Json::objectValue); auto& x = (res[jss::validator_list] = Json::objectValue);
x[jss::count] = static_cast<Json::UInt>(count()); x[jss::count] = static_cast<Json::UInt>(count(read_lock));
if (auto when = expires()) if (auto when = expires(read_lock))
{ {
if (*when == TimeKeeper::time_point::max()) if (*when == TimeKeeper::time_point::max())
{ {
@@ -767,7 +800,7 @@ ValidatorList::for_each_listed(
std::shared_lock read_lock{mutex_}; std::shared_lock read_lock{mutex_};
for (auto const& v : keyListings_) for (auto const& v : keyListings_)
func(v.first, trusted(v.first)); func(v.first, trusted(read_lock, v.first));
} }
void void
@@ -903,7 +936,7 @@ ValidatorList::updateTrusted(hash_set<NodeID> const& seenValidators)
{ {
if (list.second.available && if (list.second.available &&
list.second.expiration <= timeKeeper_.now()) list.second.expiration <= timeKeeper_.now())
removePublisherList(list.first); removePublisherList(lock, list.first);
} }
TrustChanges trustChanges; TrustChanges trustChanges;
@@ -997,7 +1030,7 @@ ValidatorList::getNegativeUNL() const
void void
ValidatorList::setNegativeUNL(hash_set<PublicKey> const& negUnl) ValidatorList::setNegativeUNL(hash_set<PublicKey> const& negUnl)
{ {
std::lock_guard lock{mutex_}; std::unique_lock lock{mutex_};
negativeUNL_ = negUnl; negativeUNL_ = negUnl;
} }
@@ -1017,7 +1050,7 @@ ValidatorList::negativeUNLFilter(
ret.end(), ret.end(),
[&](auto const& v) -> bool { [&](auto const& v) -> bool {
if (auto const masterKey = if (auto const masterKey =
getTrustedKey(v->getSignerPublic()); getTrustedKey(read_lock, v->getSignerPublic());
masterKey) masterKey)
{ {
return negativeUNL_.count(*masterKey); return negativeUNL_.count(*masterKey);