Write-protect the dispatcher on all platforms.

This commit is contained in:
Henrik Rydgard committed 2016-08-28 13:35:27 +02:00
1 parent 5005e83254
commit 323eb72b7c
6 files changed
+45 -19

No files matched your search

+20 -5
View File
@@ -291,6 +291,10 @@ bool PlatformIsWXExclusive() {
}
void ProtectMemoryPages(void* ptr, size_t size, uint32_t memProtFlags) {
if (PlatformIsWXExclusive()) {
if ((memProtFlags & (MEM_PROT_WRITE | MEM_PROT_EXEC)) == (MEM_PROT_WRITE | MEM_PROT_EXEC))
PanicAlert("Bad memory protect : W^X is in effect, can't both write and exec");
}
#ifdef _WIN32
uint32_t protect = ConvertProtFlagsWin32(memProtFlags);
DWORD oldValue;
@@ -300,10 +304,21 @@ void ProtectMemoryPages(void* ptr, size_t size, uint32_t memProtFlags) {
// Do nothing
#else
uint32_t protect = ConvertProtFlagsUnix(memProtFlags);
if (PlatformIsWXExclusive()) {
if ((flags & (MEM_PROT_WRITE | MEM_PROT_EXEC)) == (MEM_PROT_WRITE | MEM_PROT_EXEC))
PanicAlert("Bad memory protect : W^X is in effect, can't both write and exec");
}
mprotect(ptr, size, protect);
#endif
}
}
// Hardcoded in the header. This is a way to check it dynamically in case it ever becomes necessary.
/*
#ifdef _WIN32
// 4k on most archs, 8k on Itanium (but who cares).
// Should not be confused with the allocation granularity which is 65k and is how precise
// VirtualAlloc allocations can be located in memory. We don't really care about that much though.
int GetMemoryProtectPageSize() {
if (sys_info.dwPageSize == 0)
GetSystemInfo(&sys_info);
return sys_info.dwPageSize;
}
#endif
*/
+7 -8
View File
@@ -29,18 +29,21 @@ using std::size_t;
// Returns true if we need to avoid setting both writable and executable at the same time (W^X)
bool PlatformIsWXExclusive();
#define MEM_PROT_READ 1
#define MEM_PROT_WRITE 2
#define MEM_PROT_EXEC 4
// Note that some platforms go through special contortions to allocate executable memory. So for memory
// that's intended for execution, allocate it first using AllocateExecutableMemory, then modify protection as desired.
// AllocateMemoryPages is simpler and more generic.
// AllocateMemoryPages is simpler and more generic. Note that on W^X platforms, this will return executable but not writable
// memory!
void* AllocateExecutableMemory(size_t size);
void* AllocateMemoryPages(size_t size, uint32_t memProtFlags);
// Note that on platforms returning PlatformIsWXExclusive, you cannot set a page to be both readable and writable at the same time.
void ProtectMemoryPages(void* ptr, size_t size, uint32_t memProtFlags);
inline void ProtectMemoryPages(const void *start, const void *end, uint32_t memProtFlags) {
ProtectMemoryPages((void *)start, (const uint8_t *)end - (const uint8_t *)start, memProtFlags);
}
void FreeMemoryPages(void* ptr, size_t size);
// Regular aligned memory. Don't try to apply memory protection willy-nilly to memory allocated this way as in-page alignment is unknown (though could be checked).
@@ -52,12 +55,8 @@ void ResetExecutableMemory(void* ptr);
#endif
inline int GetMemoryProtectPageSize() {
#ifdef _WIN32
// For memory protection purposes, Windows is still stuck at 64k except in WSL (linux on windows).
return 65536;
#else
return PAGE_SIZE;
#endif
// This is 4096 on all platforms we care about. 8k on Itanium but meh.
return 4096;
}
template <typename T>
+4 -2
View File
@@ -135,9 +135,11 @@ const u8 *XEmitter::AlignCode16()
const u8 *XEmitter::AlignCodePage()
{
int c = int((u64)code & 4095);
// Memory protection pages matter.
int page_size = GetMemoryProtectPageSize();
int c = int((u64)code & (page_size - 1));
if (c)
ReserveCodeSpace(4096-c);
ReserveCodeSpace(page_size - c);
return code;
}
+5 -1
View File
@@ -72,7 +72,7 @@ namespace MIPSComp {
using namespace ArmJitConstants;
void ArmJit::GenerateFixedCode() {
const u8 *start = GetCodePtr();
const u8 *start = AlignCodePage();
// LR == SCRATCHREG2 on ARM32 so it needs to be pushed.
restoreRoundingMode = AlignCode16(); {
@@ -279,6 +279,10 @@ void ArmJit::GenerateFixedCode() {
// Don't forget to zap the instruction cache!
FlushLitPool();
FlushIcache();
// Freeze the dispatcher code
const void *end = AlignCodePage();
ProtectMemoryPages(start, end, MEM_PROT_READ | MEM_PROT_EXEC);
}
} // namespace MIPSComp
+4 -1
View File
@@ -94,7 +94,7 @@ namespace MIPSComp {
using namespace Arm64JitConstants;
void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
const u8 *start = AlignCode16();
const u8 *start = AlignCodePage();
if (jo.useStaticAlloc) {
saveStaticRegisters = AlignCode16();
STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
@@ -316,6 +316,9 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
// Don't forget to zap the instruction cache! This must stay at the end of this function.
FlushIcache();
// Freeze the dispatcher code
const void *end = AlignCodePage();
ProtectMemoryPages(start, end, MEM_PROT_READ | MEM_PROT_EXEC);
}
} // namespace MIPSComp
+5 -2
View File
@@ -63,7 +63,7 @@ void ImHere() {
}
void Jit::GenerateFixedCode(JitOptions &jo) {
const u8 *start = AlignCode16();
const u8 *start = AlignCodePage();
restoreRoundingMode = AlignCode16(); {
STMXCSR(M(&mips_->temp));
@@ -217,7 +217,10 @@ void Jit::GenerateFixedCode(JitOptions &jo) {
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
endOfPregeneratedCode = GetCodePtr();
endOfPregeneratedCode = AlignCodePage();
// Freeze the pre-generated code.
ProtectMemoryPages(start, endOfPregeneratedCode, MEM_PROT_READ | MEM_PROT_EXEC);
}
} // namespace