プロセスごとのデフォルトのスタックサイズ制限は8MBで、mmap_baseはスタックサイズとrlimitの任意の値に基づいて計算されることがわかります。以下のコードは、x86(linux/include/uapi/asm-generic/resource.h)でmmap_baseアドレスを計算するmmap_base関数です。
static unsigned long mmap_base(unsigned long rnd)
{
unsigned long gap = rlimit(RLIMIT_STACK);
if (gap < MIN_GAP)
gap = MIN_GAP;
else if (gap > MAX_GAP)
gap = MAX_GAP;
return PAGE_ALIGN(TASK_SIZE - gap - rnd);
}
プログラムスタックサイズが8MB + rndの値より大きい場合はどうなるか知りたいです。スタックサイズがmmap_base以上に大きくなるとどうなりますか? 8MB以上のスタックメモリを割り当てると、分割エラーで失敗しますか?カーネルが自動的にスタックサイズを拡張すると、mmap_baseの内容を別のスペースに移動できますか?
答え1
プロセスのデフォルトスレッドスタックサイズは、設定された制限より大きくすることはできません。この制限のデフォルト値は8MBです。この制限を超えると、分割エラーが発生し、プロセスに信号が渡され、SIGSEGV
デフォルトで終了します。ulimit -s
プログラムを開始する前にスタックの最大サイズを変更できます。プログラムの起動後、カーネルはメモリ領域(mmap領域など)から移動せず、そうすることもできません。通常、その領域を指すポインタは、移動後に誤ったアドレスを指すためです。
ただし、スタックメモリにアクセスするとスタックオーバーフローチェックが行われるため、スタックへの大規模な割り当てやスタックポインタの値の変更が必ずしもエラーになるわけではありません。
この動作を悪用する可能性は、2017年夏に議論されました。一部の攻撃者がプログラムをだまして大量のメモリを割り当てることができる場合、スタックポインタは保護された領域をスキップして有効ですが、他の領域を指す可能性があります。これにより、プロセスを制御するためのいくつかの巧妙なトリックを使用する機会が開きます。バラよりこのlwn.netの記事この問題を議論しましょう。