简体   繁体   中英

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

I am recently studying some JIT compiler. As far as I know, JIT is a technique to compile some scripting language code to native code on the fly (right before execution). 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. 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. Does it means the JIT has to bypass compiler, along with setting the executable flag?

Edit : Besides mprotect , if you use mmap , you can also specify a permission to the page to map:

   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 .

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

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. So any code you construct in your jit compiler will not be executable and trying to call a function you constructed there will fail. You have to make the memory executable first using mprotect .

For security reasons you should follow the W^X principle. 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. Never combine writable and executable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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