Solidity編譯器漏洞詳解及防範策略

Solidity編譯器漏洞解析及應對策略

編譯器是現代計算機系統的基本組成部分之一。它是一種計算機程序,主要功能是將人類易於理解和編寫的高級程序語言原始碼轉換成計算機底層CPU或字節碼虛擬機可執行的指令代碼。

大多數開發者和安全人員通常會比較關注程序應用代碼的安全,但可能會忽視編譯器自身的安全性。事實上,編譯器作爲一種計算機程序,也存在安全漏洞,而這些漏洞在某些情況下可能帶來嚴重的安全風險。例如,瀏覽器在編譯並解析執行Javascript前端代碼的過程中,可能由於Javascript解析引擎的漏洞,導致用戶在訪問惡意網頁時被攻擊者利用漏洞實現遠程代碼執行,最終完成對受害者瀏覽器甚至操作系統的控制。

Solidity編譯器也不例外,在多個不同版本中都存在安全漏洞。

Solidity編譯器漏洞

Solidity編譯器的作用是將開發人員編寫的智能合約代碼轉換成以太坊虛擬機(EVM)指令代碼,這些EVM指令代碼通過交易打包上傳到以太坊上,最終由EVM解析執行。

需要將Solidity編譯器漏洞與EVM自身的漏洞區分開來。EVM的漏洞是指虛擬機在執行指令時產生的安全問題。由於攻擊者可以上傳任意代碼到以太坊上,這些代碼最終將在每個以太坊P2P客戶端程序中運行,如果EVM存在安全漏洞,將影響整個以太坊網路,可能造成整個網路的拒絕服務(DoS)甚至導致整個鏈完全被攻擊者接管。不過,由於EVM本身設計相對簡單,且核心代碼不會頻繁更新,因此出現上述問題的可能性相對較低。

Solidity編譯器漏洞是指編譯器將Solidity轉換成EVM代碼時存在的問題。與瀏覽器這種會在用戶客戶端計算機上編譯運行Javascript的場景不同,Solidity編譯過程只在智能合約開發者的計算機上進行,不會在以太坊上運行。因此Solidity編譯器漏洞不會直接影響以太坊網路本身。

Solidity編譯器漏洞的一個主要危害在於,可能導致生成的EVM代碼與智能合約開發者的預期存在不一致。由於以太坊上的智能合約通常涉及用戶的加密貨幣資產,因此編譯器導致的任何智能合約bug都可能造成用戶資產損失,產生嚴重後果。

開發者和合約審計人員可能會重點關注合約代碼邏輯實現問題,以及重入、整數溢出等Solidity層面的安全問題。而對於Solidity編譯器的漏洞,僅通過對合約源碼邏輯的審計是很難發現的。需要結合特定編譯器版本與特定的代碼模式共同分析,才能確定智能合約是否受編譯器漏洞的影響。

Solidity編譯器漏洞解析及應對措施

Solidity編譯器漏洞示例

下面以幾個真實的Solidity編譯器漏洞爲例,展示其具體形式、成因及危害。

SOL-2016-9 HighOrderByteCleanStorage

該漏洞存在於較早期的Solidity編譯器版本中(>=0.1.6 <0.4.4)。

考慮如下代碼:

solidity contract C { uint32 a = 0x1234; uint32 b = 0; function f() public { a += 1; } function run() public view returns (uint) { return b; } }

其中storage變量b沒有經過任何修改,因此run()函數應該返回默認值0。但實際上,在存在漏洞的編譯器版本中生成的代碼中,run()將返回1。

在不了解該編譯器漏洞的情況下,普通開發者很難通過簡單的代碼審查發現上述代碼中存在的bug。這個示例代碼比較簡單,可能不會造成特別嚴重的危害。但如果b變量被用於權限驗證、資產記帳等用途,這種與預期的不一致可能導致十分嚴重的後果。

產生這種異常現象的原因在於,EVM使用棧式虛擬機,棧中每個元素均爲32字節大小(即uint256變量大小)。另一方面,底層存儲storage的每個slot也爲32字節大小。而Solidity語言層面支持uint32等各類小於32字節的數據類型,編譯器在處理這種類型的變量時,需要對其高位進行適當的清除操作(clean up)以保證數據的正確性。上述情況中,在加法產生整數溢出時,編譯器沒有正確地對結果高位進行clean up,導致溢出後高位的1 bit被寫入storage中,最終覆蓋了a變量後面的b變量,使b變量的值被修改成了1。

SOL-2022-4 InlineAssemblyMemorySideEffects

該漏洞存在於>=0.8.13 <0.8.15版本的編譯器中。考慮如下代碼:

solidity contract C { function f() public pure returns (uint) { assembly { mstore(0, 0x42) } uint x; assembly { x := mload(0) } return x; } }

Solidity編譯器在將Solidity語言轉換成EVM代碼的過程中,不僅僅是簡單的翻譯。它還會進行深入的控制流與數據分析,實現各種編譯優化流程,以縮減生成代碼的體積,優化執行過程中的gas消耗。這類優化操作在各種高級語言的編譯器中都很常見,但由於需要考慮的情況十分復雜,也很容易出現bug或安全漏洞。

上述代碼的漏洞就源於這類優化操作。考慮這樣一種情況:如果某個函數中存在修改內存0偏移處數據的代碼,但後續沒有任何地方使用該數據,那麼實際上可以將修改內存0的代碼直接移除,從而節約gas,並且不影響後續的程序邏輯。

這種優化策略本身並沒有問題,但在具體的Solidity編譯器代碼實現中,此類優化只應用於單一的assembly block內。對上述示例代碼,對內存0的寫入和訪問存在於兩個不同的assembly block中,而編譯器卻只對單獨的assembly block進行了分析優化。由於第一個assembly block中在寫入內存0後沒有任何讀取操作,因此判定該寫入指令是冗餘的,會將該指令移除,從而產生bug。在存在漏洞的版本中,f()函數將返回值0,而實際上上述代碼應該返回正確的值0x42。

SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup

該漏洞影響>= 0.5.8 < 0.8.16版本的編譯器。考慮如下代碼:

solidity contract C { function f(uint8[4] calldata a) public pure returns (string memory) { return string(abi.encode(a)); } }

正常情況下,上述代碼返回的a變量應爲"aaaa"。但在存在漏洞的版本中會返回空字符串""。

該漏洞的成因是Solidity對calldata類型的數組進行abi.encode操作時,錯誤地對某些數據進行了clean up,導致修改了相鄰的其他數據,造成了編碼解碼後的數據存在不一致。

值得注意的是,Solidity在進行external call和emit event時,會隱式地對參數進行abi.encode,因此上述漏洞代碼出現的概率會比直觀感覺更高。

Solidity編譯器漏洞解析及應對措施

安全建議

基於對Solidity編譯器漏洞威脅模型的分析以及歷史漏洞的梳理,對開發者和安全人員提出以下建議。

對開發者:

  • 使用較新版本的Solidity編譯器。盡管新版本也可能引入新的安全問題,但已知的安全問題通常較舊版本要少。

  • 完善單元測試用例。大部分編譯器層面的bug會導致代碼執行結果與預期不一致。這類問題很難通過代碼審查發現,但很容易在測試階段暴露出來。因此通過提高代碼覆蓋率,可以最大程度地避免此類問題。

  • 盡量避免使用內聯匯編、針對多維數組和復雜結構體的abi編解碼等復雜操作,沒有明確需求時避免追求炫技而盲目使用語言新特性和實驗性功能。根據歷史漏洞的梳理,大部分漏洞與內聯匯編、abi編碼器等操作有關。編譯器在處理復雜的語言特性時確實更容易出現bug。另一方面開發者在使用新特性時也容易出現使用上的誤區,導致安全問題。

對安全人員:

  • 在對Solidity代碼進行安全審計時,不要忽視Solidity編譯器可能引入的安全風險。在Smart Contract Weakness Classification(SWC)中對應的檢查項爲SWC-102: Outdated Compiler Version。

  • 在內部SDL開發流程中,敦促開發團隊升級Solidity編譯器版本,並可以考慮在CI/CD流程中引入針對編譯器版本的自動檢查。

  • 但對編譯器漏洞無需過度恐慌,大部分編譯器漏洞只在特定的代碼模式下觸發,並非使用有漏洞版本的編譯器編譯的合約就一定存在安全風險,實際的安全影響需要根據項目情況具體評估。

一些實用資源:

  • Solidity團隊定期發布的安全警報:

  • Solidity官方倉庫定期更新的bug列表:

  • 各版本編譯器bug列表:

  • Etherscan上Contract -> Code頁面右上角的三角形感嘆號標志可提示當前版本編譯器所存在的安全漏洞。

總結

本文從編譯器的基本概念講起,介紹了Solidity編譯器漏洞,並分析了其在實際以太坊開發環境中可能導致的安全風險,最終對開發者和安全人員提供了若幹實際的安全建議。

Solidity編譯器漏洞解析及應對措施

ETH-1%
查看原文
此頁面可能包含第三方內容,僅供參考(非陳述或保證),不應被視為 Gate 認可其觀點表述,也不得被視為財務或專業建議。詳見聲明
  • 讚賞
  • 5
  • 分享
留言
0/400
ZK佛系玩家vip
· 13小時前
溢出漏洞有点哈人
回復0
链下人生赢家vip
· 18小時前
写代码根本顾不上bug
回復0
链上_狙击手vip
· 18小時前
编译器翻车?早就遇到过了
回復0
倒霉蛋验证者vip
· 18小時前
编译器都有漏洞 吓skr人
回復0
Liquidity_Surfervip
· 18小時前
叫开发仔赶紧修漏洞
回復0
交易,隨時隨地
qrCode
掃碼下載 Gate APP
社群列表
繁體中文
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)