简体   繁体   English

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

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

I am recently studying some JIT compiler.我最近在研究一些 JIT 编译器。 As far as I know, JIT is a technique to compile some scripting language code to native code on the fly (right before execution).据我所知,JIT 是一种将一些脚本语言代码即时(在执行之前)编译为本机代码的技术。 As I imagined the internal of such a compiler, I figured out there must be a segment of dynamic-allocated buffer where the generated native code resides.当我想象这样一个编译器的内部时,我发现生成的本机代码所在的地方一定有一段动态分配的缓冲区。 But then we need a way to start running the code from within the buffer, which is holding data.但是我们需要一种方法来从保存数据的缓冲区中开始运行代码。 I mean, you can't just put some code into a char[] and then jump into execution since the security implication, for which the OS must prevent you from doing so.我的意思是,您不能只是将一些代码放入char[] ,然后因为安全隐患而跳入执行,为此操作系统必须阻止您这样做。 There must be some way to mark the buffer as executable.必须有某种方式将缓冲区标记为可执行文件。 Consider the following naive approach:考虑以下幼稚的方法:

#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)();

}

On Ubuntu the code will run but will exit with status code 26. Given the type unsafe nature of C, the code can compile, but for C++, the compiler simply stops you.在 Ubuntu 上,代码将运行,但会以状态代码 26 退出。鉴于 C 的类型不安全性质,代码可以编译,但对于 C++,编译器只会阻止您。 Does it means the JIT has to bypass compiler, along with setting the executable flag?这是否意味着 JIT 必须绕过编译器,同时设置可执行标志?

Edit : Besides mprotect , if you use mmap , you can also specify a permission to the page to map:编辑:除了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.

As such the page will have executable permission.因此,该页面将具有可执行权限。

If you want to make a region in the heap executable you can use mprotect .如果要在堆可执行文件中创建一个区域,可以使用mprotect

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

You can also OR the flags with PROT_READ/PROT_WRITE您还可以使用 PROT_READ/PROT_WRITE 或标志

In your code you are taking the address of an existing function.在您的代码中,您正在获取现有函数的地址。 This will naturally point into a region of memory that is already executable.这自然会指向一个已经可以执行的内存区域。 But that same region will not be writable on any modern system.但是同一个区域在任何现代系统上都是不可写的。

On the other hand if you malloc() some memory it will be writable but not executable.另一方面,如果您 malloc() 一些内存,它将是可写但不可执行的。 So any code you construct in your jit compiler will not be executable and trying to call a function you constructed there will fail.因此,您在 jit 编译器中构建的任何代码都将无法执行,并且尝试调用您在那里构建的函数将失败。 You have to make the memory executable first using mprotect .您必须首先使用mprotect使内存可执行。

For security reasons you should follow the W^X principle.出于安全原因,您应该遵循W^X原则。 Meaning that any page can only be writable or executable but never both.这意味着任何页面都只能是可写或可执行的,但不能两者兼而有之。 When you use mprotect to make the code executable also make it non-writable and vice versa.当您使用 mprotect 使代码可执行时,也会使其不可写,反之亦然。 Never combine writable and executable.永远不要将可写和可执行结合起来。

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

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