Merge pull request #81 from mrexodia/breakpoint-lock

Fix race condition with breakpoint data structures
This commit is contained in:
Duncan Ogilvie 2026-08-30 13:40:19 +02:00 committed by GitHub
commit 0ee4126a92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 108 additions and 28 deletions

View File

@ -5,6 +5,8 @@ namespace GleeBug
{ {
void Debugger::exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance) void Debugger::exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance)
{ {
std::unique_lock<std::recursive_mutex> lock(mProcess->breakpointMutex);
//check if the breakpoint exists //check if the breakpoint exists
auto exceptionAddress = ptr(exceptionRecord.ExceptionAddress); auto exceptionAddress = ptr(exceptionRecord.ExceptionAddress);
auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Software, exceptionAddress }); auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Software, exceptionAddress });
@ -17,6 +19,7 @@ namespace GleeBug
mContinueStatus = DBG_CONTINUE; mContinueStatus = DBG_CONTINUE;
//call the callback //call the callback
lock.unlock();
cbSystemBreakpoint(); cbSystemBreakpoint();
} }
else else
@ -50,6 +53,8 @@ namespace GleeBug
} }
mProcess->StepInternal([this, info]() mProcess->StepInternal([this, info]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
//only restore the bytes if the breakpoint still exists //only restore the bytes if the breakpoint still exists
auto foundBreakpoint = mProcess->breakpoints.find({ BreakpointType::Software, info.address }); auto foundBreakpoint = mProcess->breakpoints.find({ BreakpointType::Software, info.address });
if(foundBreakpoint != mProcess->breakpoints.end()) if(foundBreakpoint != mProcess->breakpoints.end())
@ -64,13 +69,18 @@ namespace GleeBug
} }
}); });
BreakpointCallback breakpointCallback;
auto foundCallback = mProcess->breakpointCallbacks.find({ BreakpointType::Software, info.address });
if(foundCallback != mProcess->breakpointCallbacks.end())
breakpointCallback = foundCallback->second;
lock.unlock();
//call the generic callback //call the generic callback
cbBreakpoint(info); cbBreakpoint(info);
//call the user callback //call the user callback
auto foundCallback = mProcess->breakpointCallbacks.find({ BreakpointType::Software, info.address }); if(breakpointCallback)
if(foundCallback != mProcess->breakpointCallbacks.end()) breakpointCallback(info);
foundCallback->second(info);
//delete the breakpoint if it is singleshoot //delete the breakpoint if it is singleshoot
if(info.singleshoot) if(info.singleshoot)
@ -85,9 +95,6 @@ namespace GleeBug
mThread->isInternalStepping = false; mThread->isInternalStepping = false;
mContinueStatus = DBG_CONTINUE; mContinueStatus = DBG_CONTINUE;
// Internal step callbacks can re-arm a memory-breakpoint page. Serialize
// that map access with concurrent set/delete transactions.
std::lock_guard<std::recursive_mutex> lock(mProcess->memoryBreakpointMutex);
mThread->cbInternalStep(); mThread->cbInternalStep();
} }
if(mThread->isSingleStepping) //handle single step if(mThread->isSingleStepping) //handle single step
@ -142,6 +149,7 @@ namespace GleeBug
return; //not a hardware breakpoint return; //not a hardware breakpoint
//find the breakpoint in the internal structures //find the breakpoint in the internal structures
std::unique_lock<std::recursive_mutex> lock(mProcess->breakpointMutex);
auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Hardware, breakpointAddress }); auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Hardware, breakpointAddress });
if(foundInfo == mProcess->breakpoints.end()) if(foundInfo == mProcess->breakpoints.end())
return; //not a valid hardware breakpoint return; //not a valid hardware breakpoint
@ -156,21 +164,31 @@ namespace GleeBug
mThread->DeleteHardwareBreakpoint(breakpointSlot); mThread->DeleteHardwareBreakpoint(breakpointSlot);
mProcess->StepInternal([this, info]() mProcess->StepInternal([this, info]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
//only restore if the breakpoint still exists //only restore if the breakpoint still exists
if(mProcess->breakpoints.find({ BreakpointType::Hardware, info.address }) != mProcess->breakpoints.end()) if(mProcess->breakpoints.find({ BreakpointType::Hardware, info.address }) != mProcess->breakpoints.end())
mThread->SetHardwareBreakpoint(info.address, info.internal.hardware.slot, info.internal.hardware.type, info.internal.hardware.size); mThread->SetHardwareBreakpoint(info.address, info.internal.hardware.slot, info.internal.hardware.type, info.internal.hardware.size);
}); });
BreakpointCallback breakpointCallback;
auto foundCallback = mProcess->breakpointCallbacks.find({ BreakpointType::Hardware, info.address });
if(foundCallback != mProcess->breakpointCallbacks.end())
breakpointCallback = foundCallback->second;
lock.unlock();
//call the generic callback //call the generic callback
cbBreakpoint(info); cbBreakpoint(info);
//call the user callback //call the user callback
auto foundCallback = mProcess->breakpointCallbacks.find({ BreakpointType::Hardware, info.address }); if(breakpointCallback)
if(foundCallback != mProcess->breakpointCallbacks.end()) breakpointCallback(info);
foundCallback->second(info);
//if the breakpoint was deleted during callback, clear internal stepping to prevent thread suspension //if the breakpoint was deleted during callback, clear internal stepping to prevent thread suspension
if(mProcess->breakpoints.find({ BreakpointType::Hardware, info.address }) == mProcess->breakpoints.end()) lock.lock();
const bool breakpointDeleted = mProcess->breakpoints.find({ BreakpointType::Hardware, info.address }) == mProcess->breakpoints.end();
lock.unlock();
if(breakpointDeleted)
{ {
mThread->isInternalStepping = false; mThread->isInternalStepping = false;
Registers(mThread->hThread, CONTEXT_CONTROL).TrapFlag = false; Registers(mThread->hThread, CONTEXT_CONTROL).TrapFlag = false;
@ -185,13 +203,22 @@ namespace GleeBug
{ {
// Page protections are changed before set/delete publishes its metadata. // Page protections are changed before set/delete publishes its metadata.
// Wait for the transaction before classifying this memory-breakpoint fault. // Wait for the transaction before classifying this memory-breakpoint fault.
std::unique_lock<std::recursive_mutex> lock(mProcess->memoryBreakpointMutex); std::unique_lock<std::recursive_mutex> lock(mProcess->breakpointMutex);
/* /*
ASSUME: ASSUME:
exceptionAddress may or may not have been generated by your breakpoints. exceptionAddress may or may not have been generated by your breakpoints.
*/ */
char error[128] = ""; char error[128] = "";
auto reportError = [&]()
{
const bool relock = lock.owns_lock();
if(relock)
lock.unlock();
cbInternalError(error);
if(relock)
lock.lock();
};
auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]); auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]);
//check if the exception address is directly in the range of a memory breakpoint //check if the exception address is directly in the range of a memory breakpoint
@ -212,7 +239,7 @@ namespace GleeBug
if(!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect)) if(!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)foundPage->first); sprintf_s(error, "MemProtect failed on 0x%p", (void*)foundPage->first);
cbInternalError(error); reportError();
} }
//However the following situations may occur: //However the following situations may occur:
@ -223,6 +250,8 @@ namespace GleeBug
// then we ought to restore the protection. // then we ought to restore the protection.
mProcess->StepInternal([this, pBaseAddr]() mProcess->StepInternal([this, pBaseAddr]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
//seek out the page address //seek out the page address
auto found_page = mProcess->memoryBreakpointPages.find(pBaseAddr); auto found_page = mProcess->memoryBreakpointPages.find(pBaseAddr);
if(found_page == mProcess->memoryBreakpointPages.end()) if(found_page == mProcess->memoryBreakpointPages.end())
@ -245,7 +274,7 @@ namespace GleeBug
if(foundInfo == mProcess->breakpoints.end()) if(foundInfo == mProcess->breakpoints.end())
{ {
sprintf_s(error, "inconsistent memory breakpoint at 0x%p", (void*)exceptionAddress); sprintf_s(error, "inconsistent memory breakpoint at 0x%p", (void*)exceptionAddress);
cbInternalError(error); reportError();
return; return;
} }
@ -260,7 +289,7 @@ namespace GleeBug
if(bpxPage == mProcess->memoryBreakpointPages.end()) if(bpxPage == mProcess->memoryBreakpointPages.end())
{ {
sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", (void*)(exceptionAddress & ~(PAGE_SIZE - 1))); sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", (void*)(exceptionAddress & ~(PAGE_SIZE - 1)));
cbInternalError(error); reportError();
return; return;
} }
auto pageAddr = bpxPage->first; auto pageAddr = bpxPage->first;
@ -284,11 +313,13 @@ namespace GleeBug
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
cbInternalError(error); reportError();
} }
mProcess->StepInternal([this, pageAddr]() mProcess->StepInternal([this, pageAddr]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr); auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
if(found_page == mProcess->memoryBreakpointPages.end()) if(found_page == mProcess->memoryBreakpointPages.end())
return; return;
@ -328,11 +359,13 @@ namespace GleeBug
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
cbInternalError(error); reportError();
} }
//Pass info as well //Pass info as well
mProcess->StepInternal([this, pageAddr]() mProcess->StepInternal([this, pageAddr]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
//With page check this should work better: So when we reach this part of the code we are sure that: //With page check this should work better: So when we reach this part of the code we are sure that:
//-The exception Address In deed corresponded to an existing (now possibly deleted) memory breakpoint range //-The exception Address In deed corresponded to an existing (now possibly deleted) memory breakpoint range
//-memoryBreakpointPages was in deed consistent with this memory address that generated the exception (The data structure wasn't corrupted somehow) //-memoryBreakpointPages was in deed consistent with this memory address that generated the exception (The data structure wasn't corrupted somehow)
@ -364,13 +397,22 @@ namespace GleeBug
void Debugger::exceptionAccessViolation(const EXCEPTION_RECORD & exceptionRecord, bool firstChance) void Debugger::exceptionAccessViolation(const EXCEPTION_RECORD & exceptionRecord, bool firstChance)
{ {
std::unique_lock<std::recursive_mutex> lock(mProcess->memoryBreakpointMutex); std::unique_lock<std::recursive_mutex> lock(mProcess->breakpointMutex);
/* /*
ASSUME: ASSUME:
exceptionAddress may or may not have been generated by your breakpoints. exceptionAddress may or may not have been generated by your breakpoints.
*/ */
char error[128] = ""; char error[128] = "";
auto reportError = [&]()
{
const bool relock = lock.owns_lock();
if(relock)
lock.unlock();
cbInternalError(error);
if(relock)
lock.lock();
};
auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]); auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]);
//check if the exception address is directly in the range of a memory breakpoint //check if the exception address is directly in the range of a memory breakpoint
@ -391,7 +433,7 @@ namespace GleeBug
if(!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect)) if(!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)foundPage->first); sprintf_s(error, "MemProtect failed on 0x%p", (void*)foundPage->first);
cbInternalError(error); reportError();
} }
//However the following situations may occur: //However the following situations may occur:
@ -402,6 +444,8 @@ namespace GleeBug
// then we ought to restore the protection. // then we ought to restore the protection.
mProcess->StepInternal([this, pBaseAddr]() mProcess->StepInternal([this, pBaseAddr]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
//seek out the page address //seek out the page address
auto found_page = mProcess->memoryBreakpointPages.find(pBaseAddr); auto found_page = mProcess->memoryBreakpointPages.find(pBaseAddr);
if(found_page == mProcess->memoryBreakpointPages.end()) if(found_page == mProcess->memoryBreakpointPages.end())
@ -424,7 +468,7 @@ namespace GleeBug
if(foundInfo == mProcess->breakpoints.end()) if(foundInfo == mProcess->breakpoints.end())
{ {
sprintf_s(error, "inconsistent memory breakpoint at 0x%p", (void*)exceptionAddress); sprintf_s(error, "inconsistent memory breakpoint at 0x%p", (void*)exceptionAddress);
cbInternalError(error); reportError();
return; return;
} }
@ -439,7 +483,7 @@ namespace GleeBug
if(bpxPage == mProcess->memoryBreakpointPages.end()) if(bpxPage == mProcess->memoryBreakpointPages.end())
{ {
sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", (void*)(exceptionAddress & ~(PAGE_SIZE - 1))); sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", (void*)(exceptionAddress & ~(PAGE_SIZE - 1)));
cbInternalError(error); reportError();
return; return;
} }
auto pageAddr = bpxPage->first; auto pageAddr = bpxPage->first;
@ -474,7 +518,7 @@ namespace GleeBug
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
cbInternalError(error); reportError();
} }
// The page-wide protection belongs to another range. Execute this // The page-wide protection belongs to another range. Execute this
@ -482,6 +526,8 @@ namespace GleeBug
// any memory breakpoint still owns it. // any memory breakpoint still owns it.
mProcess->StepInternal([this, pageAddr]() mProcess->StepInternal([this, pageAddr]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
auto foundPage = mProcess->memoryBreakpointPages.find(pageAddr); auto foundPage = mProcess->memoryBreakpointPages.find(pageAddr);
if(foundPage != mProcess->memoryBreakpointPages.end()) if(foundPage != mProcess->memoryBreakpointPages.end())
mProcess->MemProtect(pageAddr, PAGE_SIZE, foundPage->second.NewProtect); mProcess->MemProtect(pageAddr, PAGE_SIZE, foundPage->second.NewProtect);
@ -513,11 +559,13 @@ namespace GleeBug
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
cbInternalError(error); reportError();
} }
//Pass info as well //Pass info as well
mProcess->StepInternal([this, pageAddr]() mProcess->StepInternal([this, pageAddr]()
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
//With page check this should work better: So when we reach this part of the code we are sure that: //With page check this should work better: So when we reach this part of the code we are sure that:
//-The exception Address In deed corresponded to an existing (now possibly deleted) memory breakpoint range //-The exception Address In deed corresponded to an existing (now possibly deleted) memory breakpoint range
//-memoryBreakpointPages was in deed consistent with this memory address that generated the exception (The data structure wasn't corrupted somehow) //-memoryBreakpointPages was in deed consistent with this memory address that generated the exception (The data structure wasn't corrupted somehow)

View File

@ -75,7 +75,10 @@ namespace GleeBug
// single-step event arrives. // single-step event arrives.
consecutiveTimeouts++; consecutiveTimeouts++;
if(consecutiveTimeouts >= 2 && ThreadBeingProcessed == 0 && SuspendedThreads.empty() && DeferredExceptionThreads.empty() && mProcess) if(consecutiveTimeouts >= 2 && ThreadBeingProcessed == 0 && SuspendedThreads.empty() && DeferredExceptionThreads.empty() && mProcess)
{
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
mProcess->recentlyDeletedSwbp.clear(); mProcess->recentlyDeletedSwbp.clear();
}
continue; continue;
} }
} }

View File

@ -4,6 +4,8 @@ namespace GleeBug
{ {
bool Process::SetBreakpoint(ptr address, bool singleshoot, SoftwareType type) bool Process::SetBreakpoint(ptr address, bool singleshoot, SoftwareType type)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//check the address //check the address
if(!MemIsValidPtr(address) || if(!MemIsValidPtr(address) ||
breakpoints.find({ BreakpointType::Software, address }) != breakpoints.end()) breakpoints.find({ BreakpointType::Software, address }) != breakpoints.end())
@ -44,6 +46,8 @@ namespace GleeBug
bool Process::SetBreakpoint(ptr address, const BreakpointCallback & cbBreakpoint, bool singleshoot, SoftwareType type) bool Process::SetBreakpoint(ptr address, const BreakpointCallback & cbBreakpoint, bool singleshoot, SoftwareType type)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//check if a callback on this address was already found //check if a callback on this address was already found
if(breakpointCallbacks.find({ BreakpointType::Software, address }) != breakpointCallbacks.end()) if(breakpointCallbacks.find({ BreakpointType::Software, address }) != breakpointCallbacks.end())
return false; return false;
@ -57,6 +61,8 @@ namespace GleeBug
bool Process::DeleteBreakpoint(ptr address) bool Process::DeleteBreakpoint(ptr address)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//find the breakpoint //find the breakpoint
auto found = breakpoints.find({ BreakpointType::Software, address }); auto found = breakpoints.find({ BreakpointType::Software, address });
if(found == breakpoints.end()) if(found == breakpoints.end())
@ -79,6 +85,8 @@ namespace GleeBug
bool Process::GetFreeHardwareBreakpointSlot(HardwareSlot & slot) const bool Process::GetFreeHardwareBreakpointSlot(HardwareSlot & slot) const
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//find a free hardware breakpoint slot //find a free hardware breakpoint slot
for(int i = 0; i < HWBP_COUNT; i++) for(int i = 0; i < HWBP_COUNT; i++)
{ {
@ -93,6 +101,8 @@ namespace GleeBug
bool Process::SetHardwareBreakpoint(ptr address, HardwareSlot slot, HardwareType type, HardwareSize size, bool singleshoot) bool Process::SetHardwareBreakpoint(ptr address, HardwareSlot slot, HardwareType type, HardwareSize size, bool singleshoot)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//check the address //check the address
if(!MemIsValidPtr(address) || if(!MemIsValidPtr(address) ||
breakpoints.find({ BreakpointType::Hardware, address }) != breakpoints.end()) breakpoints.find({ BreakpointType::Hardware, address }) != breakpoints.end())
@ -138,6 +148,8 @@ namespace GleeBug
bool Process::SetHardwareBreakpoint(ptr address, HardwareSlot slot, const BreakpointCallback & cbBreakpoint, HardwareType type, HardwareSize size, bool singleshoot) bool Process::SetHardwareBreakpoint(ptr address, HardwareSlot slot, const BreakpointCallback & cbBreakpoint, HardwareType type, HardwareSize size, bool singleshoot)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//check if a callback on this address was already found //check if a callback on this address was already found
if(breakpointCallbacks.find({ BreakpointType::Hardware, address }) != breakpointCallbacks.end()) if(breakpointCallbacks.find({ BreakpointType::Hardware, address }) != breakpointCallbacks.end())
return false; return false;
@ -151,6 +163,8 @@ namespace GleeBug
bool Process::DeleteHardwareBreakpoint(ptr address) bool Process::DeleteHardwareBreakpoint(ptr address)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//find the hardware breakpoint //find the hardware breakpoint
auto found = breakpoints.find({ BreakpointType::Hardware, address }); auto found = breakpoints.find({ BreakpointType::Hardware, address });
if(found == breakpoints.end()) if(found == breakpoints.end())
@ -369,7 +383,7 @@ namespace GleeBug
bool Process::SetMemoryBreakpoint(ptr address, ptr size, MemoryType type, bool singleshoot) bool Process::SetMemoryBreakpoint(ptr address, ptr size, MemoryType type, bool singleshoot)
{ {
std::lock_guard<std::recursive_mutex> lock(memoryBreakpointMutex); std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
DPRINTF(); DPRINTF();
// Basic checks, including the range-end overflow that would otherwise wrap // Basic checks, including the range-end overflow that would otherwise wrap
@ -458,7 +472,7 @@ namespace GleeBug
bool Process::SetMemoryBreakpoint(ptr address, ptr size, const BreakpointCallback & cbBreakpoint, MemoryType type, bool singleshoot) bool Process::SetMemoryBreakpoint(ptr address, ptr size, const BreakpointCallback & cbBreakpoint, MemoryType type, bool singleshoot)
{ {
std::lock_guard<std::recursive_mutex> lock(memoryBreakpointMutex); std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
//check if a callback on this address was already found //check if a callback on this address was already found
if(breakpointCallbacks.find({ BreakpointType::Memory, address }) != breakpointCallbacks.end()) if(breakpointCallbacks.find({ BreakpointType::Memory, address }) != breakpointCallbacks.end())
@ -473,7 +487,7 @@ namespace GleeBug
bool Process::DeleteMemoryBreakpoint(ptr address) bool Process::DeleteMemoryBreakpoint(ptr address)
{ {
std::lock_guard<std::recursive_mutex> lock(memoryBreakpointMutex); std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
// Find the byte range containing address, then find its breakpoint record. // Find the byte range containing address, then find its breakpoint record.
const auto range = memoryBreakpointRanges.find(Range(address, address)); const auto range = memoryBreakpointRanges.find(Range(address, address));

View File

@ -13,6 +13,8 @@ namespace GleeBug
bool Process::MemReadSafe(ptr address, void* buffer, ptr size, ptr* bytesRead) const bool Process::MemReadSafe(ptr address, void* buffer, ptr size, ptr* bytesRead) const
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
if(!MemReadUnsafe(address, buffer, size, bytesRead)) if(!MemReadUnsafe(address, buffer, size, bytesRead))
return false; return false;
@ -65,6 +67,8 @@ namespace GleeBug
bool Process::MemWriteSafe(ptr address, const void* buffer, ptr size, ptr* bytesWritten) bool Process::MemWriteSafe(ptr address, const void* buffer, ptr size, ptr* bytesWritten)
{ {
std::lock_guard<std::recursive_mutex> lock(breakpointMutex);
if(size == 0) if(size == 0)
{ {
if(bytesWritten) if(bytesWritten)

View File

@ -31,7 +31,7 @@ namespace GleeBug
BreakpointInfo hardwareBreakpoints[4]; BreakpointInfo hardwareBreakpoints[4];
MemoryBreakpointSet memoryBreakpointRanges; MemoryBreakpointSet memoryBreakpointRanges;
MemoryBreakpointMap memoryBreakpointPages; MemoryBreakpointMap memoryBreakpointPages;
std::recursive_mutex memoryBreakpointMutex; mutable std::recursive_mutex breakpointMutex;
std::unordered_set<ptr> recentlyDeletedSwbp; std::unordered_set<ptr> recentlyDeletedSwbp;

View File

@ -116,6 +116,8 @@ retry_no_aslr:
if(mProcess) if(mProcess)
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
// 1. Restore all software (INT3) breakpoints, otherwise the debuggee // 1. Restore all software (INT3) breakpoints, otherwise the debuggee
// faults on a leftover 0xCC once it is no longer being debugged. // faults on a leftover 0xCC once it is no longer being debugged.
for(auto it = mProcess->breakpoints.begin(); it != mProcess->breakpoints.end(); ) for(auto it = mProcess->breakpoints.begin(); it != mProcess->breakpoints.end(); )

View File

@ -793,6 +793,7 @@ public:
bool IsBPXEnabled(ULONG_PTR bpxAddress) bool IsBPXEnabled(ULONG_PTR bpxAddress)
{ {
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
return (mProcess->MemIsValidPtr(bpxAddress) && return (mProcess->MemIsValidPtr(bpxAddress) &&
mProcess->breakpoints.find({ BreakpointType::Software, bpxAddress }) != mProcess->breakpoints.end()); mProcess->breakpoints.find({ BreakpointType::Software, bpxAddress }) != mProcess->breakpoints.end());
} }
@ -854,7 +855,11 @@ public:
auto slot = IndexOfRegister - UE_DR0; auto slot = IndexOfRegister - UE_DR0;
if(!mProcess || slot > 3) if(!mProcess || slot > 3)
return false; return false;
auto address = mProcess->hardwareBreakpoints[slot].address; ptr address;
{
std::lock_guard<std::recursive_mutex> lock(mProcess->breakpointMutex);
address = mProcess->hardwareBreakpoints[slot].address;
}
return mProcess->DeleteHardwareBreakpoint(address); return mProcess->DeleteHardwareBreakpoint(address);
} }
@ -874,7 +879,11 @@ public:
{ {
for(auto & it : mProcesses) for(auto & it : mProcesses)
{ {
auto breakpoints = it.second->breakpoints; //explicit copy BreakpointMap breakpoints;
{
std::lock_guard<std::recursive_mutex> lock(it.second->breakpointMutex);
breakpoints = it.second->breakpoints;
}
for(const auto & jt : breakpoints) for(const auto & jt : breakpoints)
it.second->DeleteGenericBreakpoint(jt.second); it.second->DeleteGenericBreakpoint(jt.second);
} }