From: Lang Hames Date: Thu, 16 Nov 2017 23:04:44 +0000 (+0000) Subject: [Support] Support NetBSD PaX MPROTECT in sys::Memory. X-Git-Tag: llvmorg-6.0.0-rc1~3181 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=afcb70d0319a6e1b97cd74157e61f095cbc769c6;p=platform%2Fupstream%2Fllvm.git [Support] Support NetBSD PaX MPROTECT in sys::Memory. Removes AllocateRWX, setWritable and setExecutable from sys::Memory and standardizes on allocateMappedMemory / protectMappedMemory. The allocateMappedMemory method is updated to request full permissions for memory blocks so that they can be marked executable later. llvm-svn: 318464 --- diff --git a/llvm/include/llvm/Support/Memory.h b/llvm/include/llvm/Support/Memory.h index 8103aea..3140dc6 100644 --- a/llvm/include/llvm/Support/Memory.h +++ b/llvm/include/llvm/Support/Memory.h @@ -109,51 +109,10 @@ namespace sys { static std::error_code protectMappedMemory(const MemoryBlock &Block, unsigned Flags); - /// This method allocates a block of Read/Write/Execute memory that is - /// suitable for executing dynamically generated code (e.g. JIT). An - /// attempt to allocate \p NumBytes bytes of virtual memory is made. - /// \p NearBlock may point to an existing allocation in which case - /// an attempt is made to allocate more memory near the existing block. - /// - /// On success, this returns a non-null memory block, otherwise it returns - /// a null memory block and fills in *ErrMsg. - /// - /// @brief Allocate Read/Write/Execute memory. - static MemoryBlock AllocateRWX(size_t NumBytes, - const MemoryBlock *NearBlock, - std::string *ErrMsg = nullptr); - - /// This method releases a block of Read/Write/Execute memory that was - /// allocated with the AllocateRWX method. It should not be used to - /// release any memory block allocated any other way. - /// - /// On success, this returns false, otherwise it returns true and fills - /// in *ErrMsg. - /// @brief Release Read/Write/Execute memory. - static bool ReleaseRWX(MemoryBlock &block, std::string *ErrMsg = nullptr); - /// InvalidateInstructionCache - Before the JIT can run a block of code /// that has been emitted it must invalidate the instruction cache on some /// platforms. static void InvalidateInstructionCache(const void *Addr, size_t Len); - - /// setExecutable - Before the JIT can run a block of code, it has to be - /// given read and executable privilege. Return true if it is already r-x - /// or the system is able to change its previlege. - static bool setExecutable(MemoryBlock &M, std::string *ErrMsg = nullptr); - - /// setWritable - When adding to a block of code, the JIT may need - /// to mark a block of code as RW since the protections are on page - /// boundaries, and the JIT internal allocations are not page aligned. - static bool setWritable(MemoryBlock &M, std::string *ErrMsg = nullptr); - - /// setRangeExecutable - Mark the page containing a range of addresses - /// as executable. - static bool setRangeExecutable(const void *Addr, size_t Size); - - /// setRangeWritable - Mark the page containing a range of addresses - /// as writable. - static bool setRangeWritable(const void *Addr, size_t Size); }; /// Owning version of MemoryBlock. diff --git a/llvm/lib/Support/Unix/Memory.inc b/llvm/lib/Support/Unix/Memory.inc index cf812d0..3148016 100644 --- a/llvm/lib/Support/Unix/Memory.inc +++ b/llvm/lib/Support/Unix/Memory.inc @@ -102,6 +102,10 @@ Memory::allocateMappedMemory(size_t NumBytes, int Protect = getPosixProtectionFlags(PFlags); +#if defined(__NetBSD__) && defined(PROT_MPROTECT) + Protect |= PROT_MPROTECT(PROT_READ | PROT_WRITE | PROT_EXEC); +#endif + // Use any near hint and the page size to set a page-aligned starting address uintptr_t Start = NearBlock ? reinterpret_cast(NearBlock->base()) + NearBlock->size() : 0; @@ -166,129 +170,6 @@ Memory::protectMappedMemory(const MemoryBlock &M, unsigned Flags) { return std::error_code(); } -/// AllocateRWX - Allocate a slab of memory with read/write/execute -/// permissions. This is typically used for JIT applications where we want -/// to emit code to the memory then jump to it. Getting this type of memory -/// is very OS specific. -/// -MemoryBlock -Memory::AllocateRWX(size_t NumBytes, const MemoryBlock* NearBlock, - std::string *ErrMsg) { - if (NumBytes == 0) return MemoryBlock(); - - static const size_t PageSize = Process::getPageSize(); - size_t NumPages = (NumBytes+PageSize-1)/PageSize; - - int fd = -1; - - int flags = MAP_PRIVATE | -#ifdef MAP_ANONYMOUS - MAP_ANONYMOUS -#else - MAP_ANON -#endif - ; - - void* start = NearBlock ? (unsigned char*)NearBlock->base() + - NearBlock->size() : nullptr; - -#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) - void *pa = ::mmap(start, PageSize*NumPages, PROT_READ|PROT_EXEC, - flags, fd, 0); -#elif defined(__NetBSD__) && defined(PROT_MPROTECT) - void *pa = - ::mmap(start, PageSize * NumPages, - PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC), flags, fd, 0); -#else - void *pa = ::mmap(start, PageSize*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC, - flags, fd, 0); -#endif - if (pa == MAP_FAILED) { - if (NearBlock) //Try again without a near hint - return AllocateRWX(NumBytes, nullptr); - - MakeErrMsg(ErrMsg, "Can't allocate RWX Memory"); - return MemoryBlock(); - } - -#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) - kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)pa, - (vm_size_t)(PageSize*NumPages), 0, - VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY); - if (KERN_SUCCESS != kr) { - MakeErrMsg(ErrMsg, "vm_protect max RX failed"); - return MemoryBlock(); - } - - kr = vm_protect(mach_task_self(), (vm_address_t)pa, - (vm_size_t)(PageSize*NumPages), 0, - VM_PROT_READ | VM_PROT_WRITE); - if (KERN_SUCCESS != kr) { - MakeErrMsg(ErrMsg, "vm_protect RW failed"); - return MemoryBlock(); - } -#endif - - MemoryBlock result; - result.Address = pa; - result.Size = NumPages*PageSize; - - return result; -} - -bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) { - if (M.Address == nullptr || M.Size == 0) return false; - if (0 != ::munmap(M.Address, M.Size)) - return MakeErrMsg(ErrMsg, "Can't release RWX Memory"); - return false; -} - -bool Memory::setWritable (MemoryBlock &M, std::string *ErrMsg) { -#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) - if (M.Address == 0 || M.Size == 0) return false; - Memory::InvalidateInstructionCache(M.Address, M.Size); - kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address, - (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_WRITE); - return KERN_SUCCESS == kr; -#else - return true; -#endif -} - -bool Memory::setExecutable (MemoryBlock &M, std::string *ErrMsg) { - if (M.Address == nullptr || M.Size == 0) return false; - Memory::InvalidateInstructionCache(M.Address, M.Size); -#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) - kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address, - (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY); - return KERN_SUCCESS == kr; -#else - return true; -#endif -} - -bool Memory::setRangeWritable(const void *Addr, size_t Size) { -#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) - kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr, - (vm_size_t)Size, 0, - VM_PROT_READ | VM_PROT_WRITE); - return KERN_SUCCESS == kr; -#else - return true; -#endif -} - -bool Memory::setRangeExecutable(const void *Addr, size_t Size) { -#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) - kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr, - (vm_size_t)Size, 0, - VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY); - return KERN_SUCCESS == kr; -#else - return true; -#endif -} - /// InvalidateInstructionCache - Before the JIT can run a block of code /// that has been emitted it must invalidate the instruction cache on some /// platforms. diff --git a/llvm/lib/Support/Windows/Memory.inc b/llvm/lib/Support/Windows/Memory.inc index 7eab9ff..318e65a 100644 --- a/llvm/lib/Support/Windows/Memory.inc +++ b/llvm/lib/Support/Windows/Memory.inc @@ -160,85 +160,5 @@ void Memory::InvalidateInstructionCache( FlushInstructionCache(GetCurrentProcess(), Addr, Len); } - -MemoryBlock Memory::AllocateRWX(size_t NumBytes, - const MemoryBlock *NearBlock, - std::string *ErrMsg) { - MemoryBlock MB; - std::error_code EC; - MB = allocateMappedMemory(NumBytes, NearBlock, - MF_READ|MF_WRITE|MF_EXEC, EC); - if (EC != std::error_code() && ErrMsg) { - MakeErrMsg(ErrMsg, EC.message()); - } - return MB; -} - -bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) { - std::error_code EC = releaseMappedMemory(M); - if (EC == std::error_code()) - return false; - MakeErrMsg(ErrMsg, EC.message()); - return true; -} - -static DWORD getProtection(const void *addr) { - MEMORY_BASIC_INFORMATION info; - if (sizeof(info) == ::VirtualQuery(addr, &info, sizeof(info))) { - return info.Protect; - } - return 0; -} - -bool Memory::setWritable(MemoryBlock &M, std::string *ErrMsg) { - if (!setRangeWritable(M.Address, M.Size)) { - return MakeErrMsg(ErrMsg, "Cannot set memory to writeable"); - } - return true; -} - -bool Memory::setExecutable(MemoryBlock &M, std::string *ErrMsg) { - if (!setRangeExecutable(M.Address, M.Size)) { - return MakeErrMsg(ErrMsg, "Cannot set memory to executable"); - } - return true; -} - -bool Memory::setRangeWritable(const void *Addr, size_t Size) { - DWORD prot = getProtection(Addr); - if (!prot) - return false; - - if (prot == PAGE_EXECUTE || prot == PAGE_EXECUTE_READ) { - prot = PAGE_EXECUTE_READWRITE; - } else if (prot == PAGE_NOACCESS || prot == PAGE_READONLY) { - prot = PAGE_READWRITE; - } - - DWORD oldProt; - Memory::InvalidateInstructionCache(Addr, Size); - return ::VirtualProtect(const_cast(Addr), Size, prot, &oldProt) - == TRUE; -} - -bool Memory::setRangeExecutable(const void *Addr, size_t Size) { - DWORD prot = getProtection(Addr); - if (!prot) - return false; - - if (prot == PAGE_NOACCESS) { - prot = PAGE_EXECUTE; - } else if (prot == PAGE_READONLY) { - prot = PAGE_EXECUTE_READ; - } else if (prot == PAGE_READWRITE) { - prot = PAGE_EXECUTE_READWRITE; - } - - DWORD oldProt; - Memory::InvalidateInstructionCache(Addr, Size); - return ::VirtualProtect(const_cast(Addr), Size, prot, &oldProt) - == TRUE; -} - } // namespace sys } // namespace llvm diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp index daafcdf..f48c3f0 100644 --- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -178,10 +178,14 @@ public: void deregisterEHFrames() override {} void preallocateSlab(uint64_t Size) { - std::string Err; - sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err); + std::error_code EC; + sys::MemoryBlock MB = + sys::Memory::allocateMappedMemory(Size, nullptr, + sys::Memory::MF_READ | + sys::Memory::MF_WRITE, + EC); if (!MB.base()) - report_fatal_error("Can't allocate enough memory: " + Err); + report_fatal_error("Can't allocate enough memory: " + EC.message()); PreallocSlab = MB; UsePreallocation = true; @@ -222,10 +226,14 @@ uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, if (UsePreallocation) return allocateFromSlab(Size, Alignment, true /* isCode */); - std::string Err; - sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err); + std::error_code EC; + sys::MemoryBlock MB = + sys::Memory::allocateMappedMemory(Size, nullptr, + sys::Memory::MF_READ | + sys::Memory::MF_WRITE, + EC); if (!MB.base()) - report_fatal_error("MemoryManager allocation failed: " + Err); + report_fatal_error("MemoryManager allocation failed: " + EC.message()); FunctionMemory.push_back(MB); return (uint8_t*)MB.base(); } @@ -242,10 +250,14 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size, if (UsePreallocation) return allocateFromSlab(Size, Alignment, false /* isCode */); - std::string Err; - sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err); + std::error_code EC; + sys::MemoryBlock MB = + sys::Memory::allocateMappedMemory(Size, nullptr, + sys::Memory::MF_READ | + sys::Memory::MF_WRITE, + EC); if (!MB.base()) - report_fatal_error("MemoryManager allocation failed: " + Err); + report_fatal_error("MemoryManager allocation failed: " + EC.message()); DataMemory.push_back(MB); return (uint8_t*)MB.base(); } @@ -453,9 +465,11 @@ static int executeInput() { // Make sure the memory is executable. // setExecutable will call InvalidateInstructionCache. - std::string ErrorStr; - if (!sys::Memory::setExecutable(FM, &ErrorStr)) - ErrorAndExit("unable to mark function executable: '" + ErrorStr + "'"); + if (auto EC = sys::Memory::protectMappedMemory(FM, + sys::Memory::MF_READ | + sys::Memory::MF_EXEC)) + ErrorAndExit("unable to mark function executable: '" + EC.message() + + "'"); } // Dispatch to _main().