Skip to content

Commit 67010ab

Browse files
authored
Optional W^X policy for JIT pages (#112)
1 parent 971f10c commit 67010ab

12 files changed

+195
-55
lines changed

src/dataset.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,10 @@ namespace randomx {
140140

141141
void initCacheCompile(randomx_cache* cache, const void* key, size_t keySize) {
142142
initCache(cache, key, keySize);
143+
cache->jit->enableWriting();
143144
cache->jit->generateSuperscalarHash(cache->programs, cache->reciprocalCache);
144145
cache->jit->generateDatasetInitCode();
146+
cache->jit->enableExecution();
145147
}
146148

147149
constexpr uint64_t superscalarMul0 = 6364136223846793005ULL;

src/jit_compiler_x86.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ namespace randomx {
218218
}
219219

220220
JitCompilerX86::JitCompilerX86() {
221-
code = (uint8_t*)allocExecutableMemory(CodeSize);
221+
code = (uint8_t*)allocMemoryPages(CodeSize);
222222
memcpy(code, codePrologue, prologueSize);
223223
memcpy(code + epilogueOffset, codeEpilogue, epilogueSize);
224224
}
@@ -227,6 +227,18 @@ namespace randomx {
227227
freePagedMemory(code, CodeSize);
228228
}
229229

230+
void JitCompilerX86::enableAll() {
231+
setPagesRWX(code, CodeSize);
232+
}
233+
234+
void JitCompilerX86::enableWriting() {
235+
setPagesRW(code, CodeSize);
236+
}
237+
238+
void JitCompilerX86::enableExecution() {
239+
setPagesRX(code, CodeSize);
240+
}
241+
230242
void JitCompilerX86::generateProgram(Program& prog, ProgramConfiguration& pcfg) {
231243
generateProgramPrologue(prog, pcfg);
232244
memcpy(code + codePos, codeReadDataset, readDatasetSize);

src/jit_compiler_x86.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ namespace randomx {
6262
return code;
6363
}
6464
size_t getCodeSize();
65+
void enableWriting();
66+
void enableExecution();
67+
void enableAll();
6568
private:
6669
static InstructionGeneratorX86 engine[256];
6770
std::vector<int32_t> instructionOffsets;

src/randomx.cpp

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,21 @@ extern "C" {
175175
break;
176176

177177
case RANDOMX_FLAG_JIT:
178-
vm = new randomx::CompiledLightVmDefault();
178+
if (flags & RANDOMX_FLAG_SECURE) {
179+
vm = new randomx::CompiledLightVmDefaultSecure();
180+
}
181+
else {
182+
vm = new randomx::CompiledLightVmDefault();
183+
}
179184
break;
180185

181186
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT:
182-
vm = new randomx::CompiledVmDefault();
187+
if (flags & RANDOMX_FLAG_SECURE) {
188+
vm = new randomx::CompiledVmDefaultSecure();
189+
}
190+
else {
191+
vm = new randomx::CompiledVmDefault();
192+
}
183193
break;
184194

185195
case RANDOMX_FLAG_HARD_AES:
@@ -191,11 +201,21 @@ extern "C" {
191201
break;
192202

193203
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES:
194-
vm = new randomx::CompiledLightVmHardAes();
204+
if (flags & RANDOMX_FLAG_SECURE) {
205+
vm = new randomx::CompiledLightVmHardAesSecure();
206+
}
207+
else {
208+
vm = new randomx::CompiledLightVmHardAes();
209+
}
195210
break;
196211

197212
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES:
198-
vm = new randomx::CompiledVmHardAes();
213+
if (flags & RANDOMX_FLAG_SECURE) {
214+
vm = new randomx::CompiledVmHardAesSecure();
215+
}
216+
else {
217+
vm = new randomx::CompiledVmHardAes();
218+
}
199219
break;
200220

201221
case RANDOMX_FLAG_LARGE_PAGES:
@@ -207,11 +227,21 @@ extern "C" {
207227
break;
208228

209229
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES:
210-
vm = new randomx::CompiledLightVmLargePage();
230+
if (flags & RANDOMX_FLAG_SECURE) {
231+
vm = new randomx::CompiledLightVmLargePageSecure();
232+
}
233+
else {
234+
vm = new randomx::CompiledLightVmLargePage();
235+
}
211236
break;
212237

213238
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES:
214-
vm = new randomx::CompiledVmLargePage();
239+
if (flags & RANDOMX_FLAG_SECURE) {
240+
vm = new randomx::CompiledVmLargePageSecure();
241+
}
242+
else {
243+
vm = new randomx::CompiledVmLargePage();
244+
}
215245
break;
216246

217247
case RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES:
@@ -223,11 +253,21 @@ extern "C" {
223253
break;
224254

225255
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES:
226-
vm = new randomx::CompiledLightVmLargePageHardAes();
256+
if (flags & RANDOMX_FLAG_SECURE) {
257+
vm = new randomx::CompiledLightVmLargePageHardAesSecure();
258+
}
259+
else {
260+
vm = new randomx::CompiledLightVmLargePageHardAes();
261+
}
227262
break;
228263

229264
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES:
230-
vm = new randomx::CompiledVmLargePageHardAes();
265+
if (flags & RANDOMX_FLAG_SECURE) {
266+
vm = new randomx::CompiledVmLargePageHardAesSecure();
267+
}
268+
else {
269+
vm = new randomx::CompiledVmLargePageHardAes();
270+
}
231271
break;
232272

233273
default:

src/randomx.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef enum {
4444
RANDOMX_FLAG_HARD_AES = 2,
4545
RANDOMX_FLAG_FULL_MEM = 4,
4646
RANDOMX_FLAG_JIT = 8,
47+
RANDOMX_FLAG_SECURE = 16
4748
} randomx_flags;
4849

4950
typedef struct randomx_dataset randomx_dataset;
@@ -135,12 +136,14 @@ RANDOMX_EXPORT void randomx_release_dataset(randomx_dataset *dataset);
135136
/**
136137
* Creates and initializes a RandomX virtual machine.
137138
*
138-
* @param flags is any combination of these 4 flags (each flag can be set or not set):
139+
* @param flags is any combination of these 5 flags (each flag can be set or not set):
139140
* RANDOMX_FLAG_LARGE_PAGES - allocate scratchpad memory in large pages
140141
* RANDOMX_FLAG_HARD_AES - virtual machine will use hardware accelerated AES
141142
* RANDOMX_FLAG_FULL_MEM - virtual machine will use the full dataset
142143
* RANDOMX_FLAG_JIT - virtual machine will use a JIT compiler
143-
* The numeric values of the flags are ordered so that a higher value will provide
144+
* RANDOMX_FLAG_SECURE - when combined with RANDOMX_FLAG_JIT, the JIT pages are never
145+
* writable and executable at the same time (W^X policy)
146+
* The numeric values of the first 4 flags are ordered so that a higher value will provide
144147
* faster hash calculation and a lower numeric value will provide higher portability.
145148
* Using RANDOMX_FLAG_DEFAULT (all flags not set) works on all platforms, but is the slowest.
146149
* @param cache is a pointer to an initialized randomx_cache structure. Can be

src/tests/benchmark.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ void printUsage(const char* executable) {
8282
std::cout << " --mine mining mode: 2080 MiB" << std::endl;
8383
std::cout << " --verify verification mode: 256 MiB" << std::endl;
8484
std::cout << " --jit x86-64 JIT compiled mode (default: interpreter)" << std::endl;
85+
std::cout << " --secure W^X policy for JIT pages (default: off)" << std::endl;
8586
std::cout << " --largePages use large pages" << std::endl;
8687
std::cout << " --softAes use software AES (default: x86 AES-NI)" << std::endl;
8788
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
126127
}
127128

128129
int main(int argc, char** argv) {
129-
bool softAes, miningMode, verificationMode, help, largePages, jit;
130+
bool softAes, miningMode, verificationMode, help, largePages, jit, secure;
130131
int noncesCount, threadCount, initThreadCount;
131132
uint64_t threadAffinity;
132133
int32_t seedValue;
@@ -143,6 +144,7 @@ int main(int argc, char** argv) {
143144
readOption("--largePages", argc, argv, largePages);
144145
readOption("--jit", argc, argv, jit);
145146
readOption("--help", argc, argv, help);
147+
readOption("--secure", argc, argv, secure);
146148

147149
store32(&seed, seedValue);
148150

@@ -171,7 +173,12 @@ int main(int argc, char** argv) {
171173

172174
if (jit) {
173175
flags = (randomx_flags)(flags | RANDOMX_FLAG_JIT);
174-
std::cout << " - JIT compiled mode" << std::endl;
176+
std::cout << " - JIT compiled mode ";
177+
if (secure) {
178+
flags = (randomx_flags)(flags | RANDOMX_FLAG_SECURE);
179+
std::cout << "(secure)";
180+
}
181+
std::cout << std::endl;
175182
}
176183
else {
177184
std::cout << " - interpreted mode" << std::endl;

src/virtual_memory.cpp

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4141
#ifndef MAP_ANONYMOUS
4242
#define MAP_ANONYMOUS MAP_ANON
4343
#endif
44+
#define PAGE_READONLY PROT_READ
45+
#define PAGE_READWRITE (PROT_READ | PROT_WRITE)
46+
#define PAGE_EXECUTE_READ (PROT_READ | PROT_EXEC)
47+
#define PAGE_EXECUTE_READWRITE (PROT_READ | PROT_WRITE | PROT_EXEC)
4448
#endif
4549

4650
#if defined(_WIN32) || defined(__CYGWIN__)
@@ -83,20 +87,44 @@ void setPrivilege(const char* pszPrivilege, BOOL bEnable) {
8387
}
8488
#endif
8589

86-
void* allocExecutableMemory(std::size_t bytes) {
90+
void* allocMemoryPages(std::size_t bytes) {
8791
void* mem;
8892
#if defined(_WIN32) || defined(__CYGWIN__)
89-
mem = VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
93+
mem = VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_READWRITE);
9094
if (mem == nullptr)
91-
throw std::runtime_error(getErrorMessage("allocExecutableMemory - VirtualAlloc"));
95+
throw std::runtime_error(getErrorMessage("allocMemoryPages - VirtualAlloc"));
9296
#else
93-
mem = mmap(nullptr, bytes, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
97+
mem = mmap(nullptr, bytes, PAGE_READWRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
9498
if (mem == MAP_FAILED)
95-
throw std::runtime_error("allocExecutableMemory - mmap failed");
99+
throw std::runtime_error("allocMemoryPages - mmap failed");
96100
#endif
97101
return mem;
98102
}
99103

104+
static inline void pageProtect(void* ptr, std::size_t bytes, int rules) {
105+
#if defined(_WIN32) || defined(__CYGWIN__)
106+
DWORD oldp;
107+
if (!VirtualProtect(ptr, bytes, (DWORD)rules, &oldp)) {
108+
throw std::runtime_error(getErrorMessage("VirtualProtect"));
109+
}
110+
#else
111+
if (-1 == mprotect(ptr, bytes, rules))
112+
throw std::runtime_error("mprotect failed");
113+
#endif
114+
}
115+
116+
void setPagesRW(void* ptr, std::size_t bytes) {
117+
pageProtect(ptr, bytes, PAGE_READWRITE);
118+
}
119+
120+
void setPagesRX(void* ptr, std::size_t bytes) {
121+
pageProtect(ptr, bytes, PAGE_EXECUTE_READ);
122+
}
123+
124+
void setPagesRWX(void* ptr, std::size_t bytes) {
125+
pageProtect(ptr, bytes, PAGE_EXECUTE_READWRITE);
126+
}
127+
100128
void* allocLargePagesMemory(std::size_t bytes) {
101129
void* mem;
102130
#if defined(_WIN32) || defined(__CYGWIN__)

src/virtual_memory.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ constexpr std::size_t alignSize(std::size_t pos, std::size_t align) {
3434
return ((pos - 1) / align + 1) * align;
3535
}
3636

37-
void* allocExecutableMemory(std::size_t);
37+
void* allocMemoryPages(std::size_t);
38+
void setPagesRW(void*, std::size_t);
39+
void setPagesRX(void*, std::size_t);
40+
void setPagesRWX(void*, std::size_t);
3841
void* allocLargePagesMemory(std::size_t);
3942
void freePagedMemory(void*, std::size_t);

src/vm_compiled.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,44 @@ namespace randomx {
3434
static_assert(sizeof(MemoryRegisters) == 2 * sizeof(addr_t) + sizeof(uintptr_t), "Invalid alignment of struct randomx::MemoryRegisters");
3535
static_assert(sizeof(RegisterFile) == 256, "Invalid alignment of struct randomx::RegisterFile");
3636

37-
template<class Allocator, bool softAes>
38-
void CompiledVm<Allocator, softAes>::setDataset(randomx_dataset* dataset) {
37+
template<class Allocator, bool softAes, bool secureJit>
38+
CompiledVm<Allocator, softAes, secureJit>::CompiledVm() {
39+
if (!secureJit) {
40+
compiler.enableAll(); //make JIT buffer both writable and executable
41+
}
42+
}
43+
44+
template<class Allocator, bool softAes, bool secureJit>
45+
void CompiledVm<Allocator, softAes, secureJit>::setDataset(randomx_dataset* dataset) {
3946
datasetPtr = dataset;
4047
}
4148

42-
template<class Allocator, bool softAes>
43-
void CompiledVm<Allocator, softAes>::run(void* seed) {
49+
template<class Allocator, bool softAes, bool secureJit>
50+
void CompiledVm<Allocator, softAes, secureJit>::run(void* seed) {
4451
VmBase<Allocator, softAes>::generateProgram(seed);
4552
randomx_vm::initialize();
53+
if (secureJit) {
54+
compiler.enableWriting();
55+
}
4656
compiler.generateProgram(program, config);
57+
if (secureJit) {
58+
compiler.enableExecution();
59+
}
4760
mem.memory = datasetPtr->memory + datasetOffset;
4861
execute();
4962
}
5063

51-
template<class Allocator, bool softAes>
52-
void CompiledVm<Allocator, softAes>::execute() {
64+
template<class Allocator, bool softAes, bool secureJit>
65+
void CompiledVm<Allocator, softAes, secureJit>::execute() {
5366
compiler.getProgramFunc()(reg, mem, scratchpad, RANDOMX_PROGRAM_ITERATIONS);
5467
}
5568

56-
template class CompiledVm<AlignedAllocator<CacheLineSize>, false>;
57-
template class CompiledVm<AlignedAllocator<CacheLineSize>, true>;
58-
template class CompiledVm<LargePageAllocator, false>;
59-
template class CompiledVm<LargePageAllocator, true>;
69+
template class CompiledVm<AlignedAllocator<CacheLineSize>, false, false>;
70+
template class CompiledVm<AlignedAllocator<CacheLineSize>, true, false>;
71+
template class CompiledVm<LargePageAllocator, false, false>;
72+
template class CompiledVm<LargePageAllocator, true, false>;
73+
template class CompiledVm<AlignedAllocator<CacheLineSize>, false, true>;
74+
template class CompiledVm<AlignedAllocator<CacheLineSize>, true, true>;
75+
template class CompiledVm<LargePageAllocator, false, true>;
76+
template class CompiledVm<LargePageAllocator, true, true>;
6077
}

src/vm_compiled.hpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3737

3838
namespace randomx {
3939

40-
template<class Allocator, bool softAes>
40+
template<class Allocator, bool softAes, bool secureJit>
4141
class CompiledVm : public VmBase<Allocator, softAes> {
4242
public:
4343
void* operator new(size_t size) {
@@ -49,6 +49,7 @@ namespace randomx {
4949
void operator delete(void* ptr) {
5050
AlignedAllocator<CacheLineSize>::freeMemory(ptr, sizeof(CompiledVm));
5151
}
52+
CompiledVm();
5253
void setDataset(randomx_dataset* dataset) override;
5354
void run(void* seed) override;
5455

@@ -65,8 +66,12 @@ namespace randomx {
6566
JitCompiler compiler;
6667
};
6768

68-
using CompiledVmDefault = CompiledVm<AlignedAllocator<CacheLineSize>, true>;
69-
using CompiledVmHardAes = CompiledVm<AlignedAllocator<CacheLineSize>, false>;
70-
using CompiledVmLargePage = CompiledVm<LargePageAllocator, true>;
71-
using CompiledVmLargePageHardAes = CompiledVm<LargePageAllocator, false>;
69+
using CompiledVmDefault = CompiledVm<AlignedAllocator<CacheLineSize>, true, false>;
70+
using CompiledVmHardAes = CompiledVm<AlignedAllocator<CacheLineSize>, false, false>;
71+
using CompiledVmLargePage = CompiledVm<LargePageAllocator, true, false>;
72+
using CompiledVmLargePageHardAes = CompiledVm<LargePageAllocator, false, false>;
73+
using CompiledVmDefaultSecure = CompiledVm<AlignedAllocator<CacheLineSize>, true, true>;
74+
using CompiledVmHardAesSecure = CompiledVm<AlignedAllocator<CacheLineSize>, false, true>;
75+
using CompiledVmLargePageSecure = CompiledVm<LargePageAllocator, true, true>;
76+
using CompiledVmLargePageHardAesSecure = CompiledVm<LargePageAllocator, false, true>;
7277
}

0 commit comments

Comments
 (0)