Skip to content

Commit 1dc9179

Browse files
bdash0cyn
authored andcommitted
[SharedCache] Split view-specific state into a separate struct
The existing view-specific state was stored in several global unordered maps. Many of these were accessed without locking, including `viewSpecificMutexes`, which is racy in the face of multiple threads. View-specific state is stored in a new heap-allocated `ViewSpecificState` struct that is reference counted via `std::shared_ptr`. A static map holds a `std::weak_ptr` to each view-specific state, keyed by session id. `SharedCache` retrieves its view-specific state during its constructor. Since `ViewSpecificState` is reference counted it will naturally be deallocated when the last `SharedCache` instance that references it goes away. Its corresponding entry will remain in the static map, though since it only holds a `std::weak_ptr` rather than any state it will not use much memory. The next time view-specific state is retrieved any expired entries will be removed from the map.
1 parent 7630dcd commit 1dc9179

File tree

2 files changed

+96
-71
lines changed

2 files changed

+96
-71
lines changed

view/sharedcache/core/SharedCache.cpp

Lines changed: 93 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "SharedCache.h"
2828
#include "ObjC.h"
2929
#include <filesystem>
30+
#include <mutex>
31+
#include <unordered_map>
3032
#include <utility>
3133
#include <fcntl.h>
3234
#include <memory>
@@ -78,19 +80,51 @@ struct SharedCache::State
7880
DSCViewState viewState = DSCViewStateUnloaded;
7981
};
8082

81-
static std::recursive_mutex viewStateMutex;
82-
static std::unordered_map<uint64_t, std::shared_ptr<struct SharedCache::State>> viewStateCache;
83+
struct SharedCache::ViewSpecificState {
84+
std::mutex typeLibraryMutex;
85+
std::mutex viewOperationsThatInfluenceMetadataMutex;
8386

84-
std::mutex progressMutex;
85-
std::unordered_map<uint64_t, BNDSCViewLoadProgress> progressMap;
87+
std::atomic<BNDSCViewLoadProgress> progress;
8688

87-
struct ViewSpecificMutexes {
88-
std::mutex viewOperationsThatInfluenceMetadataMutex;
89-
std::mutex typeLibraryLookupAndApplicationMutex;
89+
std::mutex stateMutex;
90+
std::shared_ptr<struct SharedCache::State> cachedState;
9091
};
9192

92-
static std::unordered_map<uint64_t, ViewSpecificMutexes> viewSpecificMutexes;
9393

94+
std::shared_ptr<SharedCache::ViewSpecificState> ViewSpecificStateForId(uint64_t viewIdentifier, bool insertIfNeeded = true) {
95+
static std::mutex viewSpecificStateMutex;
96+
static std::unordered_map<uint64_t, std::weak_ptr<SharedCache::ViewSpecificState>> viewSpecificState;
97+
98+
std::lock_guard lock(viewSpecificStateMutex);
99+
100+
if (auto it = viewSpecificState.find(viewIdentifier); it != viewSpecificState.end()) {
101+
if (auto statePtr = it->second.lock()) {
102+
return statePtr;
103+
}
104+
}
105+
106+
if (!insertIfNeeded) {
107+
return nullptr;
108+
}
109+
110+
auto statePtr = std::make_shared<SharedCache::ViewSpecificState>();
111+
viewSpecificState[viewIdentifier] = statePtr;
112+
113+
// Prune entries for any views that are no longer in use.
114+
for (auto it = viewSpecificState.begin(); it != viewSpecificState.end(); ) {
115+
if (it->second.expired()) {
116+
it = viewSpecificState.erase(it);
117+
} else {
118+
++it;
119+
}
120+
}
121+
122+
return statePtr;
123+
}
124+
125+
std::shared_ptr<SharedCache::ViewSpecificState> ViewSpecificStateForView(Ref<BinaryNinja::BinaryView> view) {
126+
return ViewSpecificStateForId(view->GetFile()->GetSessionId());
127+
}
94128

95129
std::string base_name(std::string const& path)
96130
{
@@ -220,9 +254,7 @@ void SharedCache::PerformInitialLoad()
220254
auto path = m_dscView->GetFile()->GetOriginalFilename();
221255
auto baseFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), path)->lock();
222256

223-
progressMutex.lock();
224-
progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingCaches;
225-
progressMutex.unlock();
257+
m_viewSpecificState->progress = LoadProgressLoadingCaches;
226258

227259
WillMutateState();
228260

@@ -737,9 +769,8 @@ void SharedCache::PerformInitialLoad()
737769
}
738770
}
739771
baseFile.reset();
740-
progressMutex.lock();
741-
progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingImages;
742-
progressMutex.unlock();
772+
773+
m_viewSpecificState->progress = LoadProgressLoadingImages;
743774

744775
// We have set up enough metadata to map VM now.
745776

@@ -952,9 +983,7 @@ void SharedCache::PerformInitialLoad()
952983

953984
m_logger->LogDebug("Finished initial load of Shared Cache");
954985

955-
progressMutex.lock();
956-
progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished;
957-
progressMutex.unlock();
986+
m_viewSpecificState->progress = LoadProgressFinished;
958987
}
959988

960989
std::shared_ptr<VM> SharedCache::GetVMMap(bool mapPages)
@@ -983,10 +1012,10 @@ void SharedCache::DeserializeFromRawView()
9831012
{
9841013
if (m_dscView->QueryMetadata(SharedCacheMetadataTag))
9851014
{
986-
std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
987-
if (auto it = viewStateCache.find(m_dscView->GetFile()->GetSessionId()); it != viewStateCache.end())
1015+
std::lock_guard lock(m_viewSpecificState->stateMutex);
1016+
if (m_viewSpecificState->cachedState)
9881017
{
989-
m_state = it->second;
1018+
m_state = m_viewSpecificState->cachedState;
9901019
m_stateIsShared = true;
9911020
m_metadataValid = true;
9921021
}
@@ -1367,7 +1396,7 @@ void SharedCache::ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAcces
13671396
}
13681397

13691398

1370-
SharedCache::SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView) : m_dscView(dscView)
1399+
SharedCache::SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView) : m_dscView(dscView), m_viewSpecificState(ViewSpecificStateForView(dscView))
13711400
{
13721401
if (dscView->GetTypeName() != VIEW_NAME)
13731402
{
@@ -1381,46 +1410,43 @@ SharedCache::SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView) : m_
13811410
DeserializeFromRawView();
13821411
if (!m_metadataValid)
13831412
return;
1384-
if (State().viewState == DSCViewStateUnloaded)
1413+
1414+
if (State().viewState != DSCViewStateUnloaded) {
1415+
m_viewSpecificState->progress = LoadProgressFinished;
1416+
return;
1417+
}
1418+
1419+
std::unique_lock lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
1420+
try {
1421+
PerformInitialLoad();
1422+
}
1423+
catch (...)
13851424
{
1386-
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
1387-
try {
1388-
PerformInitialLoad();
1389-
}
1390-
catch (...)
1391-
{
1392-
m_logger->LogError("Failed to perform initial load of Shared Cache");
1393-
}
1425+
m_logger->LogError("Failed to perform initial load of Shared Cache");
1426+
}
13941427

1395-
auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
1396-
bool autoLoadLibsystem = true;
1397-
if (settings && settings->Contains("loader.dsc.autoLoadLibSystem"))
1398-
{
1399-
autoLoadLibsystem = settings->Get<bool>("loader.dsc.autoLoadLibSystem", m_dscView);
1400-
}
1401-
if (autoLoadLibsystem)
1428+
auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
1429+
bool autoLoadLibsystem = true;
1430+
if (settings && settings->Contains("loader.dsc.autoLoadLibSystem"))
1431+
{
1432+
autoLoadLibsystem = settings->Get<bool>("loader.dsc.autoLoadLibSystem", m_dscView);
1433+
}
1434+
if (autoLoadLibsystem)
1435+
{
1436+
for (const auto& [_, header] : State().headers)
14021437
{
1403-
for (const auto& [_, header] : State().headers)
1438+
if (header.installName.find("libsystem_c.dylib") != std::string::npos)
14041439
{
1405-
if (header.installName.find("libsystem_c.dylib") != std::string::npos)
1406-
{
1407-
lock.unlock();
1408-
m_logger->LogInfo("Loading core libsystem_c.dylib library");
1409-
LoadImageWithInstallName(header.installName, false);
1410-
lock.lock();
1411-
break;
1412-
}
1440+
lock.unlock();
1441+
m_logger->LogInfo("Loading core libsystem_c.dylib library");
1442+
LoadImageWithInstallName(header.installName, false);
1443+
break;
14131444
}
14141445
}
1415-
MutableState().viewState = DSCViewStateLoaded;
1416-
SaveToDSCView();
1417-
}
1418-
else
1419-
{
1420-
progressMutex.lock();
1421-
progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished;
1422-
progressMutex.unlock();
14231446
}
1447+
1448+
MutableState().viewState = DSCViewStateLoaded;
1449+
SaveToDSCView();
14241450
}
14251451

14261452
SharedCache::~SharedCache() {
@@ -1536,7 +1562,7 @@ bool SharedCache::LoadImageContainingAddress(uint64_t address, bool skipObjC)
15361562

15371563
bool SharedCache::LoadSectionAtAddress(uint64_t address)
15381564
{
1539-
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
1565+
std::unique_lock lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
15401566
DeserializeFromRawView();
15411567
WillMutateState();
15421568

@@ -1806,7 +1832,7 @@ bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObj
18061832
{
18071833
auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
18081834

1809-
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
1835+
std::unique_lock lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
18101836

18111837
DeserializeFromRawView();
18121838
WillMutateState();
@@ -1880,7 +1906,7 @@ bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObj
18801906
return false;
18811907
}
18821908

1883-
std::unique_lock<std::mutex> typelibLock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex);
1909+
std::unique_lock typelibLock(m_viewSpecificState->typeLibraryMutex);
18841910
auto typeLib = m_dscView->GetTypeLibrary(header.installName);
18851911

18861912
if (!typeLib)
@@ -2875,7 +2901,7 @@ std::vector<std::pair<std::string, Ref<Symbol>>> SharedCache::LoadAllSymbolsAndW
28752901
{
28762902
WillMutateState();
28772903

2878-
std::unique_lock<std::mutex> initialLoadBlock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
2904+
std::lock_guard initialLoadBlock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
28792905

28802906
std::vector<std::pair<std::string, Ref<Symbol>>> symbols;
28812907
for (const auto& img : State().images)
@@ -2973,7 +2999,7 @@ void SharedCache::FindSymbolAtAddrAndApplyToAddr(
29732999
}
29743000
auto exportList = SharedCache::ParseExportTrie(mapping, *header);
29753001
std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
2976-
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex);
3002+
std::unique_lock lock(m_viewSpecificState->typeLibraryMutex);
29773003
auto typeLib = m_dscView->GetTypeLibrary(header->installName);
29783004
if (!typeLib)
29793005
{
@@ -3020,7 +3046,7 @@ void SharedCache::FindSymbolAtAddrAndApplyToAddr(
30203046
}
30213047
}
30223048
{
3023-
std::unique_lock<std::mutex> _lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
3049+
std::lock_guard lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
30243050
MutableState().exportInfos[header->textBase] = std::move(exportMapping);
30253051
}
30263052
m_dscView->EndBulkModifySymbols();
@@ -3044,8 +3070,8 @@ bool SharedCache::SaveToDSCView()
30443070
m_state = cachedState;
30453071
m_stateIsShared = true;
30463072

3047-
std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
3048-
viewStateCache[m_dscView->GetFile()->GetSessionId()] = std::move(cachedState);
3073+
std::lock_guard lock(m_viewSpecificState->stateMutex);
3074+
m_viewSpecificState->cachedState = std::move(cachedState);
30493075

30503076
m_metadataValid = true;
30513077

@@ -3055,7 +3081,7 @@ bool SharedCache::SaveToDSCView()
30553081
}
30563082
std::vector<MemoryRegion> SharedCache::GetMappedRegions() const
30573083
{
3058-
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
3084+
std::lock_guard lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
30593085
return State().regionsMappedIntoMemory;
30603086
}
30613087

@@ -3392,15 +3418,11 @@ extern "C"
33923418

33933419
BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID)
33943420
{
3395-
progressMutex.lock();
3396-
if (progressMap.find(sessionID) == progressMap.end())
3397-
{
3398-
progressMutex.unlock();
3399-
return LoadProgressNotStarted;
3421+
if (auto viewSpecificState = ViewSpecificStateForId(sessionID, false)) {
3422+
return viewSpecificState->progress;
34003423
}
3401-
auto progress = progressMap[sessionID];
3402-
progressMutex.unlock();
3403-
return progress;
3424+
3425+
return LoadProgressNotStarted;
34043426
}
34053427

34063428
uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* data)

view/sharedcache/core/SharedCache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,8 @@ namespace SharedCacheCore {
538538

539539
struct State;
540540

541+
struct ViewSpecificState;
542+
541543
private:
542544
Ref<Logger> m_logger;
543545
/* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */
@@ -552,6 +554,7 @@ namespace SharedCacheCore {
552554
bool m_metadataValid = false;
553555

554556
/* VIEWSTATE END -- NOTHING PAST THIS IS SERIALIZED */
557+
std::shared_ptr<ViewSpecificState> m_viewSpecificState;
555558

556559
/* API VIEW START */
557560
BinaryNinja::Ref<BinaryNinja::BinaryView> m_dscView;

0 commit comments

Comments
 (0)