java安全沙箱(二)之.class文件檢驗器

java是一種類型安全的語言,它有四類稱爲安全沙箱機制的安全機制來保證語言的安全性,這四類安全沙箱分別是:
java

本篇博客主要介紹「.class文件檢驗器」的基本原理;如需瞭解其它幾類安全機制能夠經過上面的博客連接進入查看性能

簡介

jvm的.class文件檢驗器用於檢查.class文件是否擁有合法的內存結構,這種檢查是有必要的,由於java的.class文件可能來自本機,也可能來自網絡,多是你本身編譯的文件,也多是別人篡改過的文件。而對於jvm來講,一個.class文件就是一個字節序列,它不會過問字節序列的來源,只會校驗字節序列的結構是否正確。ui

.class文件檢驗器保證安全的措施就是檢驗.class文件字節碼的健壯性,好比某個.class文件是被惡意篡改過的,這個.class文件中包含一個方法,該方法有一條goto指令,直接跳到方法外部去執行未知的代碼,若是執行該方法,極可能會致使jvm崩潰。因此,由.class文件檢驗器檢查字節碼的健壯性是頗有必要的。spa

雖然.class文件檢驗器檢查字節碼能保證程序的健壯性,而後這是須要犧牲一些性能的;爲了將這種影響降到最低,.class文件檢驗器會在字節碼執行以前完成大部分的檢驗工做,也就是說,.class文件檢驗器會在字節碼執行前而不是執行中進行檢查,並且這種檢查只會進行一次。好比每次遇到一條跳轉指令.class文件檢驗器都會確認該跳轉指令跳轉到了另一條合法指令,而該指令是是在同一方法的字節流中的。.net

.class文件檢驗器會進行四次獨立的掃描來保證字節碼的合法性。第一趟掃描在類裝載時進行,此次會檢查.class文件的內部結構,以保證它能被安全的編譯;第二和第三掃描是在鏈接時進行的,這時會檢查.class文件的數據定義是否聽從了java語言的語義規範,還會檢查字節碼的完整性;第四掃描是在解析符號引用時進行的,此次會檢查.class文件所引用的字段、方法和類是否存在。orm

第一趟掃描

在.class文件被裝載時,會進行第一次掃描,這時.class文件檢驗器會檢查每一條字節序列,看它是否符合java class文件的基本結構。首先,檢驗器會檢查.class文件是否以魔數0XCAFEBABY開頭,這個魔數的做用是爲了區分一些明顯錯誤的或被破壞的.class文件。而後,檢驗器會檢查.class文件的主版本號和次版本號,這個版本號必須在jvm所支持的範圍以內,好比我用java8編譯編譯的.class文件放在java6的jvm內執行會拋出java.lang.UnsupportedClassVersionError,由於java6不支持java8的一些新特性好比lambda表達式,因此不能去執行java8的編譯器編譯的字節碼;而後java通常都是向後兼容的,好比java8的jvm是能執行java6編譯的.class文件的。第一趟掃描主要是檢查.class文件是否聽從了java class文件的固定格式,這樣才能字節碼編譯成方法區內的內部數據結構。

第二趟掃描

第二趟掃描會進行數據類型的語義檢查,它不會再去檢查.class文件的二進制數據了,而是會去檢查方法區中定義的數據結構。類中定義的字段、方法和方法描述符等在.class文件中都會存儲爲一個字符串,檢驗器會檢查這些字符串是否符合java規範;檢驗器還會檢查類自己是否符合java語言規範,好比java語言規定除了Object類外全部類都必須有一個父類;檢查器還會檢查被final修飾的類(好比String.class)是否被繼承,一樣也會檢查被final修飾的方法是否被覆蓋,若是出現錯誤會拋出java.lang.VerifyError。檢驗器還會檢查常量池裏定義的常量是否合法,它會檢查常量的索引會指向正確的常量池條目。

第三趟掃描

第三趟掃描會進行字節碼驗證,字節碼檢查會確認字節碼的操做碼的正確性,也會確保操做數棧包含正確的數值和類型,還會檢查類的方法被調用時會傳入正確的參數和參數類型。檢驗器在第三趟掃描會進行大量的操做,好比會檢驗全部的操做碼都有一個合法的操做數等,在這趟掃描事後,它須要保證.class文件的字節碼流能夠被jvm安全地執行。而後檢驗器並不能檢查出全部的安全問題,好比「停機問題」它就不能檢查出來。停機問題是一個著名的計算機領域問題,即不能寫出一個程序,用它來判斷做爲其輸入的某個程序在執行時是否會停機。

第四趟掃描

第四趟掃描是在符號的動態鏈接時進行檢查,檢驗器會檢查符號引用是否合法;由於此次掃描須要檢查該class類所引用的類,因此這是可能須要裝載新的類;然而爲了節約內存並保證程序正確性,jvm會使用延遲加載的策略來裝載類,即直到類被程序真正使用時纔會去裝載。若是一個jvm實現爲了加快裝載速度預先裝載了類,它仍是會表現爲延遲裝載。好比jvm在預裝載某個類時發現這個類不存在,它並不會立刻拋出NoClassDefFoundError,而是直到這個缺乏的類被程序使用時才拋出異常。若是jvm進行預先鏈接,第四次掃描會緊接着第三次掃描馬上執行;而若是jvm進行延遲鏈接(即在某個引用第一次執行時才鏈接),那麼第四趟掃描可能在第三趟掃描以後好久才執行(甚至不執行)。

相關文章
相關標籤/搜索