From 6f756473c0e330b91d7d358b14b45266e4496fe5 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 8 May 2022 17:34:14 +0100 Subject: [PATCH] Don't use exceptions in low-level mem allocator --- CMakeLists.txt | 2 +- src/allocator.cpp | 9 +- src/dataset.cpp | 2 +- src/jit_compiler_a64.cpp | 4 +- src/jit_compiler_x86.cpp | 4 +- src/randomx.cpp | 8 ++ src/{virtual_memory.cpp => virtual_memory.c} | 134 ++++++++++++------- src/{virtual_memory.hpp => virtual_memory.h} | 26 ++-- 8 files changed, 121 insertions(+), 68 deletions(-) rename src/{virtual_memory.cpp => virtual_memory.c} (57%) rename src/{virtual_memory.hpp => virtual_memory.h} (80%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f41f606..4b6ba9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ src/bytecode_machine.cpp src/cpu.cpp src/dataset.cpp src/soft_aes.cpp -src/virtual_memory.cpp +src/virtual_memory.c src/vm_interpreted.cpp src/allocator.cpp src/assembly_generator_x86.cpp diff --git a/src/allocator.cpp b/src/allocator.cpp index 4c6d86e..bcee0f6 100644 --- a/src/allocator.cpp +++ b/src/allocator.cpp @@ -29,7 +29,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "allocator.hpp" #include "intrin_portable.h" -#include "virtual_memory.hpp" +#include "virtual_memory.h" #include "common.hpp" namespace randomx { @@ -50,11 +50,14 @@ namespace randomx { template struct AlignedAllocator; void* LargePageAllocator::allocMemory(size_t count) { - return allocLargePagesMemory(count); + void *mem = allocLargePagesMemory(count); + if (mem == nullptr) + throw std::bad_alloc(); + return mem; } void LargePageAllocator::freeMemory(void* ptr, size_t count) { freePagedMemory(ptr, count); }; -} \ No newline at end of file +} diff --git a/src/dataset.cpp b/src/dataset.cpp index 675c5ab..7ebf1bc 100644 --- a/src/dataset.cpp +++ b/src/dataset.cpp @@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "common.hpp" #include "dataset.hpp" -#include "virtual_memory.hpp" +#include "virtual_memory.h" #include "superscalar.hpp" #include "blake2_generator.hpp" #include "reciprocal.h" diff --git a/src/jit_compiler_a64.cpp b/src/jit_compiler_a64.cpp index e45774e..91e31d6 100644 --- a/src/jit_compiler_a64.cpp +++ b/src/jit_compiler_a64.cpp @@ -31,7 +31,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "superscalar.hpp" #include "program.hpp" #include "reciprocal.h" -#include "virtual_memory.hpp" +#include "virtual_memory.h" namespace ARMV8A { @@ -93,6 +93,8 @@ JitCompilerA64::JitCompilerA64() , literalPos(ImulRcpLiteralsEnd) , num32bitLiterals(0) { + if (code == nullptr) + throw std::runtime_error("allocMemoryPages"); memset(reg_changed_offset, 0, sizeof(reg_changed_offset)); memcpy(code, (void*) randomx_program_aarch64, CodeSize); diff --git a/src/jit_compiler_x86.cpp b/src/jit_compiler_x86.cpp index e75f763..96c6492 100644 --- a/src/jit_compiler_x86.cpp +++ b/src/jit_compiler_x86.cpp @@ -34,7 +34,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "superscalar.hpp" #include "program.hpp" #include "reciprocal.h" -#include "virtual_memory.hpp" +#include "virtual_memory.h" namespace randomx { /* @@ -225,6 +225,8 @@ namespace randomx { JitCompilerX86::JitCompilerX86() { code = (uint8_t*)allocMemoryPages(CodeSize); + if (code == nullptr) + throw std::runtime_error("allocMemoryPages"); memcpy(code, codePrologue, prologueSize); memcpy(code + epilogueOffset, codeEpilogue, epilogueSize); } diff --git a/src/randomx.cpp b/src/randomx.cpp index 7d239f6..0b33806 100644 --- a/src/randomx.cpp +++ b/src/randomx.cpp @@ -113,6 +113,10 @@ extern "C" { cache = nullptr; } } + if (cache && cache->memory == nullptr) { + randomx_release_cache(cache); + cache = nullptr; + } return cache; } @@ -162,6 +166,10 @@ extern "C" { dataset = nullptr; } } + if (dataset && dataset->memory == nullptr) { + randomx_release_dataset(dataset); + dataset = nullptr; + } return dataset; } diff --git a/src/virtual_memory.cpp b/src/virtual_memory.c similarity index 57% rename from src/virtual_memory.cpp rename to src/virtual_memory.c index caf0292..1c8ad60 100644 --- a/src/virtual_memory.cpp +++ b/src/virtual_memory.c @@ -26,9 +26,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "virtual_memory.hpp" - -#include +#include "virtual_memory.h" #if defined(_WIN32) || defined(__CYGWIN__) #include @@ -40,10 +38,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # if TARGET_OS_OSX # define USE_PTHREAD_JIT_WP 1 # include +# include # endif #endif #include #include +#include #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif @@ -53,27 +53,48 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define PAGE_EXECUTE_READWRITE (PROT_READ | PROT_WRITE | PROT_EXEC) #endif -#if defined(_WIN32) || defined(__CYGWIN__) -std::string getErrorMessage(const char* function) { - LPSTR messageBuffer = nullptr; - size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); - std::string message(messageBuffer, size); - LocalFree(messageBuffer); - return std::string(function) + std::string(": ") + message; +#if defined(USE_PTHREAD_JIT_WP) && defined(MAC_OS_VERSION_11_0) \ + && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 +static int MacOSchecked, MacOSver; +/* This function is used implicitly by clang's __builtin_available() checker. + * When cross-compiling, the library containing this function doesn't exist, + * and linking will fail because the symbol is unresolved. The function here + * is a quick and dirty hack to get close enough to identify MacOSX 11.0. + */ +static int32_t __isOSVersionAtLeast(int32_t major, int32_t minor, int32_t subminor) { + if (!MacOSchecked) { + struct utsname ut; + int mmaj, mmin; + uname(&ut); + sscanf(ut.release, "%d.%d", &mmaj, &mmin); + // The utsname release version is 9 greater than the canonical OS version + mmaj -= 9; + MacOSver = (mmaj << 8) | mmin; + MacOSchecked = 1; + } + return MacOSver >= ((major << 8) | minor); } +#endif -void setPrivilege(const char* pszPrivilege, BOOL bEnable) { + +#if defined(_WIN32) || defined(__CYGWIN__) +#define Fail(func) do {*errfunc = func; return GetLastError();} while(0) +int setPrivilege(const char* pszPrivilege, BOOL bEnable, char **errfunc) { HANDLE hToken; TOKEN_PRIVILEGES tp; BOOL status; - DWORD error; + DWORD error = 0; + + *errfunc = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) - throw std::runtime_error(getErrorMessage("OpenProcessToken")); + Fail("OpenProcessToken"); - if (!LookupPrivilegeValue(NULL, pszPrivilege, &tp.Privileges[0].Luid)) - throw std::runtime_error(getErrorMessage("LookupPrivilegeValue")); + if (!LookupPrivilegeValue(NULL, pszPrivilege, &tp.Privileges[0].Luid)) { + *errfunc = "LookupPrivilegeValue"; + error = GetLastError(); + goto out; + } tp.PrivilegeCount = 1; @@ -85,20 +106,28 @@ void setPrivilege(const char* pszPrivilege, BOOL bEnable) { status = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, 0); error = GetLastError(); - if (!status || (error != ERROR_SUCCESS)) - throw std::runtime_error(getErrorMessage("AdjustTokenPrivileges")); + if (!status || (error != ERROR_SUCCESS)) { + *errfunc = "AdjustTokenPrivileges"; + goto out; + } - if (!CloseHandle(hToken)) - throw std::runtime_error(getErrorMessage("CloseHandle")); +out: + if (!CloseHandle(hToken)) { + if (*errfunc == NULL) { + *errfunc = "CloseHandle"; + error = GetLastError(); + } + } + return error; } +#else +#define Fail(func) do {*errfunc = func; return errno;} while(0) #endif -void* allocMemoryPages(std::size_t bytes) { +void* allocMemoryPages(size_t bytes) { void* mem; #if defined(_WIN32) || defined(__CYGWIN__) - mem = VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_READWRITE); - if (mem == nullptr) - throw std::runtime_error(getErrorMessage("allocMemoryPages - VirtualAlloc")); + mem = VirtualAlloc(NULL, bytes, MEM_COMMIT, PAGE_READWRITE); #else #if defined(__NetBSD__) #define RESERVED_FLAGS PROT_MPROTECT(PROT_EXEC) @@ -112,9 +141,7 @@ void* allocMemoryPages(std::size_t bytes) { #define MEXTRA 0 #define PEXTRA 0 #endif - mem = mmap(nullptr, bytes, PAGE_READWRITE | RESERVED_FLAGS | PEXTRA, MAP_ANONYMOUS | MAP_PRIVATE | MEXTRA, -1, 0); - if (mem == MAP_FAILED) - throw std::runtime_error("allocMemoryPages - mmap failed"); + mem = mmap(NULL, bytes, PAGE_READWRITE | RESERVED_FLAGS | PEXTRA, MAP_ANONYMOUS | MAP_PRIVATE | MEXTRA, -1, 0); #if defined(USE_PTHREAD_JIT_WP) && defined(MAC_OS_VERSION_11_0) \ && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 if (__builtin_available(macOS 11.0, *)) { @@ -125,76 +152,81 @@ void* allocMemoryPages(std::size_t bytes) { return mem; } -static inline void pageProtect(void* ptr, std::size_t bytes, int rules) { +static inline int pageProtect(void* ptr, size_t bytes, int rules, char **errfunc) { #if defined(_WIN32) || defined(__CYGWIN__) DWORD oldp; if (!VirtualProtect(ptr, bytes, (DWORD)rules, &oldp)) { - throw std::runtime_error(getErrorMessage("VirtualProtect")); + Fail("VirtualProtect"); } #else if (-1 == mprotect(ptr, bytes, rules)) - throw std::runtime_error("mprotect failed"); + Fail("mprotect"); #endif + return 0; } -void setPagesRW(void* ptr, std::size_t bytes) { +void setPagesRW(void* ptr, size_t bytes) { + char *errfunc; #if defined(USE_PTHREAD_JIT_WP) && defined(MAC_OS_VERSION_11_0) \ && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 if (__builtin_available(macOS 11.0, *)) { pthread_jit_write_protect_np(false); } else { - pageProtect(ptr, bytes, PAGE_READWRITE); + pageProtect(ptr, bytes, PAGE_READWRITE, &errfunc); } #else - pageProtect(ptr, bytes, PAGE_READWRITE); + pageProtect(ptr, bytes, PAGE_READWRITE, &errfunc); #endif } -void setPagesRX(void* ptr, std::size_t bytes) { +void setPagesRX(void* ptr, size_t bytes) { + char *errfunc; #if defined(USE_PTHREAD_JIT_WP) && defined(MAC_OS_VERSION_11_0) \ && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 if (__builtin_available(macOS 11.0, *)) { pthread_jit_write_protect_np(true); } else { - pageProtect(ptr, bytes, PAGE_EXECUTE_READ); + pageProtect(ptr, bytes, PAGE_EXECUTE_READ, &errfunc); } #else - pageProtect(ptr, bytes, PAGE_EXECUTE_READ); + pageProtect(ptr, bytes, PAGE_EXECUTE_READ, &errfunc); #endif } -void setPagesRWX(void* ptr, std::size_t bytes) { - pageProtect(ptr, bytes, PAGE_EXECUTE_READWRITE); +void setPagesRWX(void* ptr, size_t bytes) { + char *errfunc; + pageProtect(ptr, bytes, PAGE_EXECUTE_READWRITE, &errfunc); } -void* allocLargePagesMemory(std::size_t bytes) { +void* allocLargePagesMemory(size_t bytes) { void* mem; + char *errfunc; #if defined(_WIN32) || defined(__CYGWIN__) - setPrivilege("SeLockMemoryPrivilege", 1); + if (setPrivilege("SeLockMemoryPrivilege", 1, &errfunc)) + return NULL; auto pageMinimum = GetLargePageMinimum(); - if (pageMinimum > 0) - mem = VirtualAlloc(NULL, alignSize(bytes, pageMinimum), MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE); - else - throw std::runtime_error("allocLargePagesMemory - Large pages are not supported"); - if (mem == nullptr) - throw std::runtime_error(getErrorMessage("allocLargePagesMemory - VirtualAlloc")); + if (pageMinimum <= 0) { + errfunc = "No large pages"; + return NULL; + } + mem = VirtualAlloc(NULL, alignSize(bytes, pageMinimum), MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE); #else #ifdef __APPLE__ - mem = mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); + mem = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); #elif defined(__FreeBSD__) - mem = mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0); + mem = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0); #elif defined(__OpenBSD__) || defined(__NetBSD__) mem = MAP_FAILED; // OpenBSD does not support huge pages #else - mem = mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1, 0); + mem = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1, 0); #endif if (mem == MAP_FAILED) - throw std::runtime_error("allocLargePagesMemory - mmap failed"); + mem = NULL; #endif return mem; } -void freePagedMemory(void* ptr, std::size_t bytes) { +void freePagedMemory(void* ptr, size_t bytes) { #if defined(_WIN32) || defined(__CYGWIN__) VirtualFree(ptr, 0, MEM_RELEASE); #else diff --git a/src/virtual_memory.hpp b/src/virtual_memory.h similarity index 80% rename from src/virtual_memory.hpp rename to src/virtual_memory.h index 9e8bc29..5e8e31d 100644 --- a/src/virtual_memory.hpp +++ b/src/virtual_memory.h @@ -28,15 +28,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once -#include +#ifdef __cplusplus +extern "C" { +#endif -constexpr std::size_t alignSize(std::size_t pos, std::size_t align) { - return ((pos - 1) / align + 1) * align; -} +#include + +#define alignSize(pos, align) (((pos - 1) / align + 1) * align) -void* allocMemoryPages(std::size_t); -void setPagesRW(void*, std::size_t); -void setPagesRX(void*, std::size_t); -void setPagesRWX(void*, std::size_t); -void* allocLargePagesMemory(std::size_t); -void freePagedMemory(void*, std::size_t); +void* allocMemoryPages(size_t); +void setPagesRW(void*, size_t); +void setPagesRX(void*, size_t); +void setPagesRWX(void*, size_t); +void* allocLargePagesMemory(size_t); +void freePagedMemory(void*, size_t); + +#ifdef __cplusplus +} +#endif