mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
fuzz: use per-level fetch scopes in coinscache_sim
Keep one StartFetching guard per cache level instead of a single guard for the top level, so overlays continue fetching while new cache levels are added on top. Tear the guards down top down, since resetting a lower cache while an upper overlay's workers read through it would cause a data race. Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
This commit is contained in:
@@ -226,8 +226,8 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
CoinsViewBottom bottom;
|
||||
/** Real CCoinsViewCache objects. */
|
||||
std::vector<std::unique_ptr<CCoinsViewCache>> caches;
|
||||
/** Long-lived StartFetching guard (nullptr unless corresponding level is a CoinsViewOverlay). */
|
||||
std::unique_ptr<OverlayFetchScope> overlay_fetch_scope;
|
||||
/** Long-lived StartFetching guards, parallel to `caches` (entries are nullptr unless corresponding level is a CoinsViewOverlay). */
|
||||
std::vector<std::unique_ptr<OverlayFetchScope>> fetch_scopes;
|
||||
/** Simulated cache data (sim_caches[0] matches bottom, sim_caches[i+1] matches caches[i]). */
|
||||
CacheLevel sim_caches[MAX_CACHES + 1];
|
||||
/** Current height in the simulation. */
|
||||
@@ -265,6 +265,12 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
}
|
||||
};
|
||||
|
||||
/** Helper creating a fetch scope for the top cache (which must be a CoinsViewOverlay). */
|
||||
const auto make_fetch_scope{[&] {
|
||||
auto& overlay{static_cast<CoinsViewOverlay&>(*caches.back())};
|
||||
return std::make_unique<OverlayFetchScope>(overlay, data.block);
|
||||
}};
|
||||
|
||||
// Main simulation loop: read commands from the fuzzer input, and apply them
|
||||
// to both the real cache stack and the simulation.
|
||||
FuzzedDataProvider provider(buffer.data(), buffer.size());
|
||||
@@ -275,8 +281,10 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
// Make sure there is always at least one CCoinsViewCache.
|
||||
if (caches.empty()) {
|
||||
caches.emplace_back(new CCoinsViewCache(&bottom, /*deterministic=*/true));
|
||||
fetch_scopes.emplace_back();
|
||||
sim_caches[caches.size()].Wipe();
|
||||
}
|
||||
assert(caches.size() == fetch_scopes.size());
|
||||
|
||||
// Execute command.
|
||||
CallOneOf(
|
||||
@@ -403,17 +411,13 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
|
||||
[&]() { // Add a cache level (if not already at the max).
|
||||
if (caches.size() != MAX_CACHES) {
|
||||
if (overlay_fetch_scope) {
|
||||
overlay_fetch_scope.reset();
|
||||
sim_caches[caches.size()].Wipe();
|
||||
}
|
||||
// Apply to real caches.
|
||||
if (provider.ConsumeBool()) {
|
||||
caches.emplace_back(new CCoinsViewCache(&*caches.back(), /*deterministic=*/true));
|
||||
fetch_scopes.emplace_back();
|
||||
} else {
|
||||
caches.emplace_back(new CoinsViewOverlay(&*caches.back(), g_thread_pool, /*deterministic=*/true));
|
||||
auto& overlay{static_cast<CoinsViewOverlay&>(*caches.back())};
|
||||
overlay_fetch_scope = std::make_unique<OverlayFetchScope>(overlay, data.block);
|
||||
fetch_scopes.emplace_back(make_fetch_scope());
|
||||
}
|
||||
// Apply to simulation data.
|
||||
sim_caches[caches.size()].Wipe();
|
||||
@@ -423,7 +427,7 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
[&]() { // Remove a cache level.
|
||||
// Apply to real caches (this reduces caches.size(), implicitly doing the same on the simulation data).
|
||||
caches.back()->SanityCheck();
|
||||
overlay_fetch_scope.reset();
|
||||
fetch_scopes.pop_back();
|
||||
caches.pop_back();
|
||||
},
|
||||
|
||||
@@ -440,7 +444,7 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
},
|
||||
|
||||
[&]() { // Sync.
|
||||
if (overlay_fetch_scope) return; // CoinsViewOverlay::Sync() is never called in production
|
||||
if (fetch_scopes.back()) return; // CoinsViewOverlay::Sync() is never called in production
|
||||
// Apply to simulation data (note that in our simulation, syncing and flushing is the same thing).
|
||||
flush();
|
||||
// Apply to real caches.
|
||||
@@ -450,10 +454,9 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
[&]() { // Reset.
|
||||
sim_caches[caches.size()].Wipe();
|
||||
// Apply to real caches. Optionally start fetching again.
|
||||
if (overlay_fetch_scope && provider.ConsumeBool()) {
|
||||
overlay_fetch_scope.reset();
|
||||
auto& overlay{static_cast<CoinsViewOverlay&>(*caches.back())};
|
||||
overlay_fetch_scope = std::make_unique<OverlayFetchScope>(overlay, data.block);
|
||||
if (fetch_scopes.back() && provider.ConsumeBool()) {
|
||||
fetch_scopes.back().reset(); // Stop fetching before starting again.
|
||||
fetch_scopes.back() = make_fetch_scope();
|
||||
} else {
|
||||
(void)caches.back()->CreateResetGuard();
|
||||
}
|
||||
@@ -514,4 +517,7 @@ FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<
|
||||
assert(realcoin->nHeight == sim->second);
|
||||
}
|
||||
}
|
||||
|
||||
// Tear down the fetch scopes top down. Otherwise lower level could reset while upper level is reading from it.
|
||||
while (!fetch_scopes.empty()) fetch_scopes.pop_back();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user