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。

普通開發者很難通過簡單的代碼審查發現上述代碼中存在的問題。雖然這個示例相對簡單,可能不會造成特別嚴重的後果,但如果 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 中。對上述 PoC 代碼,對內存 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(string[1] calldata a) external pure returns (string memory) { return abi.decode(abi.encode(a), (string[1]))[0]; } }

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

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

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

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

安全建議

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

對開發者:

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

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

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

對安全人員:

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

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

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

實用資源

  • Solidity 團隊定期發布的安全警報文章
  • Solidity 官方倉庫定期更新的 bug 列表
  • 各版本編譯器 bug 列表。可據此在 CI/CD 過程中引入自動進行編譯器版本的檢查,提示當前版本中存在的安全漏洞。
  • Etherscan 上 Contract -> Code 頁面右上角的三角形感嘆號標志可提示當前版本編譯器所存在的安全漏洞。

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

總結

本文從編譯器的基本概念出發,介紹了 Solidity 編譯器漏洞,並分析了其在實際以太坊開發環境中可能導致的安全風險,最後爲開發者和安全人員提供了若幹實用的安全建議。通過了解這些漏洞和採取相應的預防措施,我們可以更好地保護智能合約的安全性,減少潛在的資產損失風險。

ETH-3.72%
查看原文
此頁面可能包含第三方內容,僅供參考(非陳述或保證),不應被視為 Gate 認可其觀點表述,也不得被視為財務或專業建議。詳見聲明
  • 讚賞
  • 6
  • 分享
留言
0/400
0xLuckboxvip
· 07-31 00:35
逻辑漏洞啥的真伤头 溜了溜了
回復0
LidoStakeAddictvip
· 07-30 09:56
溢出咯 代码还得改
回復0
稳定币套利者vip
· 07-30 09:26
*调整眼镜* 嗯... 从统计学的角度来看,编译器风险在 DeFi TVL 计算中被严重低估了。
查看原文回復0
BoredStakervip
· 07-30 09:24
啥时候能说人话!
回復0
无情的套利机器vip
· 07-30 09:24
又得来搞编译器的坑了?
回復0
APY追逐者vip
· 07-30 09:15
狂猎gas费的时候才想起编译器漏洞
回復0
交易,隨時隨地
qrCode
掃碼下載 Gate APP
社群列表
繁體中文
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)