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で修正された。