ヒープベースのバッファーオーバーランに対してシステムを強化するために、以前に次の方法を提案、評価、または展開した人がいるのではないかと思います。スタック。
次のような構造体を考えます
struct whatever {
int blah;
char buf[256];
void (*fp)(); // a function pointer
}
buf
フィールドの終わりを超えて書き込むオーバーランがある場合、関数ポインターフィールドfp
を上書きできることに注意してください。
コンパイラは、バッファと関数ポインタの間に保存されたカナリア(秘密のランダムな値)を導入することで、これを正当に防御できます。基本的に、コンパイラーは構造のレイアウトを次のように変換します。
struct whatever {
int blah;
char buf[256];
unsigned int canary; // inserted by compiler; not exposed to source code
void (*fp)(); // a function pointer
}
たとえば、コンパイラは、プログラムがcanary
に書き込むときはいつでも、グローバルシークレット値でfp
フィールドを書き込むように調整し、canary
の値が残っていることを確認できます。プログラムがfp
から読み取るときは常に変更されません。
これは基本的にスタックカナリアの類似物ですが、スタックの戻りアドレスではなく、ヒープの関数ポインタの保護に焦点を当てています。それは自然な考えのようです。
誰かがこれを以前に提案したことがありますか?誰かがそれを試作したり、このようなことをすることのパフォーマンスコストを評価したりしましたか? (スタックカナリアのように、コンパイラの変更が必要であるという事実を超えて)展開への明らかでない障壁はありますか?
私が行った研究:ヒープ内のオブジェクト間にガードページを挿入するという考えは知っていますが、それは異なります(単一のオブジェクトの境界を超えるヒープオーバーフローから保護しますが、単一のヒープオブジェクトの領域内にとどまるヒープオーバーフローから保護します)。私は Cruiser と ContraPolice に精通していますが、これはヒープ内のオブジェクト間にカナリアを配置しますが、これもオブジェクト内オーバーフローではなくオブジェクト間オーバーフローに焦点を当てています。また、mallocのメタデータを保護するためのスタックカナリアまたはポインター暗号化の使用にも精通していますが、これはオブジェクト内オーバーフローを保護せず、関数ポインターではなくmallocのメタデータを保護することを目的としています。
オブジェクト内のカナリアは、1つの実用的な問題にぶつかります。それは、これらのオブジェクトのメモリ内レイアウトを変更します。このレイアウトは、たとえばプログラムとライブラリの間で渡されるときに一貫している必要があります。タイプstruct whatever *
のポインターを、コンパイルされたプログラムからwithこの計測器に、ライブラリーにコンパイルされたwithoutこの計測器に渡した場合(ただし、同じ宣言struct whatever
)の場合、一部のフィールドが異なるオフセットにあるため、問題が発生します。その結果、理論的な研究以外の目的で内部カナリアを使用すると、既存のほとんどすべてのコードとライブラリとの互換性が失われます。