簡體   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