Sfoglia il codice sorgente

Optional W^X policy for JIT pages (#112)

tevador 7 anni fa
parent
commit
67010ab554

+ 2 - 0
src/dataset.cpp

@@ -140,8 +140,10 @@ namespace randomx {
 
 	void initCacheCompile(randomx_cache* cache, const void* key, size_t keySize) {
 		initCache(cache, key, keySize);
+		cache->jit->enableWriting();
 		cache->jit->generateSuperscalarHash(cache->programs, cache->reciprocalCache);
 		cache->jit->generateDatasetInitCode();
+		cache->jit->enableExecution();
 	}
 
 	constexpr uint64_t superscalarMul0 = 6364136223846793005ULL;

+ 13 - 1
src/jit_compiler_x86.cpp

@@ -218,7 +218,7 @@ namespace randomx {
 	}
 
 	JitCompilerX86::JitCompilerX86() {
-		code = (uint8_t*)allocExecutableMemory(CodeSize);
+		code = (uint8_t*)allocMemoryPages(CodeSize);
 		memcpy(code, codePrologue, prologueSize);
 		memcpy(code + epilogueOffset, codeEpilogue, epilogueSize);
 	}
@@ -227,6 +227,18 @@ namespace randomx {
 		freePagedMemory(code, CodeSize);
 	}
 
+	void JitCompilerX86::enableAll() {
+		setPagesRWX(code, CodeSize);
+	}
+
+	void JitCompilerX86::enableWriting() {
+		setPagesRW(code, CodeSize);
+	}
+
+	void JitCompilerX86::enableExecution() {
+		setPagesRX(code, CodeSize);
+	}
+
 	void JitCompilerX86::generateProgram(Program& prog, ProgramConfiguration& pcfg) {
 		generateProgramPrologue(prog, pcfg);
 		memcpy(code + codePos, codeReadDataset, readDatasetSize);

+ 3 - 0
src/jit_compiler_x86.hpp

@@ -62,6 +62,9 @@ namespace randomx {
 			return code;
 		}
 		size_t getCodeSize();
+		void enableWriting();
+		void enableExecution();
+		void enableAll();
 	private:
 		static InstructionGeneratorX86 engine[256];
 		std::vector<int32_t> instructionOffsets;

+ 48 - 8
src/randomx.cpp

@@ -175,11 +175,21 @@ extern "C" {
 					break;
 
 				case RANDOMX_FLAG_JIT:
-					vm = new randomx::CompiledLightVmDefault();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledLightVmDefaultSecure();
+					}
+					else {
+						vm = new randomx::CompiledLightVmDefault();
+					}
 					break;
 
 				case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT:
-					vm = new randomx::CompiledVmDefault();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledVmDefaultSecure();
+					}
+					else {
+						vm = new randomx::CompiledVmDefault();
+					}
 					break;
 
 				case RANDOMX_FLAG_HARD_AES:
@@ -191,11 +201,21 @@ extern "C" {
 					break;
 
 				case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES:
-					vm = new randomx::CompiledLightVmHardAes();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledLightVmHardAesSecure();
+					}
+					else {
+						vm = new randomx::CompiledLightVmHardAes();
+					}
 					break;
 
 				case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES:
-					vm = new randomx::CompiledVmHardAes();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledVmHardAesSecure();
+					}
+					else {
+						vm = new randomx::CompiledVmHardAes();
+					}
 					break;
 
 				case RANDOMX_FLAG_LARGE_PAGES:
@@ -207,11 +227,21 @@ extern "C" {
 					break;
 
 				case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES:
-					vm = new randomx::CompiledLightVmLargePage();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledLightVmLargePageSecure();
+					}
+					else {
+						vm = new randomx::CompiledLightVmLargePage();
+					}
 					break;
 
 				case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES:
-					vm = new randomx::CompiledVmLargePage();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledVmLargePageSecure();
+					}
+					else {
+						vm = new randomx::CompiledVmLargePage();
+					}
 					break;
 
 				case RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES:
@@ -223,11 +253,21 @@ extern "C" {
 					break;
 
 				case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES:
-					vm = new randomx::CompiledLightVmLargePageHardAes();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledLightVmLargePageHardAesSecure();
+					}
+					else {
+						vm = new randomx::CompiledLightVmLargePageHardAes();
+					}
 					break;
 
 				case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES:
-					vm = new randomx::CompiledVmLargePageHardAes();
+					if (flags & RANDOMX_FLAG_SECURE) {
+						vm = new randomx::CompiledVmLargePageHardAesSecure();
+					}
+					else {
+						vm = new randomx::CompiledVmLargePageHardAes();
+					}
 					break;
 
 				default:

+ 5 - 2
src/randomx.h

@@ -44,6 +44,7 @@ typedef enum {
   RANDOMX_FLAG_HARD_AES = 2,
   RANDOMX_FLAG_FULL_MEM = 4,
   RANDOMX_FLAG_JIT = 8,
+  RANDOMX_FLAG_SECURE = 16
 } randomx_flags;
 
 typedef struct randomx_dataset randomx_dataset;
@@ -135,12 +136,14 @@ RANDOMX_EXPORT void randomx_release_dataset(randomx_dataset *dataset);
 /**
  * Creates and initializes a RandomX virtual machine.
  *
- * @param flags is any combination of these 4 flags (each flag can be set or not set):
+ * @param flags is any combination of these 5 flags (each flag can be set or not set):
  *        RANDOMX_FLAG_LARGE_PAGES - allocate scratchpad memory in large pages
  *        RANDOMX_FLAG_HARD_AES - virtual machine will use hardware accelerated AES
  *        RANDOMX_FLAG_FULL_MEM - virtual machine will use the full dataset
  *        RANDOMX_FLAG_JIT - virtual machine will use a JIT compiler
- *        The numeric values of the flags are ordered so that a higher value will provide
+ *        RANDOMX_FLAG_SECURE - when combined with RANDOMX_FLAG_JIT, the JIT pages are never
+ *                              writable and executable at the same time (W^X policy)
+ *        The numeric values of the first 4 flags are ordered so that a higher value will provide
  *        faster hash calculation and a lower numeric value will provide higher portability.
  *        Using RANDOMX_FLAG_DEFAULT (all flags not set) works on all platforms, but is the slowest.
  * @param cache is a pointer to an initialized randomx_cache structure. Can be

+ 9 - 2
src/tests/benchmark.cpp

@@ -82,6 +82,7 @@ void printUsage(const char* executable) {
 	std::cout << "  --mine        mining mode: 2080 MiB" << std::endl;
 	std::cout << "  --verify      verification mode: 256 MiB" << std::endl;
 	std::cout << "  --jit         x86-64 JIT compiled mode (default: interpreter)" << std::endl;
+	std::cout << "  --secure      W^X policy for JIT pages (default: off)" << std::endl;
 	std::cout << "  --largePages  use large pages" << std::endl;
 	std::cout << "  --softAes     use software AES (default: x86 AES-NI)" << std::endl;
 	std::cout << "  --threads T   use T threads (default: 1)" << std::endl;
@@ -126,7 +127,7 @@ void mine(randomx_vm* vm, std::atomic<uint32_t>& atomicNonce, AtomicHash& result
 }
 
 int main(int argc, char** argv) {
-	bool softAes, miningMode, verificationMode, help, largePages, jit;
+	bool softAes, miningMode, verificationMode, help, largePages, jit, secure;
 	int noncesCount, threadCount, initThreadCount;
 	uint64_t threadAffinity;
 	int32_t seedValue;
@@ -143,6 +144,7 @@ int main(int argc, char** argv) {
 	readOption("--largePages", argc, argv, largePages);
 	readOption("--jit", argc, argv, jit);
 	readOption("--help", argc, argv, help);
+	readOption("--secure", argc, argv, secure);
 
 	store32(&seed, seedValue);
 
@@ -171,7 +173,12 @@ int main(int argc, char** argv) {
 
 	if (jit) {
 		flags = (randomx_flags)(flags | RANDOMX_FLAG_JIT);
-		std::cout << " - JIT compiled mode" << std::endl;
+		std::cout << " - JIT compiled mode ";
+		if (secure) {
+			flags = (randomx_flags)(flags | RANDOMX_FLAG_SECURE);
+			std::cout << "(secure)";
+		}
+		std::cout << std::endl;
 	}
 	else {
 		std::cout << " - interpreted mode" << std::endl;

+ 33 - 5
src/virtual_memory.cpp

@@ -41,6 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef MAP_ANONYMOUS
 #define MAP_ANONYMOUS MAP_ANON
 #endif
+#define PAGE_READONLY PROT_READ
+#define PAGE_READWRITE (PROT_READ | PROT_WRITE)
+#define PAGE_EXECUTE_READ (PROT_READ | PROT_EXEC)
+#define PAGE_EXECUTE_READWRITE (PROT_READ | PROT_WRITE | PROT_EXEC)
 #endif
 
 #if defined(_WIN32) || defined(__CYGWIN__)
@@ -83,20 +87,44 @@ void setPrivilege(const char* pszPrivilege, BOOL bEnable) {
 }
 #endif
 
-void* allocExecutableMemory(std::size_t bytes) {
+void* allocMemoryPages(std::size_t bytes) {
 	void* mem;
 #if defined(_WIN32) || defined(__CYGWIN__)
-	mem = VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+	mem = VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_READWRITE);
 	if (mem == nullptr)
-		throw std::runtime_error(getErrorMessage("allocExecutableMemory - VirtualAlloc"));
+		throw std::runtime_error(getErrorMessage("allocMemoryPages - VirtualAlloc"));
 #else
-	mem = mmap(nullptr, bytes, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	mem = mmap(nullptr, bytes, PAGE_READWRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 	if (mem == MAP_FAILED)
-		throw std::runtime_error("allocExecutableMemory - mmap failed");
+		throw std::runtime_error("allocMemoryPages - mmap failed");
 #endif
 	return mem;
 }
 
+static inline void pageProtect(void* ptr, std::size_t bytes, int rules) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+	DWORD oldp;
+	if (!VirtualProtect(ptr, bytes, (DWORD)rules, &oldp)) {
+		throw std::runtime_error(getErrorMessage("VirtualProtect"));
+	}
+#else
+	if (-1 == mprotect(ptr, bytes, rules))
+		throw std::runtime_error("mprotect failed");
+#endif
+}
+
+void setPagesRW(void* ptr, std::size_t bytes) {
+	pageProtect(ptr, bytes, PAGE_READWRITE);
+}
+
+void setPagesRX(void* ptr, std::size_t bytes) {
+	pageProtect(ptr, bytes, PAGE_EXECUTE_READ);
+}
+
+void setPagesRWX(void* ptr, std::size_t bytes) {
+	pageProtect(ptr, bytes, PAGE_EXECUTE_READWRITE);
+}
+
 void* allocLargePagesMemory(std::size_t bytes) {
 	void* mem;
 #if defined(_WIN32) || defined(__CYGWIN__)

+ 4 - 1
src/virtual_memory.hpp

@@ -34,6 +34,9 @@ constexpr std::size_t alignSize(std::size_t pos, std::size_t align) {
 	return ((pos - 1) / align + 1) * align;
 }
 
-void* allocExecutableMemory(std::size_t);
+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);

+ 27 - 10
src/vm_compiled.cpp

@@ -34,27 +34,44 @@ namespace randomx {
 	static_assert(sizeof(MemoryRegisters) == 2 * sizeof(addr_t) + sizeof(uintptr_t), "Invalid alignment of struct randomx::MemoryRegisters");
 	static_assert(sizeof(RegisterFile) == 256, "Invalid alignment of struct randomx::RegisterFile");
 
-	template<class Allocator, bool softAes>
-	void CompiledVm<Allocator, softAes>::setDataset(randomx_dataset* dataset) {
+	template<class Allocator, bool softAes, bool secureJit>
+	CompiledVm<Allocator, softAes, secureJit>::CompiledVm() {
+		if (!secureJit) {
+			compiler.enableAll(); //make JIT buffer both writable and executable
+		}
+	}
+
+	template<class Allocator, bool softAes, bool secureJit>
+	void CompiledVm<Allocator, softAes, secureJit>::setDataset(randomx_dataset* dataset) {
 		datasetPtr = dataset;
 	}
 
-	template<class Allocator, bool softAes>
-	void CompiledVm<Allocator, softAes>::run(void* seed) {
+	template<class Allocator, bool softAes, bool secureJit>
+	void CompiledVm<Allocator, softAes, secureJit>::run(void* seed) {
 		VmBase<Allocator, softAes>::generateProgram(seed);
 		randomx_vm::initialize();
+		if (secureJit) {
+			compiler.enableWriting();
+		}
 		compiler.generateProgram(program, config);
+		if (secureJit) {
+			compiler.enableExecution();
+		}
 		mem.memory = datasetPtr->memory + datasetOffset;
 		execute();
 	}
 
-	template<class Allocator, bool softAes>
-	void CompiledVm<Allocator, softAes>::execute() {
+	template<class Allocator, bool softAes, bool secureJit>
+	void CompiledVm<Allocator, softAes, secureJit>::execute() {
 		compiler.getProgramFunc()(reg, mem, scratchpad, RANDOMX_PROGRAM_ITERATIONS);
 	}
 
-	template class CompiledVm<AlignedAllocator<CacheLineSize>, false>;
-	template class CompiledVm<AlignedAllocator<CacheLineSize>, true>;
-	template class CompiledVm<LargePageAllocator, false>;
-	template class CompiledVm<LargePageAllocator, true>;
+	template class CompiledVm<AlignedAllocator<CacheLineSize>, false, false>;
+	template class CompiledVm<AlignedAllocator<CacheLineSize>, true, false>;
+	template class CompiledVm<LargePageAllocator, false, false>;
+	template class CompiledVm<LargePageAllocator, true, false>;
+	template class CompiledVm<AlignedAllocator<CacheLineSize>, false, true>;
+	template class CompiledVm<AlignedAllocator<CacheLineSize>, true, true>;
+	template class CompiledVm<LargePageAllocator, false, true>;
+	template class CompiledVm<LargePageAllocator, true, true>;
 }

+ 10 - 5
src/vm_compiled.hpp

@@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace randomx {
 
-	template<class Allocator, bool softAes>
+	template<class Allocator, bool softAes, bool secureJit>
 	class CompiledVm : public VmBase<Allocator, softAes> {
 	public:
 		void* operator new(size_t size) {
@@ -49,6 +49,7 @@ namespace randomx {
 		void operator delete(void* ptr) {
 			AlignedAllocator<CacheLineSize>::freeMemory(ptr, sizeof(CompiledVm));
 		}
+		CompiledVm();
 		void setDataset(randomx_dataset* dataset) override;
 		void run(void* seed) override;
 
@@ -65,8 +66,12 @@ namespace randomx {
 		JitCompiler compiler;
 	};
 
-	using CompiledVmDefault = CompiledVm<AlignedAllocator<CacheLineSize>, true>;
-	using CompiledVmHardAes = CompiledVm<AlignedAllocator<CacheLineSize>, false>;
-	using CompiledVmLargePage = CompiledVm<LargePageAllocator, true>;
-	using CompiledVmLargePageHardAes = CompiledVm<LargePageAllocator, false>;
+	using CompiledVmDefault = CompiledVm<AlignedAllocator<CacheLineSize>, true, false>;
+	using CompiledVmHardAes = CompiledVm<AlignedAllocator<CacheLineSize>, false, false>;
+	using CompiledVmLargePage = CompiledVm<LargePageAllocator, true, false>;
+	using CompiledVmLargePageHardAes = CompiledVm<LargePageAllocator, false, false>;
+	using CompiledVmDefaultSecure = CompiledVm<AlignedAllocator<CacheLineSize>, true, true>;
+	using CompiledVmHardAesSecure = CompiledVm<AlignedAllocator<CacheLineSize>, false, true>;
+	using CompiledVmLargePageSecure = CompiledVm<LargePageAllocator, true, true>;
+	using CompiledVmLargePageHardAesSecure = CompiledVm<LargePageAllocator, false, true>;
 }

+ 25 - 9
src/vm_compiled_light.cpp

@@ -32,23 +32,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace randomx {
 
-	template<class Allocator, bool softAes>
-	void CompiledLightVm<Allocator, softAes>::setCache(randomx_cache* cache) {
+	template<class Allocator, bool softAes, bool secureJit>
+	void CompiledLightVm<Allocator, softAes, secureJit>::setCache(randomx_cache* cache) {
 		cachePtr = cache;
 		mem.memory = cache->memory;
+		if (secureJit) {
+			compiler.enableWriting();
+		}
 		compiler.generateSuperscalarHash(cache->programs, cache->reciprocalCache);
+		if (secureJit) {
+			compiler.enableExecution();
+		}
 	}
 
-	template<class Allocator, bool softAes>
-	void CompiledLightVm<Allocator, softAes>::run(void* seed) {
+	template<class Allocator, bool softAes, bool secureJit>
+	void CompiledLightVm<Allocator, softAes, secureJit>::run(void* seed) {
 		VmBase<Allocator, softAes>::generateProgram(seed);
 		randomx_vm::initialize();
+		if (secureJit) {
+			compiler.enableWriting();
+		}
 		compiler.generateProgramLight(program, config, datasetOffset);
-		CompiledVm<Allocator, softAes>::execute();
+		if (secureJit) {
+			compiler.enableExecution();
+		}
+		CompiledVm<Allocator, softAes, secureJit>::execute();
 	}
 
-	template class CompiledLightVm<AlignedAllocator<CacheLineSize>, false>;
-	template class CompiledLightVm<AlignedAllocator<CacheLineSize>, true>;
-	template class CompiledLightVm<LargePageAllocator, false>;
-	template class CompiledLightVm<LargePageAllocator, true>;
+	template class CompiledLightVm<AlignedAllocator<CacheLineSize>, false, false>;
+	template class CompiledLightVm<AlignedAllocator<CacheLineSize>, true, false>;
+	template class CompiledLightVm<LargePageAllocator, false, false>;
+	template class CompiledLightVm<LargePageAllocator, true, false>;
+	template class CompiledLightVm<AlignedAllocator<CacheLineSize>, false, true>;
+	template class CompiledLightVm<AlignedAllocator<CacheLineSize>, true, true>;
+	template class CompiledLightVm<LargePageAllocator, false, true>;
+	template class CompiledLightVm<LargePageAllocator, true, true>;
 }

+ 16 - 12
src/vm_compiled_light.hpp

@@ -33,8 +33,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace randomx {
 
-	template<class Allocator, bool softAes>
-	class CompiledLightVm : public CompiledVm<Allocator, softAes> {
+	template<class Allocator, bool softAes, bool secureJit>
+	class CompiledLightVm : public CompiledVm<Allocator, softAes, secureJit> {
 	public:
 		void* operator new(size_t size) {
 			void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);
@@ -49,16 +49,20 @@ namespace randomx {
 		void setDataset(randomx_dataset* dataset) override { }
 		void run(void* seed) override;
 
-		using CompiledVm<Allocator, softAes>::mem;
-		using CompiledVm<Allocator, softAes>::compiler;
-		using CompiledVm<Allocator, softAes>::program;
-		using CompiledVm<Allocator, softAes>::config;
-		using CompiledVm<Allocator, softAes>::cachePtr;
-		using CompiledVm<Allocator, softAes>::datasetOffset;
+		using CompiledVm<Allocator, softAes, secureJit>::mem;
+		using CompiledVm<Allocator, softAes, secureJit>::compiler;
+		using CompiledVm<Allocator, softAes, secureJit>::program;
+		using CompiledVm<Allocator, softAes, secureJit>::config;
+		using CompiledVm<Allocator, softAes, secureJit>::cachePtr;
+		using CompiledVm<Allocator, softAes, secureJit>::datasetOffset;
 	};
 
-	using CompiledLightVmDefault = CompiledLightVm<AlignedAllocator<CacheLineSize>, true>;
-	using CompiledLightVmHardAes = CompiledLightVm<AlignedAllocator<CacheLineSize>, false>;
-	using CompiledLightVmLargePage = CompiledLightVm<LargePageAllocator, true>;
-	using CompiledLightVmLargePageHardAes = CompiledLightVm<LargePageAllocator, false>;
+	using CompiledLightVmDefault = CompiledLightVm<AlignedAllocator<CacheLineSize>, true, false>;
+	using CompiledLightVmHardAes = CompiledLightVm<AlignedAllocator<CacheLineSize>, false, false>;
+	using CompiledLightVmLargePage = CompiledLightVm<LargePageAllocator, true, false>;
+	using CompiledLightVmLargePageHardAes = CompiledLightVm<LargePageAllocator, false, false>;
+	using CompiledLightVmDefaultSecure = CompiledLightVm<AlignedAllocator<CacheLineSize>, true, true>;
+	using CompiledLightVmHardAesSecure = CompiledLightVm<AlignedAllocator<CacheLineSize>, false, true>;
+	using CompiledLightVmLargePageSecure = CompiledLightVm<LargePageAllocator, true, true>;
+	using CompiledLightVmLargePageHardAesSecure = CompiledLightVm<LargePageAllocator, false, true>;
 }