DEP просто отключает разрешение на выполнение для каждой не кодовой страницы памяти. Код приложения загружается в память, которая имеет разрешение на выполнение; и есть много JIT, которые работают в Windows / Linux / MacOSX, даже когда DEP активен. Это связано с тем, что существует способ динамически распределять память с установленными необходимыми разрешениями.
Обычно обычный malloc не следует использовать, поскольку разрешения указаны для каждой страницы. Выравнивание неправильно распределенной памяти по страницам все еще возможно по цене некоторых накладных расходов. Если вы не будете использовать malloc, некоторые пользовательские управления памятью (только для исполняемого кода). Таможенное управление является распространенным способом выполнения JIT.
Существует решение от проекта Chromium, которое использует JIT для javascript V8 VM и которое является кроссплатформенным. Чтобы быть кроссплатформенным, необходимая функция реализована в нескольких файлах, и они выбираются во время компиляции.
Linux: (chromium src / v8 / src / platform-linux.cc) флаг равен PROT_EXEC для mmap ().
void* OS::Allocate(const size_t requested,
size_t* allocated,
bool is_executable) {
const size_t msize = RoundUp(requested, AllocateAlignment());
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
void* addr = OS::GetRandomMmapAddr();
void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mbase == MAP_FAILED) {
/** handle error */
return NULL;
}
*allocated = msize;
UpdateAllocatedSpaceLimits(mbase, msize);
return mbase;
}
Win32 (src / v8 / src / platform-win32.cc): флаг PAGE_EXECUTE_READWRITE для VirtualAlloc
void* OS::Allocate(const size_t requested,
size_t* allocated,
bool is_executable) {
// The address range used to randomize RWX allocations in OS::Allocate
// Try not to map pages into the default range that windows loads DLLs
// Use a multiple of 64k to prevent committing unused memory.
// Note: This does not guarantee RWX regions will be within the
// range kAllocationRandomAddressMin to kAllocationRandomAddressMax
#ifdef V8_HOST_ARCH_64_BIT
static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000;
static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000;
#else
static const intptr_t kAllocationRandomAddressMin = 0x04000000;
static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000;
#endif
// VirtualAlloc rounds allocated size to page size automatically.
size_t msize = RoundUp(requested, static_cast<int>(GetPageSize()));
intptr_t address = 0;
// Windows XP SP2 allows Data Excution Prevention (DEP).
int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
// For exectutable pages try and randomize the allocation address
if (prot == PAGE_EXECUTE_READWRITE &&
msize >= static_cast<size_t>(Page::kPageSize)) {
address = (V8::RandomPrivate(Isolate::Current()) << kPageSizeBits)
| kAllocationRandomAddressMin;
address &= kAllocationRandomAddressMax;
}
LPVOID mbase = VirtualAlloc(reinterpret_cast<void *>(address),
msize,
MEM_COMMIT | MEM_RESERVE,
prot);
if (mbase == NULL && address != 0)
mbase = VirtualAlloc(NULL, msize, MEM_COMMIT | MEM_RESERVE, prot);
if (mbase == NULL) {
LOG(ISOLATE, StringEvent("OS::Allocate", "VirtualAlloc failed"));
return NULL;
}
ASSERT(IsAligned(reinterpret_cast<size_t>(mbase), OS::AllocateAlignment()));
*allocated = msize;
UpdateAllocatedSpaceLimits(mbase, static_cast<int>(msize));
return mbase;
}
MacOS (src / v8 / src / platform-macos.cc): флаг равен PROT_EXEC для mmap, так же, как Linux или другой posix.
void* OS::Allocate(const size_t requested,
size_t* allocated,
bool is_executable) {
const size_t msize = RoundUp(requested, getpagesize());
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
void* mbase = mmap(OS::GetRandomMmapAddr(),
msize,
prot,
MAP_PRIVATE | MAP_ANON,
kMmapFd,
kMmapFdOffset);
if (mbase == MAP_FAILED) {
LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed"));
return NULL;
}
*allocated = msize;
UpdateAllocatedSpaceLimits(mbase, msize);
return mbase;
}
И я также хочу отметить, что bcdedit.exe
-подобный способ должен использоваться только для очень старых программ, который создает новый исполняемый код в памяти, но не устанавливает свойство Exec на этой странице. Для более новых программ, таких как firefox или Chrome / Chromium, или для любого современного JIT, DEP должен быть активным, а JIT будет управлять разрешениями памяти детально.