這次Google Project Zero引爆的深水炸彈,也意外的讓「預測執行 (Speculative Execution)」、「非循序執行 (Out-Of-Order Execution)」和「分支預測 (Branch Prediction)」這些歷史悠久的計算機結構專有名詞,再度成為網路論壇的熱門關鍵字。
為了方便各位科科瞬間理解上面三個名詞的關係,請記得以下恆等式,不必謝我。
「預測執行 = 分支預測 + 非循序執行」,根據分支預測的結果,先斬後奏賭博性的執行指令,再藉由非循序執行引擎維持指令執行順序的一致性,與回復當預測錯誤時的處理器狀態,各位只要知道這些就夠了。
如果一時之間還搞不懂為何要這樣作,請重新想清楚「電腦 (Computer)」和「計算器 (Calculator)」最大的不同之處:電腦最重要特徵在於「條件判斷」的能力,根據不同的條件,執行不同的指令流。
如果你不想看到整條指令管線,因為等待確認條件判斷的結果而停擺,就需要設計一個紀錄分支歷史的小型快取記憶體,根據其內容「以古鑑今」去預測分支是否發生,或著發生後會跳到哪個記憶體位址,再繼續擷取並預測性的執行指令。
分支預測當然也有可能發生錯誤,像迴圈 (Loop) 就是一例,反覆發生 (Taken) 後,起碼最後一次就不會發生 (Not Taken),這也暗示了我們可以藉由「訓練」分支預測,使其錯誤地預測執行不應被執行的程式碼。
既然系統權限已經受到重重保護,為何還會被「僭越」?
我們前面有看到處理器會有不同的程式執行權限與記憶體保護機制,但為何還會發生「下犯上」使用者模式可以讀取到系統核心資訊的慘劇,原因很簡單,因為預測執行「衝過頭」了,將不應該被應用程式存取到的記憶體位置,跳過權限檢查,被預先載入快取記憶體,即使此預測執行作廢,該記憶體位址區段仍在快取內,此時此刻有心人就有上下其手的機會,例如可「旁敲側擊」觀察記憶體存取的反應時間 (被快取到當然會更短),判斷這段記憶體位址是否屬於系統核心。
事實上,近代高效能處理器的預測與非循序執行,並非僅限於指令,連記憶體存取這檔事都可雨露均霑,不按牌理出牌,在x86世界源自於Intel Core微架構「Merom」的「記憶體資料相依性預測功能 (Memory Disambiguation)」即為最好的範例,根據預測,承受後面的記憶體載入指令、可能需要前面載入執行結果的相依性風險,不等待前面回存 (Store) 指令完成工作,大膽的提前載入 (Load) 記憶體位址,以確保指令管線不停頓的順暢運轉。
一圖勝千言,直接引用Intel當年介紹Merom的簡報。原本Load4要等待Store3完成。
▲有了記憶體資料相依性預測功能,不等Store3,Load4就賭下去了。當然,如果賭錯了,後頭要收拾殘局的成本就非常的高,大約40個時脈週期就飛了,但看在賭對了就賺翻了的份上,硬著頭皮還要給他賭下去。
▲有沒有突然感覺到,這些處理器設計者好像都蠻「賭性堅強」?
很不幸的,Intel剛剛好就是這群賭徒中,特別敢冒險的那位 (Intel其實在微架構設計上一直蠻激進的),有時候還真心覺得夜路走多了真的會碰到鬼,的確不是騙人的,Google Project Zero就活見「鬼」了。
成本高昂的分頁表隔離
前一篇有提到透過分頁表管理虛擬記憶體的配置,需要權限保護的作業系統核心,與一般使用者的應用程式,實際上是會有可能混在一起的。以32位元Linux為例,理論上應用程式可以看到4GB的虛擬位址,但其實最上面1GB的屬於系統核心;32位元Windows也有類似的限制,1GB或2GB切給作業系統核心與驅動程式,所以應用程式只能使用到3GB或2GB。
簡而言之,應用程式的確有機會利用某些漏洞,存取本應不該被允許的系統核心資訊。
那你也許會問:既然如此,為何我們不堅壁清野,維護兩份獨立的分頁表,一份系統核心,一份使用者,不就功德圓滿了?
但每次系統呼叫,都需要反覆切換分頁表並搬移大量的資料,尤其用來加速虛擬位址轉換實體位址的TLB (Translation Lookaside Buffer) 會一直「上沖下洗 (Flush)」,等於每次系統呼叫都要重建快取資料,想閃也閃不開,導致嚴重的效能損失,這也是為何主流作業系統都將敏感的系統核心資訊,安置在虛擬位址的高位,並透過將程式碼與資料「隨機性」的散布其內,以避免當某個漏洞被發現時,被用來「一招半式打天下」,難以採用通用手段危害作業系統的強固性。
但很不幸的,Google Project Zero的研究成果,終究還是將作業系統廠商逼回成本高昂的「隔離」手段,最起碼眼前的短期方案僅限於此,能否有更低成本的手段,還在未定之天。往好處想,無須系統呼叫的純運算工作,效能應該不會受到什麼影響,也許吧。
對x86處理器來說,問題就更大條了,兩份獨立的分頁表,意味著避開昔日16位元時代遺產「節區 (Segment) 記憶體定址」的「平面 (Flat) 記憶體模式」就此破功,對於那些早已「放生」老舊記憶體定址模式效能的新型x86處理器微架構來說,實在是不堪回首的嚴酷考驗,這就像一道旋轉們,你越積極的拋棄遺產讓微架構針對未來最佳化,你背後被老舊包袱狠狠集中的後座力也越大,所謂「牙膏擠了原來還能吸回去」,大概就是這麼一回事。