Вопрос

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

Есть какие-нибудь советы?

Это было полезно?

Решение

Одна из возможностей состоит в том, чтобы сделать обязательным, чтобы установки 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
}

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

Другие советы

DEP просто отключает разрешение на выполнение для каждой некодовой страницы памяти.Код приложения загружается в память, которая имеет разрешение на выполнение;и есть много JITS, которые работают в Windows / Linux / MacOSX, даже когда DEP активен.Это связано с тем, что существует способ динамического выделения памяти с установленными необходимыми разрешениями.

Обычно обычный malloc не следует использовать, поскольку разрешения предоставляются для каждой страницы.Выравнивание выделенной памяти по страницам все еще возможно ценой некоторых накладных расходов.Если вы не будете использовать malloc, какое-нибудь пользовательское управление памятью (только для исполняемого кода).Пользовательское управление - это распространенный способ выполнения JIT.

Существует решение из проекта Chromium, которое использует JIT для виртуальной машины javascript V8 и которое является кроссплатформенным.Чтобы быть кроссплатформенным, необходимая функция реализована в нескольких файлах, и они выбираются во время компиляции.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top