繁体   English   中英

如何在 C 中将一段内存标记为可执行?

[英]How can you mark a segment of memory as executable in C?

我最近在研究一些 JIT 编译器。 据我所知,JIT 是一种将一些脚本语言代码即时(在执行之前)编译为本机代码的技术。 当我想象这样一个编译器的内部时,我发现生成的本机代码所在的地方一定有一段动态分配的缓冲区。 但是我们需要一种方法来从保存数据的缓冲区中开始运行代码。 我的意思是,您不能只是将一些代码放入char[] ,然后因为安全隐患而跳入执行,为此操作系统必须阻止您这样做。 必须有某种方式将缓冲区标记为可执行文件。 考虑以下幼稚的方法:

#include <stdlib.h>

void *jit_some_native_code(void) {
  void *code_segment = malloc(1024);
  /*
   * bla bla bla...
   * Generate code into this code_segment.
   */

  return code_segment;
}

int main(void) {
  void *code = jit_some_native_code();
  /*
   * How can I start executing instruction in code?
   */

  typedef void (*func_ptr_t)(void);

  /*
   * This won't work. OS bans you doing so.
   */
  ((func_ptr_t)code)();

}

在 Ubuntu 上,代码将运行,但会以状态代码 26 退出。鉴于 C 的类型不安全性质,代码可以编译,但对于 C++,编译器只会阻止您。 这是否意味着 JIT 必须绕过编译器,同时设置可执行标志?

编辑:除了mprotect ,如果您使用mmap ,您还可以指定要映射的页面的权限:

   PROT_EXEC  Pages may be executed.
   PROT_READ  Pages may be read.
   PROT_WRITE Pages may be written.
   PROT_NONE  Pages may not be accessed.

因此,该页面将具有可执行权限。

如果要在堆可执行文件中创建一个区域,可以使用mprotect

int main() {
  typedef void (*func_t)(void);
  void *code = &some_jit_func;
  int pagesize = getpagesize();
  mprotect(code, pagesize,PROT_EXEC);
  ((func_t)code)();
}

您还可以使用 PROT_READ/PROT_WRITE 或标志

在您的代码中,您正在获取现有函数的地址。 这自然会指向一个已经可以执行的内存区域。 但是同一个区域在任何现代系统上都是不可写的。

另一方面,如果您 malloc() 一些内存,它将是可写但不可执行的。 因此,您在 jit 编译器中构建的任何代码都将无法执行,并且尝试调用您在那里构建的函数将失败。 您必须首先使用mprotect使内存可执行。

出于安全原因,您应该遵循W^X原则。 这意味着任何页面都只能是可写或可执行的,但不能两者兼而有之。 当您使用 mprotect 使代码可执行时,也会使其不可写,反之亦然。 永远不要将可写和可执行结合起来。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM