From 004f0652c95a453c394b3d7801a53f8edc0b4042 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Thu, 14 Jul 2016 20:08:31 +0200 Subject: [PATCH] Prevent double faults when either PC or SP is invalid --- exceptions/arm11/source/handlers.h | 1 + exceptions/arm11/source/handlers.s | 11 ++++++++++ exceptions/arm11/source/mainHandler.c | 16 ++++++++++----- exceptions/arm11/source/types.h | 6 +++++- exceptions/arm9/source/handlers.h | 1 + exceptions/arm9/source/handlers.s | 17 ++++++++++++++++ exceptions/arm9/source/mainHandler.c | 29 ++++++++++++++++++++++++++- exceptions/arm9/source/types.h | 5 +++++ exceptions/exception_dump_parser.py | 2 +- 9 files changed, 80 insertions(+), 8 deletions(-) diff --git a/exceptions/arm11/source/handlers.h b/exceptions/arm11/source/handlers.h index 62d74cc..912672c 100644 --- a/exceptions/arm11/source/handlers.h +++ b/exceptions/arm11/source/handlers.h @@ -26,6 +26,7 @@ void __attribute__((noreturn)) mcuReboot(void); void cleanInvalidateDCacheAndDMB(void); +bool cannotAccessVA(const void *address); void FIQHandler(void); void undefinedInstructionHandler(void); diff --git a/exceptions/arm11/source/handlers.s b/exceptions/arm11/source/handlers.s index 5167fe0..8dc624c 100644 --- a/exceptions/arm11/source/handlers.s +++ b/exceptions/arm11/source/handlers.s @@ -116,3 +116,14 @@ cleanInvalidateDCacheAndDMB: mcr p15,0,r0,c7,c14,0 @ Clean and Invalidate Entire Data Cache mcr p15,0,r0,c7,c10,4 @ Drain Memory Barrier bx lr + +.global cannotAccessVA +.type cannotAccessVA, %function +cannotAccessVA: + @ Thanks yellows8 for the hint + lsr r0, #12 + lsl r0, #12 + mcr p15,0,r0,c7,c8,0 @ VA to PA translation with privileged read permission check + mrc p15,0,r0,c7,c4,0 @ read PA register + and r0, #1 @ failure bit + bx lr diff --git a/exceptions/arm11/source/mainHandler.c b/exceptions/arm11/source/mainHandler.c index 6310163..5c7d286 100644 --- a/exceptions/arm11/source/mainHandler.c +++ b/exceptions/arm11/source/mainHandler.c @@ -63,10 +63,12 @@ void __attribute__((noreturn)) mainHandler(u32 regs[REG_DUMP_SIZE / 4], u32 type for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[8 + i]; for(u32 i = 0; i < 8; i++) registerDump[i] = regs[15 + i]; - dumpHeader.stackDumpSize = 0x1000 - (registerDump[13] & 0xfff); + dumpHeader.stackDumpSize = 0x1000 - (registerDump[13] & 0xFFF); //Dump code vu8 *instr = (vu8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem + if(cannotAccessVA((u8 *)instr) || cannotAccessVA((u8 *)instr + dumpHeader.codeDumpSize)) + dumpHeader.codeDumpSize = 0; for(u32 i = 0; i < dumpHeader.codeDumpSize; i++) codeDump[i] = instr[i]; @@ -81,13 +83,17 @@ void __attribute__((noreturn)) mainHandler(u32 regs[REG_DUMP_SIZE / 4], u32 type //Dump stack in place vu32 *sp = (vu32 *)registerDump[13]; + if(cannotAccessVA((u8 *)sp)) + dumpHeader.stackDumpSize = 0; for(u32 i = 0; i < dumpHeader.stackDumpSize / 4; i++) *final++ = sp[i]; - vu8 *currentKProcess = *(vu8 **)0xFFFF9004; - vu8 *currentKCodeSet = (currentKProcess != NULL) ? *(vu8 **)(currentKProcess + CODESET_OFFSET) : NULL; - if(currentKCodeSet != NULL) + vu8 *currentKProcess = (cannotAccessVA((u8 *)0xFFFF9004)) ? NULL : *(vu8 **)0xFFFF9004; + vu8 *currentKCodeSet = (currentKProcess != NULL && ((u32)currentKProcess & 3) == 0 && !cannotAccessVA((u8 *)currentKProcess + CODESET_OFFSET)) + ? *(vu8 **)(currentKProcess + CODESET_OFFSET) : NULL; + + if(currentKCodeSet != NULL && ((u32)currentKCodeSet & 3) == 0 && !cannotAccessVA((u8 *)currentKCodeSet)) { vu32 *additionalData = final; dumpHeader.additionalDataSize = 16; @@ -99,7 +105,7 @@ void __attribute__((noreturn)) mainHandler(u32 regs[REG_DUMP_SIZE / 4], u32 type additionalData[3] = *(vu32 *)(currentKCodeSet + 0x60); } else - dumpHeader.additionalDataSize = 16; + dumpHeader.additionalDataSize = 0; //Copy header (actually optimized by the compiler) final = (vu32 *)FINAL_BUFFER; diff --git a/exceptions/arm11/source/types.h b/exceptions/arm11/source/types.h index a9aecb8..256923d 100644 --- a/exceptions/arm11/source/types.h +++ b/exceptions/arm11/source/types.h @@ -23,7 +23,11 @@ #pragma once #include -#define NULL 0 +#include + +#ifndef NULL + #define NULL 0 +#endif //Common data types typedef uint8_t u8; diff --git a/exceptions/arm9/source/handlers.h b/exceptions/arm9/source/handlers.h index 075c495..21473e6 100644 --- a/exceptions/arm9/source/handlers.h +++ b/exceptions/arm9/source/handlers.h @@ -23,6 +23,7 @@ #pragma once #include "types.h" +u32 readMPUConfig(u32 regionSettings[8]); void FIQHandler(void); void undefinedInstructionHandler(void); void dataAbortHandler(void); diff --git a/exceptions/arm9/source/handlers.s b/exceptions/arm9/source/handlers.s index 979756a..97d5ef1 100644 --- a/exceptions/arm9/source/handlers.s +++ b/exceptions/arm9/source/handlers.s @@ -62,3 +62,20 @@ GEN_HANDLER FIQHandler GEN_HANDLER undefinedInstructionHandler GEN_HANDLER prefetchAbortHandler GEN_HANDLER dataAbortHandler + +.global readMPUConfig +.type readMPUConfig, %function +readMPUConfig: + stmfd sp!, {r4-r8} + mrc p15,0,r1,c6,c0,0 + mrc p15,0,r2,c6,c1,0 + mrc p15,0,r3,c6,c2,0 + mrc p15,0,r4,c6,c3,0 + mrc p15,0,r5,c6,c4,0 + mrc p15,0,r6,c6,c5,0 + mrc p15,0,r7,c6,c6,0 + mrc p15,0,r8,c6,c7,0 + stmia r0, {r1-r8} + mrc p15,0,r0,c5,c0,2 @ read data access permission bits + ldmfd sp!, {r4-r8} + bx lr diff --git a/exceptions/arm9/source/mainHandler.c b/exceptions/arm9/source/mainHandler.c index 02ffece..25a71a8 100644 --- a/exceptions/arm9/source/mainHandler.c +++ b/exceptions/arm9/source/mainHandler.c @@ -28,6 +28,29 @@ #define REG_DUMP_SIZE (4*17) #define CODE_DUMP_SIZE 48 +bool cannotAccessAddress(const void *address) +{ + u32 regionSettings[8]; + u32 addr = (u32)address; + + u32 dataAccessPermissions = readMPUConfig(regionSettings); + for(u32 i = 0; i < 8; i++) + { + if((dataAccessPermissions & 0xF) == 0 || (regionSettings[i] & 1) == 0) + continue; //no access / region not enabled + + u32 regionAddrBase = regionSettings[i] & ~0xFFF; + u32 regionSize = 1 << (((regionSettings[i] >> 1) & 0x1F) + 1); + + if(addr >= regionAddrBase && addr < regionAddrBase + regionSize) + return false; + + dataAccessPermissions >>= 4; + } + + return true; +} + void __attribute__((noreturn)) mainHandler(u32 regs[REG_DUMP_SIZE / 4], u32 type) { ExceptionDumpHeader dumpHeader; @@ -58,11 +81,13 @@ void __attribute__((noreturn)) mainHandler(u32 regs[REG_DUMP_SIZE / 4], u32 type for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[2 + i]; for(u32 i = 0; i < 8; i++) registerDump[i] = regs[9 + i]; - dumpHeader.stackDumpSize = 0x1000 - (registerDump[13] & 0xfff); + dumpHeader.stackDumpSize = 0x1000 - (registerDump[13] & 0xFFF); dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize; //Dump code vu8 *instr = (vu8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem + if(cannotAccessAddress((u8 *)instr) || cannotAccessAddress((u8 *)instr + dumpHeader.codeDumpSize)) + dumpHeader.codeDumpSize = 0; for(u32 i = 0; i < dumpHeader.codeDumpSize; i++) codeDump[i] = instr[i]; @@ -79,6 +104,8 @@ void __attribute__((noreturn)) mainHandler(u32 regs[REG_DUMP_SIZE / 4], u32 type //Dump stack in place vu32 *sp = (vu32 *)registerDump[13]; + if(cannotAccessAddress((u8 *)sp)) + dumpHeader.stackDumpSize = 0; for(u32 i = 0; i < dumpHeader.stackDumpSize / 4; i++) *final++ = sp[i]; diff --git a/exceptions/arm9/source/types.h b/exceptions/arm9/source/types.h index 6a1677e..256923d 100644 --- a/exceptions/arm9/source/types.h +++ b/exceptions/arm9/source/types.h @@ -23,6 +23,11 @@ #pragma once #include +#include + +#ifndef NULL + #define NULL 0 +#endif //Common data types typedef uint8_t u8; diff --git a/exceptions/exception_dump_parser.py b/exceptions/exception_dump_parser.py index ddc1129..06212f3 100644 --- a/exceptions/exception_dump_parser.py +++ b/exceptions/exception_dump_parser.py @@ -114,7 +114,7 @@ if __name__ == "__main__": else: print("Processor: ARM11 (core {0})".format(processor >> 16)) typeDetailsStr = "" - if exceptionType == 2 and (registers[16] & 0x20) == 0 and unpack_from("= 4 and unpack_from("