📢 Gate廣場 #NERO发帖挑战# 秀觀點贏大獎活動火熱開啓!
Gate NERO生態周來襲!發帖秀出NERO項目洞察和活動實用攻略,瓜分30,000NERO!
💰️ 15位優質發帖用戶 * 2,000枚NERO每人
如何參與:
1️⃣ 調研NERO項目
對NERO的基本面、社區治理、發展目標、代幣經濟模型等方面進行研究,分享你對項目的深度研究。
2️⃣ 參與並分享真實體驗
參與NERO生態周相關活動,並曬出你的參與截圖、收益圖或實用教程。可以是收益展示、簡明易懂的新手攻略、小竅門,也可以是行情點位分析,內容詳實優先。
3️⃣ 鼓勵帶新互動
如果你的帖子吸引到他人參與活動,或者有好友評論“已參與/已交易”,將大幅提升你的獲獎概率!
NERO熱門活動(帖文需附以下活動連結):
NERO Chain (NERO) 生態周:Gate 已上線 NERO 現貨交易,爲回饋平台用戶,HODLer Airdrop、Launchpool、CandyDrop、餘幣寶已上線 NERO,邀您體驗。參與攻略見公告:https://www.gate.com/announcements/article/46284
高質量帖子Tips:
教程越詳細、圖片越直觀、互動量越高,獲獎幾率越大!
市場見解獨到、真實參與經歷、有帶新互動者,評選將優先考慮。
帖子需原創,字數不少於250字,且需獲得至少3條有效互動
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編譯器漏洞爲例,展示其具體形式、成因及危害。
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編譯器。盡管新版本也可能引入新的安全問題,但已知的安全問題通常較舊版本要少。
完善單元測試用例。大部分編譯器層面的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編譯器漏洞,並分析了其在實際以太坊開發環境中可能導致的安全風險,最終對開發者和安全人員提供了若幹實際的安全建議。