3

我正在考虑尝试一些 jit 编译(只是为了学习),让它跨平台工作会很好,因为我在家里运行所有主要的三个(windows、os x、linux)。考虑到这一点,我想知道是否有任何方法可以摆脱使用虚拟内存窗口函数来分配具有执行权限的内存。最好只使用 malloc 或 new 并将处理器指向这样的块。

有小费吗?

4

2 回答 2

11

DEP 只是关闭了每个内存非代码页的执行权限。应用程序代码加载到有执行权限的内存中;并且有很多 JIT 可以在 Windows/Linux/MacOSX 中运行,即使 DEP 处于活动状态也是如此。这是因为有一种方法可以使用所需的权限集动态分配内存。

通常不应使用普通的 malloc,因为权限是每页的。以一些开销为代价,仍然可以将分配的内存与页面对齐。如果你不会使用malloc,一些自定义的内存管理(只针对可执行代码)。自定义管理是一种常见的 JIT 方式。

Chromium 项目有一个解决方案,它为 javascript V8 VM 使用 JIT 并且是跨平台的。为了跨平台,所需的功能在几个文件中实现,并在编译时选择它们。

Linux: (chromium src/v8/src/platform-linux.cc) 标志是 mmap() 的 PROT_EXEC。

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): flag 是 VirtualAlloc 的 PAGE_EXECUTE_READWRITE

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):flag 是 mmap 的 PROT_EXEC,就像 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 将以细粒度的方式管理内存权限。

于 2012-02-08T02:22:13.063 回答
0

一种可能性是要求运行您的程序的 Windows 安装配置为 DEP AlwaysOff(坏主意)或 DEP OptOut(更好的主意)。

这可以通过将 boot.ini 文件更改为具有以下设置来配置(至少在 WinXp SP2+ 和 Win2k3 SP1+ 下):

/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
}

如果您将所有与平台相关的函数放在它们自己的文件中,那么您的其余代码将自动与平台无关。

于 2009-04-08T02:53:33.847 回答