Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #12150 from AdmiralCurtiss/fast-block-cache-fix
JitCache: Fix potentially dangling pointer to fast block map.
  • Loading branch information
JMC47 committed Sep 2, 2023
2 parents 5e5887a + f1c1c6d commit 6beaee0
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 25 deletions.
38 changes: 38 additions & 0 deletions Source/Core/Common/MemArena.h
Expand Up @@ -122,4 +122,42 @@ class MemArena final
#endif
};

// This class represents a single fixed-size memory region where the individual memory pages are
// only actually allocated on first access. The memory will be zero on first access.
class LazyMemoryRegion final
{
public:
LazyMemoryRegion();
~LazyMemoryRegion();
LazyMemoryRegion(const LazyMemoryRegion&) = delete;
LazyMemoryRegion(LazyMemoryRegion&&) = delete;
LazyMemoryRegion& operator=(const LazyMemoryRegion&) = delete;
LazyMemoryRegion& operator=(LazyMemoryRegion&&) = delete;

///
/// Reserve a memory region.
///
/// @param size The size of the region.
///
/// @return The address the region was mapped at. Returns nullptr on failure.
///
void* Create(size_t size);

///
/// Reset the memory region back to zero, throwing away any mapped pages.
/// This can only be called after a successful call to Create().
///
void Clear();

///
/// Release the memory previously reserved with Create(). After this call the pointer that was
/// returned by Create() will become invalid.
///
void Release();

private:
void* m_memory = nullptr;
size_t m_size = 0;
};

} // namespace Common
43 changes: 43 additions & 0 deletions Source/Core/Common/MemArenaAndroid.cpp
Expand Up @@ -19,6 +19,7 @@
#include <sys/mman.h>
#include <unistd.h>

#include "Common/Assert.h"
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
Expand Down Expand Up @@ -142,4 +143,46 @@ void MemArena::UnmapFromMemoryRegion(void* view, size_t size)
if (retval == MAP_FAILED)
NOTICE_LOG_FMT(MEMMAP, "mmap failed");
}

LazyMemoryRegion::LazyMemoryRegion() = default;

LazyMemoryRegion::~LazyMemoryRegion()
{
Release();
}

void* LazyMemoryRegion::Create(size_t size)
{
ASSERT(!m_memory);

void* memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (!memory)
{
NOTICE_LOG_FMT(MEMMAP, "Memory allocation of {} bytes failed.", size);
return nullptr;
}

m_memory = memory;
m_size = size;

return memory;
}

void LazyMemoryRegion::Clear()
{
ASSERT(m_memory);

mmap(m_memory, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
}

void LazyMemoryRegion::Release()
{
if (m_memory)
{
munmap(m_memory, m_size);
m_memory = nullptr;
m_size = 0;
}
}

} // namespace Common
43 changes: 43 additions & 0 deletions Source/Core/Common/MemArenaUnix.cpp
Expand Up @@ -16,6 +16,7 @@
#include <sys/mman.h>
#include <unistd.h>

#include "Common/Assert.h"
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
Expand Down Expand Up @@ -108,4 +109,46 @@ void MemArena::UnmapFromMemoryRegion(void* view, size_t size)
if (retval == MAP_FAILED)
NOTICE_LOG_FMT(MEMMAP, "mmap failed");
}

LazyMemoryRegion::LazyMemoryRegion() = default;

LazyMemoryRegion::~LazyMemoryRegion()
{
Release();
}

void* LazyMemoryRegion::Create(size_t size)
{
ASSERT(!m_memory);

void* memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (!memory)
{
NOTICE_LOG_FMT(MEMMAP, "Memory allocation of {} bytes failed.", size);
return nullptr;
}

m_memory = memory;
m_size = size;

return memory;
}

void LazyMemoryRegion::Clear()
{
ASSERT(m_memory);

mmap(m_memory, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
}

void LazyMemoryRegion::Release()
{
if (m_memory)
{
munmap(m_memory, m_size);
m_memory = nullptr;
m_size = 0;
}
}

} // namespace Common
43 changes: 43 additions & 0 deletions Source/Core/Common/MemArenaWin.cpp
Expand Up @@ -433,4 +433,47 @@ void MemArena::UnmapFromMemoryRegion(void* view, size_t size)

UnmapViewOfFile(view);
}

LazyMemoryRegion::LazyMemoryRegion() = default;

LazyMemoryRegion::~LazyMemoryRegion()
{
Release();
}

void* LazyMemoryRegion::Create(size_t size)
{
ASSERT(!m_memory);

void* memory = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!memory)
{
NOTICE_LOG_FMT(MEMMAP, "Memory allocation of {} bytes failed.", size);
return nullptr;
}

m_memory = memory;
m_size = size;

return memory;
}

void LazyMemoryRegion::Clear()
{
ASSERT(m_memory);

VirtualFree(m_memory, m_size, MEM_DECOMMIT);
VirtualAlloc(m_memory, m_size, MEM_COMMIT, PAGE_READWRITE);
}

void LazyMemoryRegion::Release()
{
if (m_memory)
{
VirtualFree(m_memory, 0, MEM_RELEASE);
m_memory = nullptr;
m_size = 0;
}
}

} // namespace Common
31 changes: 7 additions & 24 deletions Source/Core/Core/PowerPC/JitCommon/JitCache.cpp
Expand Up @@ -42,7 +42,11 @@ void JitBaseBlockCache::Init()
{
Common::JitRegister::Init(Config::Get(Config::MAIN_PERF_MAP_DIR));

m_block_map_arena.GrabSHMSegment(FAST_BLOCK_MAP_SIZE, "dolphin-emu-jitblock");
m_fast_block_map = reinterpret_cast<JitBlock**>(m_block_map_arena.Create(FAST_BLOCK_MAP_SIZE));
if (m_fast_block_map)
m_fast_block_map_ptr = m_fast_block_map;
else
m_fast_block_map_ptr = m_fast_block_map_fallback.data();

Clear();
}
Expand All @@ -51,12 +55,7 @@ void JitBaseBlockCache::Shutdown()
{
Common::JitRegister::Shutdown();

if (m_fast_block_map)
{
m_block_map_arena.ReleaseView(m_fast_block_map, FAST_BLOCK_MAP_SIZE);
}

m_block_map_arena.ReleaseSHMSegment();
m_block_map_arena.Release();
}

// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
Expand All @@ -80,23 +79,7 @@ void JitBaseBlockCache::Clear()
valid_block.ClearAll();

if (m_fast_block_map)
{
m_block_map_arena.ReleaseView(m_fast_block_map, FAST_BLOCK_MAP_SIZE);
m_block_map_arena.ReleaseSHMSegment();
m_block_map_arena.GrabSHMSegment(FAST_BLOCK_MAP_SIZE, "dolphin-emu-jitblock");
}

m_fast_block_map =
reinterpret_cast<JitBlock**>(m_block_map_arena.CreateView(0, FAST_BLOCK_MAP_SIZE));

if (m_fast_block_map)
{
m_fast_block_map_ptr = m_fast_block_map;
}
else
{
m_fast_block_map_ptr = m_fast_block_map_fallback.data();
}
m_block_map_arena.Clear();
}

void JitBaseBlockCache::Reset()
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/PowerPC/JitCommon/JitCache.h
Expand Up @@ -212,7 +212,7 @@ class JitBaseBlockCache
// This is used as a fast cache of block_map used in the assembly dispatcher.
// It is implemented via a shm segment using m_block_map_arena.
JitBlock** m_fast_block_map = 0;
Common::MemArena m_block_map_arena;
Common::LazyMemoryRegion m_block_map_arena;

// An alternative for the above fast_block_map but without a shm segment
// in case the shm memory region couldn't be allocated.
Expand Down

0 comments on commit 6beaee0

Please sign in to comment.