JIT-компиляция и DEP - PullRequest
       18

JIT-компиляция и DEP

1 голос
/ 20 февраля 2009

Я думал о том, чтобы попробовать свои силы в некотором Jit-сборнике (просто для обучения), и было бы неплохо, чтобы он работал кроссплатформенно, так как я запускаю все основные три дома (windows, os x, linux) , Имея это в виду, я хочу знать, есть ли какой-нибудь способ отказаться от использования функций окон виртуальной памяти для выделения памяти с разрешениями на выполнение. Было бы неплохо просто использовать malloc или new и указать процессор на такой блок.

Любые советы?

Ответы [ 2 ]

9 голосов
/ 08 февраля 2012

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 будет управлять разрешениями памяти детально.

0 голосов
/ 08 апреля 2009

Одной из возможностей является требование, чтобы установки Windows, на которых запущена ваша программа, были настроены для DEP AlwaysOff (плохая идея) или DEP OptOut (лучшая идея).

Это можно настроить (под WinXp SP2 + и Win2k3 SP1 + как минимум), изменив файл boot.ini, чтобы он имел значение:

/noexecute=OptOut

, а затем настройте индивидуальную программу для отказа, выбрав (под XP):

Start button
    Control Panel
        System
            Advanced tab
                Performance Settings button
                    Data Execution Prevention tab

Это должно позволить вам выполнять код из вашей программы, созданный на лету в malloc() блоках.

Имейте в виду, что это делает вашу программу более восприимчивой к атакам, которые DEP должен был предотвратить.

Похоже, это также возможно в Windows 2008 с помощью команды:

bcdedit.exe /set {current} nx OptOut

Но, если честно, если вы просто хотите минимизировать код, зависящий от платформы, это легко сделать, просто выделив код в одну функцию, что-то вроде:

void *MallocWithoutDep(size_t sz) {
    #if defined _IS_WINDOWS
        return VirtualMalloc(sz, OPT_DEP_OFF); // or whatever
    #elif defined IS_LINUX
        // Do linuxy thing
    #elif defined IS_MACOS
        // Do something almost certainly inexplicable
    #endif
}

Если вы поместите все зависимые от платформы функции в их собственные файлы, остальная часть вашего кода автоматически не будет зависеть от платформы.

...