ulimit -s unlimitedによる32bit ASLR無効化
一般に、ASLRはsysctlコマンドなどでカーネルパラメータを変更することにより有効化・無効化できる。
$ sudo sysctl -w kernel.randomize_va_space=2 # enable kernel.randomize_va_space = 2 $ sudo sysctl -w kernel.randomize_va_space=0 # disable kernel.randomize_va_space = 0
この操作にはroot権限が必要であり、一般ユーザの権限では変更できない。
起動時にsetarchコマンドを使うことで、特定のプロセスのみASLRを無効化する方法もある。
$ setarch i686 -R ./a.out
この操作はroot権限を必要としないが、通常の手順で起動されるプロセスには適用できない。
しかし32bit環境の場合、rlimitコマンドでスタックサイズを無限にすると、以降に起動されるプロセスすべてでASLRが無効になる。
$ ulimit -s unlimited
この操作は一般ユーザ権限でも行うことができる。
また、プログラム中でsetrlimit(RLIMIT_STACK, {RLIM_INFINITY, RLIM_INFINITY})を呼ぶことでも同じ効果が得られる。
なお、64bit環境の場合にはASLRは無効にならない。
これは、Linuxカーネルの実装によるものであり、具体的には次のコードに対応する。
static int mmap_is_legacy(void)
{
if (current->personality & ADDR_COMPAT_LAYOUT)
return 1;
if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
return 1;
return sysctl_legacy_va_layout;
}
RLIMIT_STACKがRLIM_INFINITYの場合、mmap_is_legacyはtrueとなる。 そして、mmap_is_legacyは次の箇所で呼ばれている。
/*
* This function, called very early during the creation of a new
* process VM image, sets up which VM layout function to use:
*/
void arch_pick_mmap_layout(struct mm_struct *mm)
{
mm->mmap_legacy_base = mmap_legacy_base();
mm->mmap_base = mmap_base();
if (mmap_is_legacy()) {
mm->mmap_base = mm->mmap_legacy_base;
mm->get_unmapped_area = arch_get_unmapped_area;
} else {
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
}
}
mm->mmap_baseは、mmap_is_legacyがtrueの場合mmap_legacy_base()の返り値、falseの場合mmap_base()の返り値となっている。
これらの実装は次のようになっている。
static unsigned long mmap_rnd(void)
{
unsigned long rnd = 0;
/*
* 8 bits of randomness in 32bit mmaps, 20 address space bits
* 28 bits of randomness in 64bit mmaps, 40 address space bits
*/
if (current->flags & PF_RANDOMIZE) {
if (mmap_is_ia32())
rnd = get_random_int() % (1<<8);
else
rnd = get_random_int() % (1<<28);
}
return rnd << PAGE_SHIFT;
}
static unsigned long mmap_base(void)
{
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 - mmap_rnd());
}
/*
* Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64
* does, but not when emulating X86_32
*/
static unsigned long mmap_legacy_base(void)
{
if (mmap_is_ia32())
return TASK_UNMAPPED_BASE;
else
return TASK_UNMAPPED_BASE + mmap_rnd();
}
上の内容から、mmap_is_ia32()がtrue、つまり32bit環境の場合mmap_legacy_base()が返すアドレスが固定となっていることがわかる。
また、mmap_rnd()の実装から、32bit環境では8bit、64bit環境では28bitのランダム化が行われていることがわかる。
追記(2016-11-16)
この動作はLinux kernel 4.5で修正された。