2016-07-10 62 views
3

我目前正在用C/C++編寫一個小型VM。顯然我不能讓整個虛擬機崩潰,如果用戶解引用空指針,所以我必須檢查每個訪問越來越繁瑣,隨着虛擬機增長和更多的系統實現。signal.h是捕獲空指針的可靠方法嗎?

所以我有一個想法:寫SIGSEGV的信號處理程序,並讓OS做它的事,但不要關程序調用VM異常處理程序。

它似乎工作(與我非常簡單的測試用例),但我沒有發現任何保證null-derefs引發的sigsegv,也沒有調用操作系統生成的信號處理程序。

所以我的問題是: 我可以指望現代destkop操作系統上的signal.h(我真的不在乎它是否不符合linux/win以外的標準):這是一個寵物項目) 。是否有任何不平凡的東西,我應該知道的

謝謝(信號(...)或longjmp的(...)?晦澀的限制)!

這裏是僞實現:

/* ... */ 

jmp_buf env; 

/* ... */ 

void handler(int) { 
    longjmp(env, VM_NULLPTR); 
} 

/* ... */ 

if(setjmp(env)) { 
    return vm_throw("NullPtrException"); 
} 
switch(opcode) { 

    /* instructions */ 

    case INVOKE: 
     *stack_top = vm_call(stack_top->obj); // don't check anything in the case where stack_top or stack_top->obj is null handler() will be called an a "NullPtrException" will be thrown 
    break; 

    /* more instructions */ 

} 

/* ... */ 

注:我只需要檢查空值,垃圾(懸掛)的指針由GC處理,並且不應該發生。

+0

除了取消引用空指針外,代碼還可以解引用垃圾指針或任何其他類型的無效指針。這不一定會產生一個信號。因此,您無法驗證每個指針訪問權限。 –

+0

如果您將指針解引用操作傳遞給主機操作系統,那不是虛擬機。充其量,它是一個模擬包裝(儘管不告訴WINE它就是這樣!)。 –

+0

@Sam Varshavchik:這就是爲什麼最後一個音符在那裏:垃圾不應該發生:永遠。 –

回答

1

我能指望signal.h中對現代destkop操作系統

您可以在這個意義上指望它的頭部,並在該功能將適用於所有符合標準的系統。但是,究竟是什麼信號被拋出,什麼時候在不同的操作系統中不一致。

在Windows上,您可能需要使用Cygwin或類似環境中編譯程序來獲得系統,以提高分段故障。用visual studio編譯的程序使用「structured exceptions」來處理無效的存儲訪問。


是signal.h中一個可靠的辦法趕上空指針?

在某些情況下,即使在POSIX系統上,空指針取消引用也不會導致產生分段錯誤信號。

  • 一種情況可能是編譯器優化了操作,例如在解除引用空指針以調用不訪問任何數據成員的成員函數的情況下,這是典型情況。當沒有無效的存儲器訪問時,也沒有信號。當然,在這種情況下也不會出現崩潰。
  • 另一種情況可能是地址0實際上是有效的。 AIX上就是這種情況,你不關心。在Linux上也是這樣,你確實在乎,但不是默認情況下,你可能會選擇不關心這種情況。有關更多詳細信息,請參見this answer

再有就是你的信號處理函數的執行。 longjmp不是異步信號安全的,因此如果在執行另一個非安全操作時產生了信號,則被中斷的操作可能使程序處於不一致的狀態。有關詳細信息,請參閱John Zwinck的answerlibc documentation

+0

非常感謝你,先生。這就是我一直在尋找的! –

2

從信號處理程序調用longjmp()僅僅是安全的,如果信號處理程序不會從異步信號不安全的代碼調用。因此,舉例來說,如果您可能通過向指定的printf()系列函數傳遞錯誤的指針來接收SIGSEGV,則不能從信號處理函數中獲得longjmp()

+0

我讀過處理函數不應該返回(因爲它具有undef行爲)。這是否意味着我必須手動清理對printf的調用? C++ IO也是這種情況嗎? –

+0

這不僅僅是printf,它是這裏沒有明確列出的任何標準庫函數:http://man7.org/linux/man-pages/man7/signal.7.html。是的,它包含C++ I/O,它使用C函數實現,並且它不僅包含I/O,還包含另一個例子,不能使用'mktime()')。 –