PaX
PaX は、スタックのベースアドレス(のオフセット)を16バイト単位にランダム化し、同時にセグメントの配置をページ単位の空隙の大きさをランダム化する。全体としてのランダム化の程度は仮想空間サイズに依存する。例えば、32ビットの場合、スタックベースは256MiBの範囲のどこかに設定され、可能な位置は1600万通り、エントロピーは24ビットとなる。
スタックベースのランダム化は、シェルコードとreturn-to-libc攻撃のペイロードに影響を与える。シェルコード攻撃はスタック上のリターンポインタをペイロード内を指すよう変更しようとする。一方、return-to-libc攻撃はスタック上のフレームポインタを書き換えようとする。どちらにしても、成功する可能性は大幅に減る。スタックの位置は予測不可能となり、ペイロードによる書き換えは単にプログラムをクラッシュさせる結果となる。
シェルコードの場合、ペイロードの前にNOP命令(何もしない命令)をたくさん並べておくことがある。こうすることで、ジャンプ先が予測と多少違っても攻撃が成功するようになる。しかし、16バイトぶんのNOP命令を並べても、成功確率は1/16Mから2/16Mになるだけである。また、128バイトのNOP命令を並べても、成功確率は9/16Mにしかならない。成功確率はNOP命令数に比例する。
return-to-libc攻撃はコードを書き込むわけではなく、ある固定幅のスタックフレームを書き換える。このため、スタックフレームは正確に16バイト境界に配置されていなければならない。一般にスタックフレームは16バイトよりも大きいので、スタックフレームを繰り返し書き込むことで成功確率を上げようとする。
PaX が提供するのはNXビット(あるいはそれに相当する各CPU/MMUアーキテクチャにある機能)の機能を使った(あるいはそれをOSでエミュレートした)メモリ保護機能である。また、return-to-libc攻撃や、メモリ空間配置に関する知識に依存した攻撃を防ぐため、アドレス空間配置のランダム化を行う。
トランポリンとは、GNUコンパイラコレクションで実装されているもので、コールスタック上に実行時に小さなコードを生成するものである。つまり、トランポリンはスタック上のメモリを実行可とする必要があり、PaX ではそのようなプログラムは終了させられてしまう。
トランポリンは自己書き換えコードの一種であるため、PaX にひっかかり、プログラムの終了を招く。PaX はこれを防ぐため、トランポリン生成を識別し、その実行を許可するようになっている。しかし、これはセキュリティを弱める可能性があるとされている。
PaX の主要機能は実行空間保護である。一部プロセッサにあるNXビットの機能を用いて任意のコードの実行を防ぐ。それによって、コード注入やシェルコードといった攻撃を防ぐ。NXビットを持たないIA-32系CPUでは、PaX はその機能を様々な手段でエミュレートできる。
Linuxを含む多くのOSでは、ハードウェアのNXビットの機能を利用して、メモリのアクセス権を適切に設定する。図1は、1つのロードされたライブラリのあるプログラムのメモリセグメント群を簡単に図示したものである。緑のセグメントはデータで、青のセグメントはコードである。一般に、AMD64なども含めたプロセッサでのアドレス空間は、デフォルトでは図1のように明確にデータ部とコード部が定義されている。しかし、Linux ではデフォルトではアプリケーションが自身のメモリ保護設定を変更することを防ぐことができず、コード部を書き込み可能にしたり、データ部をコードとして実行することが可能となっている。PaX はそのような変更を防ぐとともに、典型的な操作に最適な最も制限のきつい保護設定を保証する。
実行空間保護が起動しているとき、mprotect() 制限も含まれ、PaX は本来コード部でなかった領域を実行可能にするような操作を不可能にする。