Skip to content

Commit 6421d41

Browse files
adamgreenfacchinm
authored andcommitted
Detect invalid memory accesses from GDB
Detect memory faults that occur while mriMain() is processing commands from GDB so that read/write errors can be detected and reported to GDB. I modified the mriFaultHandlerStub() assembly language handler to pass PSP and MSP in addition to EXC_RETURN when calling mriFaultHandler(). mriFaultHandler() needs the two SP register values so that it can find and advance the debugger thread's PC value past a precise faulting read instruction. I also modified mriMain() to set and clear CORTEXM_FLAGS_ACTIVE_DEBUG in mriCortexMState when entering and leaving mriDebugException().
1 parent 7eaafd8 commit 6421d41

File tree

2 files changed

+112
-21
lines changed

2 files changed

+112
-21
lines changed

libraries/ThreadMRI/src/ThreadMRI.cpp

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ extern "C"
5252
// Thread syncronization flag used to indicate that a debug event has occured.
5353
#define MRI_THREAD_DEBUG_EVENT_FLAG (1 << 0)
5454

55+
// Lower nibble of EXC_RETURN in LR will have one of these values if interrupted code was running in thread mode.
56+
// Using PSP.
57+
#define EXC_RETURN_THREADMODE_PROCESSSTACK 0xD
58+
// Using MSP.
59+
#define EXC_RETURN_THREADMODE_MAINSTACK 0x9
60+
5561

5662
// Assert routine that will dump error text to GDB connection before entering infinite loop. If user is logging MRI
5763
// remote communications then they will see the error text in the log before debug stub becomes unresponseive.
@@ -73,9 +79,6 @@ extern "C"
7379
// Flag to verify that only one ThreadMRI object has been initialized.
7480
static bool g_alreadyInitialized;
7581

76-
// The ID of the mriMain() thread.
77-
static osThreadId_t g_mriThreadId;
78-
7982
// The ID of the halted thread being debugged.
8083
static volatile osThreadId_t g_haltedThreadId;
8184

@@ -88,7 +91,10 @@ static uint32_t g_threadCount;
8891

8992
// This flag is set to a non-zero value if the DebugMon handler is to re-enable DWT watchpoints and FPB breakpoints
9093
// after being disabled by the HardFault handler when a debug event is encounted in handler mode.
91-
static volatile uint32_t g_enableDWTandFPB;
94+
static volatile uint32_t g_enableDWTandFPB;
95+
96+
// The ID of the mriMain() thread.
97+
volatile osThreadId_t mriThreadId;
9298

9399
// If non-NULL, this is the thread that we want to single step.
94100
// If NULL, single stepping is not enabled.
@@ -132,11 +138,20 @@ static void resumeApplicationThreads();
132138
static void readThreadContext(osThreadId_t thread);
133139
static void switchRtxHandlersToDebugStubsForSingleStepping();
134140
static void restoreRtxHandlers();
141+
static void setDebugActiveFlag();
142+
static void clearDebugActiveFlag();
143+
static bool isThreadMode(uint32_t excReturn);
135144
static bool hasEncounteredDebugEvent();
136145
static void recordAndClearFaultStatusBits();
137146
static void wakeMriMainToDebugCurrentThread();
138147
static void stopSingleStepping();
139148
static void recordAndSwitchFaultHandlersToDebugger();
149+
static bool isDebugThreadActive();
150+
static void setFaultDetectedFlag();
151+
static bool isImpreciseBusFault();
152+
static void advancePCToNextInstruction(uint32_t excReturn, uint32_t psp, uint32_t msp);
153+
static uint32_t* threadSP(uint32_t excReturn, uint32_t psp, uint32_t msp);
154+
static bool isInstruction32Bit(uint16_t firstWordOfInstruction);
140155

141156

142157

@@ -166,8 +181,8 @@ bool ThreadMRI::begin()
166181
.stack_size = sizeof(stack),
167182
.priority = osPriorityNormal
168183
};
169-
g_mriThreadId = osThreadNew(mriMain, NULL, &threadAttr);
170-
if (g_mriThreadId == NULL) {
184+
mriThreadId = osThreadNew(mriMain, NULL, &threadAttr);
185+
if (mriThreadId == NULL) {
171186
return false;
172187
}
173188
g_alreadyInitialized = true;
@@ -189,11 +204,13 @@ static __NO_RETURN void mriMain(void *pv)
189204
if (Platform_IsSingleStepping()) {
190205
restoreRtxHandlers();
191206
}
207+
setDebugActiveFlag();
192208

193209
osThreadSetPriority(osThreadGetId(), osPriorityNormal);
194210
mriDebugException();
195211
osThreadSetPriority(osThreadGetId(), osPriorityRealtime7);
196212

213+
clearDebugActiveFlag();
197214
if (Platform_IsSingleStepping()) {
198215
mriThreadSingleStepThreadId = g_haltedThreadId;
199216
switchRtxHandlersToDebugStubsForSingleStepping();
@@ -214,7 +231,7 @@ static void suspendAllApplicationThreads()
214231
osThreadId_t thread = g_suspendedThreads[i];
215232
const char* pThreadName = osThreadGetName(thread);
216233

217-
if (thread != g_mriThreadId && strcmp(pThreadName, "rtx_idle") != 0) {
234+
if (thread != mriThreadId && strcmp(pThreadName, "rtx_idle") != 0) {
218235
osThreadSuspend(thread);
219236
}
220237
else {
@@ -298,6 +315,15 @@ static void restoreRtxHandlers()
298315
NVIC_SetVector(SysTick_IRQn, mriThreadOrigSysTick);
299316
}
300317

318+
static void setDebugActiveFlag()
319+
{
320+
mriCortexMState.flags |= CORTEXM_FLAGS_ACTIVE_DEBUG;
321+
}
322+
323+
static void clearDebugActiveFlag()
324+
{
325+
mriCortexMState.flags &= ~CORTEXM_FLAGS_ACTIVE_DEBUG;
326+
}
301327

302328

303329

@@ -427,9 +453,7 @@ int Semihost_HandleSemihostRequest(void)
427453
// ---------------------------------------------------------------------------------------------------------------------
428454
extern "C" void mriDebugMonitorHandler(uint32_t excReturn)
429455
{
430-
excReturn &= 0xF;
431-
bool isThreadMode = (excReturn == 0xD || excReturn == 0x9);
432-
while (!isThreadMode) {
456+
while (!isThreadMode(excReturn)) {
433457
// DebugMon is running at such low priority that we should be getting ready to return to thread mode.
434458
}
435459

@@ -461,11 +485,20 @@ extern "C" void mriDebugMonitorHandler(uint32_t excReturn)
461485

462486
// This function is called if a fault (hard, mem, bus, usage) has occurred while running code in thread mode.
463487
// It is also called when a debug event has forced a hard fault while running code in handler mode.
464-
extern "C" void mriFaultHandler(uint32_t excReturn)
488+
extern "C" void mriFaultHandler(uint32_t excReturn, uint32_t psp, uint32_t msp)
465489
{
466-
excReturn &= 0xF;
467-
bool isThreadMode = (excReturn == 0xD || excReturn == 0x9);
468-
if (isThreadMode) {
490+
if (isThreadMode(excReturn)) {
491+
if (isDebugThreadActive()) {
492+
// Encountered memory fault when GDB attempted to access an invalid address.
493+
// Set flag to let debugger thread know that its access failed and advance past the faulting instruction
494+
// if it was a precise bus fault so that it doesn't just occur again on return.
495+
setFaultDetectedFlag();
496+
if (!isImpreciseBusFault()) {
497+
advancePCToNextInstruction(excReturn, psp, msp);
498+
}
499+
return;
500+
}
501+
469502
// A crash has been detected in thread mode. Wake debugger thread to debug it.
470503
recordAndClearFaultStatusBits();
471504
stopSingleStepping();
@@ -483,6 +516,12 @@ extern "C" void mriFaultHandler(uint32_t excReturn)
483516
setMonitorPending();
484517
}
485518

519+
static bool isThreadMode(uint32_t excReturn)
520+
{
521+
excReturn &= 0xF;
522+
return excReturn == EXC_RETURN_THREADMODE_PROCESSSTACK || excReturn == EXC_RETURN_THREADMODE_MAINSTACK;
523+
}
524+
486525
static bool hasEncounteredDebugEvent()
487526
{
488527
return SCB->DFSR != 0;
@@ -509,11 +548,61 @@ static void wakeMriMainToDebugCurrentThread()
509548
disableSingleStep();
510549
g_debugThreadId = osThreadGetId();
511550
g_haltedThreadId = g_debugThreadId; // UNDONE: osThreadGetId();
512-
osThreadFlagsSet(g_mriThreadId, MRI_THREAD_DEBUG_EVENT_FLAG);
551+
osThreadFlagsSet(mriThreadId, MRI_THREAD_DEBUG_EVENT_FLAG);
513552
}
514553

515554
static void stopSingleStepping()
516555
{
517556
disableSingleStep();
518557
mriThreadSingleStepThreadId = NULL;
519558
}
559+
560+
static bool isDebugThreadActive()
561+
{
562+
return (mriCortexMState.flags & CORTEXM_FLAGS_ACTIVE_DEBUG) && mriThreadId == osThreadGetId();
563+
}
564+
565+
static void setFaultDetectedFlag()
566+
{
567+
mriCortexMState.flags |= CORTEXM_FLAGS_FAULT_DURING_DEBUG;
568+
}
569+
570+
static bool isImpreciseBusFault()
571+
{
572+
return SCB->CFSR & SCB_CFSR_IMPRECISERR_Msk;
573+
}
574+
575+
static void advancePCToNextInstruction(uint32_t excReturn, uint32_t psp, uint32_t msp)
576+
{
577+
uint32_t* pSP = threadSP(excReturn, psp, msp);
578+
uint32_t* pPC = pSP + 7;
579+
uint16_t currentInstruction = *(uint16_t*)pPC;
580+
if (isInstruction32Bit(currentInstruction)) {
581+
*pPC += sizeof(uint32_t);
582+
} else {
583+
*pPC += sizeof(uint16_t);
584+
}
585+
}
586+
587+
static uint32_t* threadSP(uint32_t excReturn, uint32_t psp, uint32_t msp)
588+
{
589+
uint32_t sp;
590+
if ((excReturn & 0xF) == EXC_RETURN_THREADMODE_PROCESSSTACK) {
591+
sp = psp;
592+
} else {
593+
sp = msp;
594+
}
595+
return (uint32_t*)sp;
596+
}
597+
598+
599+
static bool isInstruction32Bit(uint16_t firstWordOfInstruction)
600+
{
601+
uint16_t maskedOffUpper5BitsOfWord = firstWordOfInstruction & 0xF800;
602+
603+
// 32-bit instructions start with 0b11101, 0b11110, 0b11111 according to page A5-152 of the
604+
// ARMv7-M Architecture Manual.
605+
return (maskedOffUpper5BitsOfWord == 0xE800 ||
606+
maskedOffUpper5BitsOfWord == 0xF000 ||
607+
maskedOffUpper5BitsOfWord == 0xF800);
608+
}

libraries/ThreadMRI/src/ThreadMRI_asm.S

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,19 @@ mriDebugMonitorHandlerStub:
6161
*/
6262
mriFaultHandlerStub:
6363
mov r0, lr
64+
mrs r1, psp
65+
mrs r2, msp
6466
/* Jump to mriFaultHandler if interrupted code was running in thread mode. */
65-
and r1, r0, #0xF
66-
cmp r1, #EXC_RETURN_THREADMODE_PROCESSSTACK
67+
and r12, r0, #0xF
68+
cmp r12, #EXC_RETURN_THREADMODE_PROCESSSTACK
6769
beq.w mriFaultHandler
68-
cmp r1, #EXC_RETURN_THREADMODE_MAINSTACK
70+
cmp r12, #EXC_RETURN_THREADMODE_MAINSTACK
6971
beq.w mriFaultHandler
7072
/* Get here if interrupted code was running in handler mode. */
7173
/* Check HardFaultStatusRegister to see if this is a debug event. */
72-
ldr r1, =HFSR
73-
ldr r1, [r1]
74-
tst r1, #HFSR_DEBUG_EVENT_BIT
74+
ldr r12, =HFSR
75+
ldr r12, [r12]
76+
tst r12, #HFSR_DEBUG_EVENT_BIT
7577
/* If forced into hard fault due to debug event then breakpoint/watchpoint occurred and needs to be ignored. */
7678
bne.w mriFaultHandler
7779
/* If not a debug event then fall through to default hard fault handler as something bad has happened. */

0 commit comments

Comments
 (0)