# Solidityコンパイラの脆弱性解析と対策コンパイラは現代のコンピュータシステムの基本的な構成要素の一つです。これはコンピュータプログラムであり、主な機能は人間が理解しやすく書きやすい高水準プログラミング言語のソースコードをコンピュータの低レベルCPUまたはバイトコード仮想マシンが実行可能な命令コードに変換することです。ほとんどの開発者やセキュリティ専門家は、通常、アプリケーションコードのセキュリティに比較的多くの関心を持っていますが、コンパイラ自体のセキュリティを見落とす可能性があります。実際、コンパイラはコンピュータプログラムの一種であり、セキュリティホールも存在し、これらのホールは特定の状況下で深刻なセキュリティリスクを引き起こす可能性があります。たとえば、ブラウザがJavaScriptフロントエンドコードをコンパイルして解析実行する過程で、JavaScript解析エンジンの脆弱性により、ユーザーが悪意のあるウェブページにアクセスすると、攻撃者がその脆弱性を利用してリモートコード実行を実現し、最終的には被害者のブラウザやオペレーティングシステムを制御することができます。Solidityコンパイラも例外ではなく、複数の異なるバージョンにおいてセキュリティの脆弱性が存在します。## Solidityコンパイラの脆弱性Solidityコンパイラの役割は、開発者が記述したスマートコントラクトコードをEthereum仮想マシン(EVM)の命令コードに変換することです。これらのEVM命令コードはトランザクションによってパッケージ化されEthereumにアップロードされ、最終的にはEVMによって解析され実行されます。Solidityコンパイラの脆弱性とEVM自体の脆弱性を区別する必要があります。EVMの脆弱性は、仮想マシンが命令を実行する際に発生するセキュリティ問題を指します。攻撃者は任意のコードをEthereumにアップロードできるため、これらのコードは最終的に各Ethereum P2Pクライアントプログラムで実行されます。EVMにセキュリティ脆弱性がある場合、Ethereumネットワーク全体に影響を与え、全体のネットワークが拒否サービス(DoS)に陥る可能性があり、最終的には全チェーンが攻撃者に完全に乗っ取られる可能性もあります。ただし、EVM自体の設計は比較的シンプルで、コアコードは頻繁に更新されないため、上述の問題が発生する可能性は比較的低いです。Solidityコンパイラの脆弱性とは、コンパイラがSolidityをEVMコードに変換する際に存在する問題を指します。ユーザーのクライアントコンピュータ上でJavascriptをコンパイルして実行するブラウザのようなシーンとは異なり、Solidityのコンパイルプロセスはスマートコントラクト開発者のコンピュータ上でのみ行われ、イーサリアム上では実行されません。したがって、Solidityコンパイラの脆弱性はイーサリアムネットワーク自体には直接影響を与えません。Solidityコンパイラの脆弱性の主な危害は、生成されたEVMコードがスマートコントラクト開発者の期待と一致しない可能性があることです。Ethereum上のスマートコントラクトは通常、ユーザーの暗号通貨資産を含むため、コンパイラによって引き起こされるスマートコントラクトのバグは、ユーザー資産の損失を引き起こし、深刻な結果をもたらす可能性があります。開発者と契約監査者は、契約コードのロジック実装の問題、再入や整数オーバーフローなどのSolidityレベルのセキュリティ問題に重点を置く可能性があります。一方、Solidityコンパイラの脆弱性については、契約ソースコードのロジックの監査だけでは発見が難しいです。特定のコンパイラバージョンと特定のコードパターンを組み合わせて分析する必要があり、スマートコントラクトがコンパイラの脆弱性の影響を受けるかどうかを判断することができます。! 【Solidityコンパイラの脆弱性解析と対策】(https://img-cdn.gateio.im/social/moments-7d1e882c0b106528437910218bf21f82)## Solidityコンパイラの脆弱性の例以下にいくつかの実際のSolidityコンパイラの脆弱性の例を挙げ、それらの具体的な形態、原因、および危害を示します。### SOL-2016-9 ハイオーダーバイトクリーンストレージこの脆弱性は、以前のSolidityコンパイラのバージョンに存在します(>=0.1.6 <0.4.4)。以下のコードを考慮してください:ソリディティコントラクトC { uint32 a = 0x1234; uint32 b = 0; 関数 f() public { a += 1; } パブリック ビュー run()関数は (uint) { を返します。 bを返す; }}ストレージ変数bは何の変更もされていないため、run()関数はデフォルト値0を返すべきです。しかし実際には、脆弱性のあるコンパイラバージョンで生成されたコードでは、run()は1を返します。そのコンパイラの脆弱性を理解していない場合、普通の開発者が簡単なコードレビューを通じて上記のコードに存在するバグを発見することは難しい。このサンプルコードは比較的簡単で、特に深刻な害を引き起こすことはないかもしれない。しかし、b変数が権限検証や資産記帳などに使用される場合、この予期せぬ不一致は非常に深刻な結果を引き起こす可能性がある。この異常現象の原因は、EVMがスタック型仮想マシンを使用しており、スタック内の各要素が32バイトの大きさ(、つまりuint256変数の大きさ)であることにあります。一方、基盤となるストレージの各スロットも32バイトの大きさです。また、Solidity言語レベルではuint32などの32バイト未満のデータ型をサポートしており、コンパイラはこのような型の変数を処理する際に、その高位を適切にクリアする操作(clean up)を行う必要があります。上記の状況では、加算によって整数オーバーフローが発生した際に、コンパイラは結果の高位に対して正しくclean upを行わなかったため、オーバーフロー後の高位の1ビットがストレージに書き込まれ、最終的にa変数の後ろにあるb変数が上書きされ、b変数の値が1に変更されました。### SOL-2022-4 インラインアセンブリメモリ副作用この脆弱性は>=0.8.13 <0.8.15バージョンのコンパイラに存在します。次のコードを考えてください:ソリディティコントラクトC { function f() public pure は (uint) { を返します。 アセンブリ{ mstore(0, 0x42) } uint x; アセンブリ{ x := mload(0) } xを返す; }}Solidityコンパイラは、Solidity言語をEVMコードに変換する過程で、単なる翻訳にとどまらず、制御フローやデータ分析を深く行い、生成されるコードのサイズを削減し、実行プロセス中のガス消費を最適化するさまざまなコンパイル最適化プロセスを実現します。このような最適化操作は、さまざまな高級言語のコンパイラで一般的ですが、考慮すべき状況が非常に複雑であるため、バグやセキュリティの脆弱性が発生しやすいです。上記のコードの脆弱性は、このような最適化操作に由来します。ある関数内にメモリの0オフセットのデータを変更するコードが存在し、その後そのデータが使用されない場合、実際にはメモリ0を変更するコードを直接削除することができ、gasを節約し、後続のプログラムロジックには影響を与えません。この最適化戦略自体には問題はありませんが、具体的なSolidityコンパイラのコード実装において、この種の最適化は単一のassembly block内にのみ適用されます。上記のサンプルコードでは、メモリ0への書き込みとアクセスが2つの異なるassembly blockに存在し、コンパイラは単独のassembly blockに対してのみ分析最適化を行いました。最初のassembly blockではメモリ0への書き込み後に読み取り操作がないため、その書き込み命令が冗長であると判断され、その命令が削除されることになり、バグが発生します。脆弱性のあるバージョンでは、f(関数は値0を返しますが、実際には上記のコードは正しい値0x42を返すべきです。) SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanupこの脆弱性は、>= 0.5.8 < 0.8.16 バージョンのコンパイラに影響します。次のコードを考慮してください:ソリディティコントラクトC { function f###uint8( calldata a[4] public pure は )string memory( { を返します。 戻り値の文字列)abi.encode(a(); }}通常、上記のコードが返すa変数は"aaaa"であるべきです。しかし、脆弱性のあるバージョンでは空の文字列""が返されます。この脆弱性の原因は、Solidityがcalldata型の配列に対してabi.encode操作を行う際に、誤って一部のデータをクリーンアップしてしまい、それにより隣接する他のデータが変更され、エンコードとデコード後のデータに不一致が生じたことです。注目すべきは、Solidityがexternal callとemit eventを行う際に、パラメーターを隐式的にabi.encodeするため、上記の脆弱性コードが発生する確率は直感的に感じるよりも高くなるということです。! 【Solidityコンパイラの脆弱性解析と対策】)https://img-cdn.gateio.im/social/moments-c97428f89ed62d5ad8551cdb2ba30867(## セキュリティの提案Solidityコンパイラの脆弱性脅威モデルの分析と歴史的な脆弱性の整理に基づき、開発者とセキュリティ担当者に以下の提案を行います。開発者向け:- より新しいバージョンのSolidityコンパイラを使用してください。新しいバージョンは新たなセキュリティ問題を引き起こす可能性がありますが、既知のセキュリティ問題は通常、古いバージョンよりも少ないです。- ユニットテストケースを充実させる。ほとんどのコンパイラレベルのバグは、コードの実行結果が期待される結果と一致しない原因となる。この種の問題はコードレビューでは発見しにくいが、テスト段階では簡単に明らかになる。そのため、コードカバレッジを向上させることで、この種の問題を最大限に回避できる。- インラインアセンブリや多次元配列、複雑な構造体のabiのエンコードおよびデコードなどの複雑な操作はできるだけ避け、明確なニーズがない場合は言語の新機能や実験的な機能を盲目的に使用して派手さを追求しないようにしてください。歴史的な脆弱性の整理に基づくと、ほとんどの脆弱性はインラインアセンブリやabiエンコーダなどの操作に関連しています。コンパイラは複雑な言語機能を処理する際にバグを引き起こしやすいです。一方、開発者は新機能を使用する際に使用上の誤解を招くことがあり、それがセキュリティ問題につながることがあります。安全スタッフへ:- Solidityコードのセキュリティ監査を行う際、Solidityコンパイラがもたらす可能性のあるセキュリティリスクを無視してはいけません。Smart Contract Weakness Classification)SWC(に対応するチェック項目はSWC-102: 古いコンパイラバージョンです。- 内部SDL開発プロセスにおいて、開発チームにSolidityコンパイラーのバージョンアップを促し、CI/CDプロセスにコンパイラーバージョンの自動チェックを導入することを検討することができます。- しかし、コンパイラの脆弱性について過度に恐れる必要はありません。大部分のコンパイラの脆弱性は特定のコードパターンでのみ発生し、脆弱性のあるバージョンのコンパイラでコンパイルされた契約が必ずしも安全リスクを伴うわけではありません。実際の安全影響はプロジェクトの状況に基づいて具体的に評価する必要があります。いくつかの便利なリソース:- Solidityチームが定期的に発表するセキュリティ警告: - Solidity公式リポジトリの定期更新バグリスト: - 各バージョンのコンパイラバグリスト: Codeページの右上隅にある三角形の感嘆符マークは、現在のバージョンのコンパイラーに存在するセキュリティ脆弱性を示します。## サマリー本稿では、コンパイラーの基本概念から始め、Solidityコンパイラーの脆弱性を紹介し、実際のEthereum開発環境において引き起こされる可能性のあるセキュリティリスクを分析し、最終的に開発者とセキュリティ担当者に対していくつかの実践的なセキュリティアドバイスを提供します。! 【Solidityコンパイラの脆弱性解析と対策】)https://img-cdn.gateio.im/social/moments-84f5083d8748f2aab71fd92671d999a7(
Solidityコンパイラの脆弱性の内訳と防止戦略
Solidityコンパイラの脆弱性解析と対策
コンパイラは現代のコンピュータシステムの基本的な構成要素の一つです。これはコンピュータプログラムであり、主な機能は人間が理解しやすく書きやすい高水準プログラミング言語のソースコードをコンピュータの低レベルCPUまたはバイトコード仮想マシンが実行可能な命令コードに変換することです。
ほとんどの開発者やセキュリティ専門家は、通常、アプリケーションコードのセキュリティに比較的多くの関心を持っていますが、コンパイラ自体のセキュリティを見落とす可能性があります。実際、コンパイラはコンピュータプログラムの一種であり、セキュリティホールも存在し、これらのホールは特定の状況下で深刻なセキュリティリスクを引き起こす可能性があります。たとえば、ブラウザがJavaScriptフロントエンドコードをコンパイルして解析実行する過程で、JavaScript解析エンジンの脆弱性により、ユーザーが悪意のあるウェブページにアクセスすると、攻撃者がその脆弱性を利用してリモートコード実行を実現し、最終的には被害者のブラウザやオペレーティングシステムを制御することができます。
Solidityコンパイラも例外ではなく、複数の異なるバージョンにおいてセキュリティの脆弱性が存在します。
Solidityコンパイラの脆弱性
Solidityコンパイラの役割は、開発者が記述したスマートコントラクトコードをEthereum仮想マシン(EVM)の命令コードに変換することです。これらのEVM命令コードはトランザクションによってパッケージ化されEthereumにアップロードされ、最終的にはEVMによって解析され実行されます。
Solidityコンパイラの脆弱性とEVM自体の脆弱性を区別する必要があります。EVMの脆弱性は、仮想マシンが命令を実行する際に発生するセキュリティ問題を指します。攻撃者は任意のコードをEthereumにアップロードできるため、これらのコードは最終的に各Ethereum P2Pクライアントプログラムで実行されます。EVMにセキュリティ脆弱性がある場合、Ethereumネットワーク全体に影響を与え、全体のネットワークが拒否サービス(DoS)に陥る可能性があり、最終的には全チェーンが攻撃者に完全に乗っ取られる可能性もあります。ただし、EVM自体の設計は比較的シンプルで、コアコードは頻繁に更新されないため、上述の問題が発生する可能性は比較的低いです。
Solidityコンパイラの脆弱性とは、コンパイラがSolidityをEVMコードに変換する際に存在する問題を指します。ユーザーのクライアントコンピュータ上でJavascriptをコンパイルして実行するブラウザのようなシーンとは異なり、Solidityのコンパイルプロセスはスマートコントラクト開発者のコンピュータ上でのみ行われ、イーサリアム上では実行されません。したがって、Solidityコンパイラの脆弱性はイーサリアムネットワーク自体には直接影響を与えません。
Solidityコンパイラの脆弱性の主な危害は、生成されたEVMコードがスマートコントラクト開発者の期待と一致しない可能性があることです。Ethereum上のスマートコントラクトは通常、ユーザーの暗号通貨資産を含むため、コンパイラによって引き起こされるスマートコントラクトのバグは、ユーザー資産の損失を引き起こし、深刻な結果をもたらす可能性があります。
開発者と契約監査者は、契約コードのロジック実装の問題、再入や整数オーバーフローなどのSolidityレベルのセキュリティ問題に重点を置く可能性があります。一方、Solidityコンパイラの脆弱性については、契約ソースコードのロジックの監査だけでは発見が難しいです。特定のコンパイラバージョンと特定のコードパターンを組み合わせて分析する必要があり、スマートコントラクトがコンパイラの脆弱性の影響を受けるかどうかを判断することができます。
! 【Solidityコンパイラの脆弱性解析と対策】(https://img-cdn.gateio.im/webp-social/moments-7d1e882c0b106528437910218bf21f82.webp)
Solidityコンパイラの脆弱性の例
以下にいくつかの実際のSolidityコンパイラの脆弱性の例を挙げ、それらの具体的な形態、原因、および危害を示します。
SOL-2016-9 ハイオーダーバイトクリーンストレージ
この脆弱性は、以前のSolidityコンパイラのバージョンに存在します(>=0.1.6 <0.4.4)。
以下のコードを考慮してください:
ソリディティ コントラクトC { uint32 a = 0x1234; uint32 b = 0; 関数 f() public { a += 1; } パブリック ビュー run()関数は (uint) { を返します。 bを返す; } }
ストレージ変数bは何の変更もされていないため、run()関数はデフォルト値0を返すべきです。しかし実際には、脆弱性のあるコンパイラバージョンで生成されたコードでは、run()は1を返します。
そのコンパイラの脆弱性を理解していない場合、普通の開発者が簡単なコードレビューを通じて上記のコードに存在するバグを発見することは難しい。このサンプルコードは比較的簡単で、特に深刻な害を引き起こすことはないかもしれない。しかし、b変数が権限検証や資産記帳などに使用される場合、この予期せぬ不一致は非常に深刻な結果を引き起こす可能性がある。
この異常現象の原因は、EVMがスタック型仮想マシンを使用しており、スタック内の各要素が32バイトの大きさ(、つまりuint256変数の大きさ)であることにあります。一方、基盤となるストレージの各スロットも32バイトの大きさです。また、Solidity言語レベルではuint32などの32バイト未満のデータ型をサポートしており、コンパイラはこのような型の変数を処理する際に、その高位を適切にクリアする操作(clean up)を行う必要があります。上記の状況では、加算によって整数オーバーフローが発生した際に、コンパイラは結果の高位に対して正しくclean upを行わなかったため、オーバーフロー後の高位の1ビットがストレージに書き込まれ、最終的にa変数の後ろにあるb変数が上書きされ、b変数の値が1に変更されました。
SOL-2022-4 インラインアセンブリメモリ副作用
この脆弱性は>=0.8.13 <0.8.15バージョンのコンパイラに存在します。次のコードを考えてください:
ソリディティ コントラクトC { function f() public pure は (uint) { を返します。 アセンブリ{ mstore(0, 0x42) } uint x; アセンブリ{ x := mload(0) } xを返す; } }
Solidityコンパイラは、Solidity言語をEVMコードに変換する過程で、単なる翻訳にとどまらず、制御フローやデータ分析を深く行い、生成されるコードのサイズを削減し、実行プロセス中のガス消費を最適化するさまざまなコンパイル最適化プロセスを実現します。このような最適化操作は、さまざまな高級言語のコンパイラで一般的ですが、考慮すべき状況が非常に複雑であるため、バグやセキュリティの脆弱性が発生しやすいです。
上記のコードの脆弱性は、このような最適化操作に由来します。ある関数内にメモリの0オフセットのデータを変更するコードが存在し、その後そのデータが使用されない場合、実際にはメモリ0を変更するコードを直接削除することができ、gasを節約し、後続のプログラムロジックには影響を与えません。
この最適化戦略自体には問題はありませんが、具体的なSolidityコンパイラのコード実装において、この種の最適化は単一のassembly block内にのみ適用されます。上記のサンプルコードでは、メモリ0への書き込みとアクセスが2つの異なるassembly blockに存在し、コンパイラは単独のassembly blockに対してのみ分析最適化を行いました。最初のassembly blockではメモリ0への書き込み後に読み取り操作がないため、その書き込み命令が冗長であると判断され、その命令が削除されることになり、バグが発生します。脆弱性のあるバージョンでは、f(関数は値0を返しますが、実際には上記のコードは正しい値0x42を返すべきです。
) SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup
この脆弱性は、>= 0.5.8 < 0.8.16 バージョンのコンパイラに影響します。次のコードを考慮してください:
ソリディティ コントラクトC { function f###uint8( calldata a[4] public pure は )string memory( { を返します。 戻り値の文字列)abi.encode(a(); } }
通常、上記のコードが返すa変数は"aaaa"であるべきです。しかし、脆弱性のあるバージョンでは空の文字列""が返されます。
この脆弱性の原因は、Solidityがcalldata型の配列に対してabi.encode操作を行う際に、誤って一部のデータをクリーンアップしてしまい、それにより隣接する他のデータが変更され、エンコードとデコード後のデータに不一致が生じたことです。
注目すべきは、Solidityがexternal callとemit eventを行う際に、パラメーターを隐式的にabi.encodeするため、上記の脆弱性コードが発生する確率は直感的に感じるよりも高くなるということです。
! 【Solidityコンパイラの脆弱性解析と対策】)https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(
セキュリティの提案
Solidityコンパイラの脆弱性脅威モデルの分析と歴史的な脆弱性の整理に基づき、開発者とセキュリティ担当者に以下の提案を行います。
開発者向け:
より新しいバージョンのSolidityコンパイラを使用してください。新しいバージョンは新たなセキュリティ問題を引き起こす可能性がありますが、既知のセキュリティ問題は通常、古いバージョンよりも少ないです。
ユニットテストケースを充実させる。ほとんどのコンパイラレベルのバグは、コードの実行結果が期待される結果と一致しない原因となる。この種の問題はコードレビューでは発見しにくいが、テスト段階では簡単に明らかになる。そのため、コードカバレッジを向上させることで、この種の問題を最大限に回避できる。
インラインアセンブリや多次元配列、複雑な構造体のabiのエンコードおよびデコードなどの複雑な操作はできるだけ避け、明確なニーズがない場合は言語の新機能や実験的な機能を盲目的に使用して派手さを追求しないようにしてください。歴史的な脆弱性の整理に基づくと、ほとんどの脆弱性はインラインアセンブリやabiエンコーダなどの操作に関連しています。コンパイラは複雑な言語機能を処理する際にバグを引き起こしやすいです。一方、開発者は新機能を使用する際に使用上の誤解を招くことがあり、それがセキュリティ問題につながることがあります。
安全スタッフへ:
Solidityコードのセキュリティ監査を行う際、Solidityコンパイラがもたらす可能性のあるセキュリティリスクを無視してはいけません。Smart Contract Weakness Classification)SWC(に対応するチェック項目はSWC-102: 古いコンパイラバージョンです。
内部SDL開発プロセスにおいて、開発チームにSolidityコンパイラーのバージョンアップを促し、CI/CDプロセスにコンパイラーバージョンの自動チェックを導入することを検討することができます。
しかし、コンパイラの脆弱性について過度に恐れる必要はありません。大部分のコンパイラの脆弱性は特定のコードパターンでのみ発生し、脆弱性のあるバージョンのコンパイラでコンパイルされた契約が必ずしも安全リスクを伴うわけではありません。実際の安全影響はプロジェクトの状況に基づいて具体的に評価する必要があります。
いくつかの便利なリソース:
Solidityチームが定期的に発表するセキュリティ警告:
Solidity公式リポジトリの定期更新バグリスト:
各バージョンのコンパイラバグリスト:
Codeページの右上隅にある三角形の感嘆符マークは、現在のバージョンのコンパイラーに存在するセキュリティ脆弱性を示します。
サマリー
本稿では、コンパイラーの基本概念から始め、Solidityコンパイラーの脆弱性を紹介し、実際のEthereum開発環境において引き起こされる可能性のあるセキュリティリスクを分析し、最終的に開発者とセキュリティ担当者に対していくつかの実践的なセキュリティアドバイスを提供します。
! 【Solidityコンパイラの脆弱性解析と対策】)https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(