diff --git a/Common/MemArena.h b/Common/MemArena.h index 715a0f57c0..bf9b06e0ca 100644 --- a/Common/MemArena.h +++ b/Common/MemArena.h @@ -63,5 +63,9 @@ private: size_t reserved_size = 0; #else int fd = -1; + // Where Find4GBBase keeps the view range reserved (inaccessible) until ReleaseSpace, so nothing else + // can be mapped between the views. + void *reserved_mem = nullptr; + size_t reserved_size = 0; #endif }; diff --git a/Common/MemArenaAndroid.cpp b/Common/MemArenaAndroid.cpp index 5b72b852a1..145a3c36ad 100644 --- a/Common/MemArenaAndroid.cpp +++ b/Common/MemArenaAndroid.cpp @@ -121,6 +121,11 @@ void MemArena::ReleaseSpace() { close(fd); } fd = -1; + if (reserved_mem) { + munmap(reserved_mem, reserved_size); + reserved_mem = nullptr; + reserved_size = 0; + } } void *MemArena::CreateView(s64 offset, size_t size, void *base) { @@ -133,7 +138,14 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) { } void MemArena::ReleaseView(s64 offset, void* view, size_t size) { - munmap(view, size); + const uintptr_t start = (uintptr_t)view; + if (reserved_mem && start >= (uintptr_t)reserved_mem && start + size <= (uintptr_t)reserved_mem + reserved_size) { + // Put the reservation back instead of leaving a hole, which another thread could map into before + // ReleaseSpace unmaps the whole range. + mmap(view, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_NORESERVE | MAP_FIXED, -1, 0); + } else { + munmap(view, size); + } } u8* MemArena::Find4GBBase() { @@ -159,14 +171,21 @@ u8* MemArena::Find4GBBase() { } #else // Address masking is used in 32-bit mode, so we can get away with less memory. - void *base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); + // Keep the range reserved, and map the views over it. Unmapping it would let the heap and the driver + // allocate in the gaps between the views (where stray guest accesses then land), or even where a + // view is about to go, which the view's MAP_FIXED would then silently replace. + void *base = mmap(0, 0x10000000, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_NORESERVE, -1, 0); if (base == MAP_FAILED) { ERROR_LOG(Log::System, "Failed to map 256 MB of memory space: %s", strerror(errno)); return nullptr; } - munmap(base, 0x10000000); + if (reserved_mem) { + munmap(reserved_mem, reserved_size); + } + reserved_mem = base; + reserved_size = 0x10000000; return static_cast(base); #endif } diff --git a/Common/MemArenaPosix.cpp b/Common/MemArenaPosix.cpp index 38598a2d7e..8db293a86b 100644 --- a/Common/MemArenaPosix.cpp +++ b/Common/MemArenaPosix.cpp @@ -110,6 +110,11 @@ bool MemArena::GrabMemSpace(size_t size) { void MemArena::ReleaseSpace() { #ifndef NO_MMAP close(fd); + if (reserved_mem) { + munmap(reserved_mem, reserved_size); + reserved_mem = nullptr; + reserved_size = 0; + } #endif } @@ -135,7 +140,14 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) void MemArena::ReleaseView(s64 offset, void* view, size_t size) { #ifndef NO_MMAP - munmap(view, size); + const uintptr_t start = (uintptr_t)view; + if (reserved_mem && start >= (uintptr_t)reserved_mem && start + size <= (uintptr_t)reserved_mem + reserved_size) { + // Put the reservation back instead of leaving a hole, which another thread could map into before + // ReleaseSpace unmaps the whole range. + mmap(view, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_NORESERVE | MAP_FIXED, -1, 0); + } else { + munmap(view, size); + } #endif } @@ -165,10 +177,16 @@ u8* MemArena::Find4GBBase() { void* base = std::malloc(0x0A000000); return static_cast(base); #else + // Keep the range reserved and map the views over it, so nothing else can be allocated in the gaps + // between them, or where a view is about to go (see MemArenaAndroid.cpp). size_t size = 0x10000000; - void* base = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED | MAP_NORESERVE, -1, 0); + void* base = mmap(0, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_NORESERVE, -1, 0); _assert_msg_(base != MAP_FAILED, "Failed to map 256 MB of memory space: %s", strerror(errno)); - munmap(base, size); + if (reserved_mem) { + munmap(reserved_mem, reserved_size); + } + reserved_mem = base; + reserved_size = size; return static_cast(base); #endif } diff --git a/Core/MIPS/ARM/ArmIRCompLoadStore.cpp b/Core/MIPS/ARM/ArmIRCompLoadStore.cpp index 9f2dcabda7..599e45fff8 100644 --- a/Core/MIPS/ARM/ArmIRCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmIRCompLoadStore.cpp @@ -46,11 +46,12 @@ void ArmJitBackend::PrepareSrc1Address(IRInst inst) { } ARMReg src1 = regs_.MapGPR(inst.src1); + // Clears bits 28-31 (see MEMVIEW32_MASK). if (inst.constant == 0) { - BIC(SCRATCH1, src1, Operand2(0xC0, 4)); + BIC(SCRATCH1, src1, Operand2(0xF0, 4)); } else { ADDI2R(SCRATCH1, src1, inst.constant, SCRATCH2); - BIC(SCRATCH1, SCRATCH1, Operand2(0xC0, 4)); + BIC(SCRATCH1, SCRATCH1, Operand2(0xF0, 4)); } } diff --git a/Core/MemMap.h b/Core/MemMap.h index 951d897e1a..f11ee5a681 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -108,7 +108,13 @@ enum { #ifdef MASKED_PSP_MEMORY // This wraparound should work for PSP too. +#if PPSSPP_ARCH(32BIT) + // 32-bit only reserves 256 MB at base. Valid addresses never have bits 28-29 set, so dropping them + // too keeps stray accesses inside the reservation, away from the heap. + MEMVIEW32_MASK = 0x0FFFFFFF, +#else MEMVIEW32_MASK = 0x3FFFFFFF, +#endif #endif // Index mask for the block shadow (see blockShadow below). Dropping bits 30-31 folds the