检测到堆栈粉碎
我敢打賭,每一個Java開發人員在他們的職業生涯初期都首次在Java代碼中遇到本機方法時都會感到驚訝。
我還可以肯定,多年來隨著了解JVM如何通過JNI處理對本機實現的調用而使驚喜消失了。
這篇文章是關于本機方法的最新經驗。 更詳細地講,使用本機方法如何導致JVM靜默崩潰,而日志文件中沒有任何合理的跟蹤。 為了向您介紹經驗,我創建了一個小測試用例。
它由一個簡單的Java類組成 ,可計算文件的校驗和。 為了實現Awesome Performance(TM),我決定使用本機實現來實現校驗和計算部分。 該代碼簡單明了,因此正在運行。 您只需要克隆存儲庫并啟動它,類似于以下示例:
$ ./gradlew jarWithNatives $ java -jar build/libs/checksum.jar 123.txt Exiting native method with checksum: 1804289383 Got checksum from native method: 1804289383該代碼似乎按預期工作。 當您發現自己盯著輸出時使用的輸入文件名略有不同(較長)時,會發現不太簡單的部分:
$ java -jar build/libs/checksum.jar 123456789012.txt Exiting native method with checksum: 1804289383 *** stack smashing detected ***: java terminated因此,本機方法可以很好地完成其執行,但是控件沒有返回給Java。 而是,JVM崩潰而沒有崩潰日志。 您應該意識到以下事實:我僅在Linux和Mac OS X上測試了示例,并且在Windows上的行為可能有所不同。
根本的問題不是太復雜,并且可能在C代碼中立即可見:
char dst_filename[MAX_FILE_NAME_LENGTH]; // cut for brevity sprintf(dst_filename, "%s.digested", src_filename);從上面可以明顯看出,緩沖區只能容納固定數量的字符。 輸入較長時,剩余字符將被寫到末尾。 實際上,這將導致堆棧崩潰,并為潛在的黑客攻擊或使應用程序處于不可預測的狀態打開大門。
對于C開發人員,底層的堆棧保護器機制是眾所周知的,但對于Java開發人員,可能需要更多說明。 除了使用更安全的snprintf占用緩沖區長度并且不會超出該長度之外,您還可以要求編譯器向堆棧中添加堆棧保護器或內存清理。 可用的安全網因編譯器而異,甚至在同一編譯器的不同版本之間也存在很大差異,但這是一個示例:
gcc -fstack-protector CheckSumCalculator.c -o CheckSumCalculator.so使用適當的堆棧保護器編譯代碼后,運行時庫或OS的實現在某些情況下可能會檢測到這種情況,并終止程序以防止意外行為。
如下面的示例所示,在未進行清理的情況下編譯代碼時,
gcc -fno-stack-protector CheckSumCalculator.c -o CheckSumCalculator.so運行此類代碼的結果可能變得完全不可預測。 在某些情況下,代碼看起來似乎可以完成,但是在某些情況下,您可能會遇到緩沖區溢出。 盡管在此示例中,使用snprintf并啟用清除功能絕對有幫助,但該錯誤可能比其細微得多,并且不會自動捕獲。
回到所謂的安全Java世界,這樣的緩沖區溢出可能會破壞內部JVM結構,甚至使提供字符串的任何人都可以執行任意代碼。 因此,JVM將保護值添加到內存中,如果在本機方法完成后對這些值進行了修改,則立即終止應用程序。 為什么在沒有更詳細的錯誤日志的情況下進行墮胎是一個不同的問題,不在本文的討論范圍之內。
我希望這篇文章在面對突然的JVM死亡甚至沒有崩潰日志時能為某人節省一整夜的時間。 在所有平臺上甚至都沒有出現標準錯誤流中的“ stack smashed”消息,并且可能需要花費大量時間才能確定發生了什么情況,尤其是在運行沒有源代碼的第三方本機庫的情況下。
翻譯自: https://www.javacodegeeks.com/2015/09/stack-smashing-detected.html
總結
- 上一篇: 电脑cpu个人能拆出来吗(电脑cpu如何
- 下一篇: guice注入带参构造器_带有Guice