メモリ安全性

メモリ安全性 (メモリあんぜんせい、) は、バッファオーバーフローやなどの、RAMアクセス時に発生するバグやセキュリティホールなどから保護されている状態のことである。例えば、Javaはで配列の境界とポインタの参照外しを確認するので、メモリ安全であると言われている。対照的に、CとC++はを行わないメモリアドレスを直接参照するポインタを使用した任意のポインタ演算が可能なので、メモリ安全ではない。

 歴史 
メモリエラーは及びタイムシェアリングシステムでFork爆弾などの問題を回避するために検討されたことが始まりである。Fingerプロトコルでのバッファオーバーフローを悪用したモリスワームが登場するまでは、これは殆ど理論上の存在であった。その後、コンピュータセキュリティの分野は急速に発展し、Return-to-libc攻撃などの多くの新たなサイバー攻撃手法が次々と誕生し、これに対する防御手法として実行保護やアドレス空間配置のランダム化などの技術が開発された。アドレス空間配置のランダム化は殆どのバッファオーバーフロー攻撃を防ぎ、攻撃者がアドレスを取得する際に、かその他のアプリケーションに依存する方法を利用することを必要とさせるものであるが、これが採用されるまでには長い時間を必要とした。但し、これらの技術が利用されるのは、ライブラリとスタックの場所をランダムにするときに限定されることが一般的である。

 手法 
DieHard及びこれの再設計であるDieHarderとは、自身のランダム仮想メモリページにオブジェクトを割り当てる特別なヒープアロケータであり、無効な読み取りと書き込みを停止し、これを引き起こす命令の詳細までデバッグすることができる。この保護はハードウェアのメモリ保護に依存しているので、オーバーヘッドはそれほど大きくないことが通常であるが、プログラムで割り当てを多用した場合には大きくなる可能性がある。アドレス空間配置のランダム化はメモリエラーに対する確率的保護のみを提供するが、多くの場合、既存のソフトウェアにバイナリを再リンクすることで容易に実装することができる。

ValgrindのMemcheckツールは、命令セットシミュレータを使用してメモリチェック仮想マシンでコンパイル済みのプログラムを実行し、実行時メモリエラーのサブセットの検出を保証する。但し、一般的にプログラムの実行速度が40倍遅くなる。更に、カスタムメモリアロケータを明示的に通知する必要がある。

ソースコードにアクセスすることによって、ポインタに対する正当な値 (メタデータ) を収集及び追跡し、各ポインタアクセスがメタデータに対して有効なものであるのかを検証するなどのライブラリが存在する。一般的に、メモリ安全性はガベージコレクションのトレースと、全てのメモリアクセスで実行時検証を行うことによって保証することができる。この手法にはオーバーヘッドがあるが、Valgrindの手法よりは少ない。ガベージコレクションを採用している全ての言語がこの手法を採用している。CとC++では、実行時にメモリ安全性の検証を行うためにコードのコンパイル時変換を実行するCheckPointerやなどの多くのツールが存在し、これらを使用した場合に実行速度が2倍遅くなる。

他の手法としては、静的コード解析と自動定理証明を使用して、プログラムにメモリエラーがないことを確認する方法がある。例えば、Rustはメモリ安全性を確保するためにボローチェッカーを実装している。などのツールはCで静的コード解析を提供する。C++のは、この手法の限定的な形式である。

 境界チェック 
は操作が境界（範囲）を超えているか否かを検査する機能である。配列アクセス時のインデックスチェック (index checking) が有名である。検査は実行時におこなわれる場合が多い（コンパイル時の完全な境界チェックは実用段階に至っていない）。実行時境界チェックによる例外を用いることで、バッファオーバーフローをはじめとした多くのメモリアクセスに対して安全性が得られる。

 メモリエラーの種類 
様々な種類のメモリエラーが発生する可能性がある:
 アクセスエラー: ポインタの無効な読み取りと書き込み
 バッファオーバーフロー - 範囲外の書き込みは、隣接するオブジェクトの中身や内部データ (ヒープの管理領域など)、リターンアドレスを破壊する可能性がある。
  - 範囲外の読み取りは、機密データを明らかにしたり、攻撃者がアドレス空間配置のランダム化を回避するのに役立つ。
 競合状態 - 共有メモリへ同時に読み取りと書き込みを行う。
 無効ページフォールト - 仮想アドレス空間で未定義のポインタへアクセスする。ヌルポインタの参照外しは、殆どの環境では例外又はプログラムの終了を引き起こすが、メモリ保護のない場合や、ヌルポインタの使用時に大きなオフセットや負のオフセットが含まれていた場合、カーネル又はシステムが破壊される可能性がある。
 解放後使用 - 削除されたオブジェクトのアドレスを格納しているの参照外し。
 : 定義されていない変数を使用した場合、望ましくない値が代入されたり、一部の言語では破損した値が代入される場合がある
 ヌルポインタの参照外し - 無効なポインタ又は割り当てられていないメモリへのポインタの参照外し。
  - 何らかの既知の状態への初期化前にポインタが使用されると発生する。検出されないままになる可能性は低いが、ダングリングポインタと同じように不規則な動作をする。
 メモリリーク: メモリ使用量が追跡されていない又は誤って追跡されている場合
 スタックの枯渇 - 一般的に、深すぎる再帰呼出しによってプログラムがスタック領域を使い果たした場合に発生する。通常、ガードページはプログラムを停止し、メモリが破壊されることを防ぐが、スタックフレームが大きな関数ではページをバイパスする場合がある。
  - プログラムが利用可能なメモリよりも多くのメモリを確保しようとする。一部の言語では、確保後にこの状態を手動で確認する必要がある。
 二重解放 - freeを繰り返し呼び出すと、同じアドレスにある新しいオブジェクトが早期解放される場合がある。正確なアドレスが再利用されていない場合、他の破壊が発生する可能性があり、特にを使用するアロケータでは可能性が高い。
 無効な解放 - 無効なアドレスをfreeに渡すと、ヒープが破壊される可能性がある。
 不一致な解放 - 複数のアロケータが使用されている場合、異なるアロケータの解放用の関数でメモリを解放しようとする。
 不要な - 無関係な目的のためにメモリロケーションが2回割り当て及び変更される場合。

 脚注 

Category:バグ
Category:エクスプロイト
Category:プログラミング言語の実装
Category:ソフトウェア工学