ローカルおよびオンライン ソースから関数を収集できる C プリプロセッサ / コンパイラを構築したいと考えています。すなわち:
#fetch MP3FileBuilder http://scripts.com/MP3Builder.gz
#fetch IpodDeviceReader http://apple.com/modules/MP3Builder.gz
void mymodule_main() {
MP3FileBuilder(&some_data);
}
それは簡単な部分です。
難しいのは、インポートされたコードを、ディスクまたはシステム リソース (メモリ割り当てとスタックを含む) への直接または無制限のアクセスから "サンドボックス化" するための信頼できる方法が必要なことです。信頼できない C コード (モジュール) の小さなスニペットを、別のプロセス、VM、またはインタープリターに配置するオーバーヘッドなしで安全に実行する方法が必要です(ただし、別のスレッドは許容されます)。
要件
- CPU時間を含むデータとリソースへのアクセスにクォータを設定する必要があります。
- 標準ライブラリへの直接アクセスをブロックします
- 無限再帰を生み出す悪意のあるコードを阻止したい
- 静的割り当てと動的割り当てを特定の制限に制限したい
- モジュールが発生させる可能性のあるすべての例外 (0 による除算など) をキャッチしたいと考えています。
- モジュールは、コア インターフェイスを介してのみ他のモジュールと対話できます
- モジュールは、コア インターフェイスを介してのみシステム (I/O など) と対話できます。
- モジュールは、ビット操作、数学、配列、列挙型、ループ、および分岐を許可する必要があります。
- モジュールは ASM を使用できません
- モジュール用に予約されたメモリへのポインタと配列のアクセスを制限したい (カスタムの safe_malloc() を介して)
- ANSI C またはサブセットをサポートする必要があります (以下を参照)。
- システムは軽量でクロスプラットフォーム (組み込みシステムを含む) である必要があります。
- システムは GPL または LGPL と互換性がある必要があります。
C のサブセットで満足しています。テンプレートやクラスなどは必要ありません。私が主に興味を持っているのは、高水準言語ではうまくいかない高速な数学、ビット演算、バイナリ データの検索と処理などです。
モジュールを作成するために、既存の C コードを変更せずに再利用できるという意図はありません。その意図は、モジュールを基本的なロジックと変換操作 (ビデオのトランスコードや圧縮操作など) に限定するように設計された一連のルールと制限に準拠することをモジュールに要求することです。
このようなコンパイラ/プリプロセッサへの理論的な入力は、module_main 関数を含む単一の ANSI C ファイル (または安全なサブセット) であり、インクルードまたはプリプロセッサ ディレクティブはなく、ASM はなく、ループ、分岐、関数呼び出し、ポインターが許可されます。数学 (モジュールに割り当てられた範囲に制限されます)、ビットシフト、ビットフィールド、キャスト、列挙型、配列、int、float、文字列、数学。それ以外はオプションです。
実装例
これをよりよく説明するための疑似コードスニペットを次に示します。ここで、モジュールはそのメモリ割り当てクォータを超え、無限再帰も作成します。
buffer* transcodeToAVI_main( &in_buffer ) {
int buffer[1000000000]; // allocation exceeding quota
while(true) {} // infinite loop
return buffer;
}
これは、プリプロセッサがウォッチポイントを追加してメモリ使用量と再帰をチェックし、すべてを例外ハンドラにラップした変換バージョンです。
buffer* transcodeToAVI_main( &in_buffer ) {
try {
core_funcStart(__FILE__,__FUNC__); // tell core we're executing this function
buffer = core_newArray(1000000000, __FILE__, __FUNC__); // memory allocation from quota
while(true) {
core_checkLoop(__FILE__, __FUNC__, __LINE__) && break; // break loop on recursion limit
}
core_moduleEnd(__FILE__,__FUNC__);
} catch {
core_exceptionHandler(__FILE__, __FUNC__);
}
return buffer;
}
これらのチェックの実行がモジュールのパフォーマンスに影響を与えることは理解していますが、解決しようとしているタスクについては、高水準言語または VM 言語よりもパフォーマンスが優れているのではないかと思います。モジュールが危険なことをするのを完全に止めようとしているわけではありません。制御された方法で (ユーザーのフィードバックなどを介して)、危険なことを強制しようとしているだけです。つまり、「モジュール X がメモリ割り当てを超えました。続行または中止しますか?」。
アップデート
これまでのところ、境界チェックといくつかのカスタム関数とループコードを備えたカスタムコンパイラ(ハッキングされたTCCのようなもの)を使用して再帰をキャッチすることが最善です。他に何を確認する必要があるか、またはどのような解決策があるかについての考えを聞きたいです。使用前に ASM を削除してポインターをチェックすることで、以下の以前の回答で表明された多くの懸念が解決されると思います。SO コミュニティからさらにフィードバックを引き出すために報奨金を追加しました。
私が探している賞金のために:
- 上で定義した理論上のシステムに対する潜在的なエクスプロイトの詳細
- アクセスごとにポインターをチェックする際の最適化の可能性
- 概念の実験的なオープンソース実装 (Google Native Client など)
- 幅広い OS とデバイスをサポートするソリューション (OS/ハードウェア ベースのソリューションではありません)
- ほとんどの C 操作、または C++ (可能な場合) をサポートするソリューション
GCC で動作するメソッド (つまり、プリプロセッサまたは小さなGCC パッチ) に対する追加の功績。
また、私が試みていることがまったく不可能であることを決定的に証明できる人を検討します。ただし、これまでのところ、反対意見のどれもが、不可能だと考える理由の技術的側面を本当に釘付けにしていないため、かなり説得力がある必要があります。いいえと答えた人を弁護するために、この質問はもともと C++ を安全に実行する方法として提起されました。要件を C の限られたサブセットに縮小しました。
私の C の理解は「中級」に分類できますが、PC ハードウェアの理解はおそらく「上級」の一歩下です。可能であれば、そのレベルの回答を指導してみてください。私は C の専門家ではないので、主に回答に与えられた投票と、その回答が私の要件にどれだけ近いかに基づいて説明します。あなたの主張に十分な証拠を提供し (回答者)、投票すること (それ以外の人) によって支援することができます。報奨金のカウントダウンが 6 時間に達したら、回答を割り当てます。
最後に、この問題を解決することは、ますますネットワーク化され偏執的な世界で C の妥当性を維持するための大きな一歩になると信じています。他の言語がパフォーマンスの点でギャップを埋め、コンピューティング能力が向上するにつれて、C 開発の追加リスクを正当化するのはますます難しくなります (現在の ASM のように)。あなたの回答は、数点の SO ポイントを獲得するよりもはるかに関連性があると思いますので、賞金が期限切れになった場合でも、できる限り貢献してください。