由一次虚拟内存耗尽看32bits elf在x86_64下运行方式及地址空间布局
基于2.6.32内核
一、现象
在x86_64的64位操作系统环境下,一个程序执行coredump,从coredump文件可以看到,导致coredump的问题是由于执行new操作时分配内存失败导致的。这个问题看起来让人有些莫名惊诧,用掉64bit下的所有虚拟地址空间,这是什么概念(这个惊诧可以用莱芜战役后国民党第二绥靖区司令长官王耀武的一句话来类比:“五万多人,三天就被消灭光,就是放五万头猪,叫共军抓,三天也抓不完”。而我当时的心情是“五万只草泥马"奔腾而过了)?还是现网在运行的程序。通过readelf -e查看coredump文件挂掉时内存布局,可以看到内存布局的确十分紧致,机会没有空闲的虚拟地址空间。coredump文件显示了0xfefff000位地址空间,也就是进程空间上限接近4G,下限为0x00048000。这一点和通常的32bit地址空间不同,因为32bits下用户地址空间的上限是3G。不过readelf的输出也的确是显示了进程的"Machine: Intel 80386",也就是一个在x86_64环境下运行的32bits二进制文件。
tsecer@harry: readelf corefile -e
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: CORE (Core file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 86
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
There are no sections in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NOTE 0x000af4 0x00000000 0x00000000 0x0162c 0x00000 0
LOAD 0x003000 0x00048000 0x00000000 0x8000000 0x8000000 RWE 0x1000
LOAD 0x8003000 0x08048000 0x00000000 0x01000 0x5e4000 R E 0x1000
LOAD 0x8004000 0x0862c000 0x00000000 0xf6000 0xf6000 RWE 0x1000
LOAD 0x80fa000 0x08722000 0x00000000 0x40c000 0x40c000 RWE 0x1000
LOAD 0x8506000 0x08bd8000 0x00000000 0x5443e000 0x5443e000 RWE 0x1000
LOAD 0x5c944000 0x5d01b000 0x00000000 0x01000 0x03000 R E 0x1000
LOAD 0x5c945000 0x5d01e000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0x5c946000 0x5d01f000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0x5c947000 0x5d020000 0x00000000 0x00000 0x07000 R E 0x1000
LOAD 0x5c947000 0x5d027000 0x00000000 0x4b8e000 0x4b8e000 RWE 0x1000
LOAD 0x614d5000 0x61bb5000 0x00000000 0x73000 0x73000 R E 0x1000
LOAD 0x61548000 0x61c28000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0x61549000 0x61c29000 0x00000000 0x37d7000 0x37d7000 RWE 0x1000
LOAD 0x64d20000 0x65400000 0x00000000 0x21000 0x21000 RWE 0x1000
LOAD 0x64d41000 0x65421000 0x00000000 0x00000 0xdf000 0x1000
LOAD 0x64d41000 0x65500000 0x00000000 0x21000 0x21000 RWE 0x1000
LOAD 0x64d62000 0x65521000 0x00000000 0x00000 0xdf000 0x1000
LOAD 0x64d62000 0x65600000 0x00000000 0x21000 0x21000 RWE 0x1000
LOAD 0x64d83000 0x65621000 0x00000000 0x00000 0xdf000 0x1000
LOAD 0x64d83000 0x65746000 0x00000000 0x01000 0x01000 0x1000
LOAD 0x64d84000 0x65747000 0x00000000 0x800000 0x800000 RWE 0x1000
LOAD 0x65584000 0x65f47000 0x00000000 0x01000 0x01000 0x1000
LOAD 0x65585000 0x65f48000 0x00000000 0x800000 0x800000 RWE 0x1000
LOAD 0x65d85000 0x66748000 0x00000000 0x01000 0x01000 0x1000
LOAD 0x65d86000 0x66749000 0x00000000 0xab6000 0xab6000 RWE 0x1000
LOAD 0x6683c000 0x671ff000 0x00000000 0x1001000 0x1001000 RWE 0x1000
LOAD 0x6783d000 0x68200000 0x00000000 0x21000 0x21000 RWE 0x1000
LOAD 0x6785e000 0x68221000 0x00000000 0x00000 0xdf000 0x1000
LOAD 0x6785e000 0x68301000 0x00000000 0x01000 0x10000 R E 0x1000
LOAD 0x6785f000 0x68311000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0x67860000 0x68312000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0x67861000 0x68313000 0x00000000 0x00000 0x35000 R E 0x1000
LOAD 0x67861000 0x68348000 0x00000000 0x01000 0x01000 0x1000
LOAD 0x67862000 0x68349000 0x00000000 0xab6000 0xab6000 RWE 0x1000
LOAD 0x68318000 0x68dff000 0x00000000 0x1001000 0x1001000 RWE 0x1000
LOAD 0x69319000 0x69e00000 0x00000000 0x21000 0x21000 RWE 0x1000
LOAD 0x6933a000 0x69e21000 0x00000000 0x00000 0xdf000 0x1000
LOAD 0x6933a000 0x69f02000 0x00000000 0x01000 0x01000 0x1000
LOAD 0x6933b000 0x69f03000 0x00000000 0xab6000 0xab6000 RWE 0x1000
LOAD 0x69df1000 0x6a9b9000 0x00000000 0x1001000 0x1001000 RWE 0x1000
LOAD 0x6adf2000 0x6b9ba000 0x00000000 0x1819000 0x1819000 RWE 0x1000
LOAD 0x6c60b000 0x6d1d3000 0x00000000 0x1001000 0x1001000 RWE 0x1000
LOAD 0x6d60c000 0x6e1d4000 0x00000000 0x1001000 0x1001000 RWE 0x1000
LOAD 0x6e60d000 0x6f1d5000 0x00000000 0x141000 0x141000 RWE 0x1000
LOAD 0x6e74e000 0x6f316000 0x00000000 0x679000 0x679000 RWE 0x1000
LOAD 0x6edc7000 0x6f98f000 0x00000000 0x679000 0x679000 RWE 0x1000
LOAD 0x6f440000 0x70008000 0x00000000 0x8001000 0x8001000 RWE 0x1000
LOAD 0x77441000 0x78009000 0x00000000 0x3d001000 0x3d001000 RWE 0x1000
LOAD 0xb4442000 0xb500a000 0x00000000 0x3600000 0x3600000 RWE 0x1000
LOAD 0xb7a42000 0xb860a000 0x00000000 0x3aa89000 0x3aa89000 RWE 0x1000
LOAD 0xf24cb000 0xf3093000 0x00000000 0x4406000 0x4406000 RWE 0x1000
LOAD 0xf68d1000 0xf7499000 0x00000000 0x01000 0x189000 R E 0x1000
LOAD 0xf68d2000 0xf7622000 0x00000000 0x00000 0x01000 0x1000
LOAD 0xf68d2000 0xf7623000 0x00000000 0x02000 0x02000 R E 0x1000
LOAD 0xf68d4000 0xf7625000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68d5000 0xf7626000 0x00000000 0x03000 0x03000 RWE 0x1000
LOAD 0xf68d8000 0xf7629000 0x00000000 0x01000 0x1d000 R E 0x1000
LOAD 0xf68d9000 0xf7646000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68da000 0xf7647000 0x00000000 0x01000 0x28000 R E 0x1000
LOAD 0xf68db000 0xf766f000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68dc000 0xf7670000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68dd000 0xf7671000 0x00000000 0x01000 0xdf000 R E 0x1000
LOAD 0xf68de000 0xf7750000 0x00000000 0x04000 0x04000 R E 0x1000
LOAD 0xf68e2000 0xf7754000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68e3000 0xf7755000 0x00000000 0x08000 0x08000 RWE 0x1000
LOAD 0xf68eb000 0xf775d000 0x00000000 0x01000 0x17000 R E 0x1000
LOAD 0xf68ec000 0xf7774000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68ed000 0xf7775000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68ee000 0xf7776000 0x00000000 0x02000 0x02000 RWE 0x1000
LOAD 0xf68f0000 0xf7778000 0x00000000 0x01000 0x03000 R E 0x1000
LOAD 0xf68f1000 0xf777b000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68f2000 0xf777c000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68f3000 0xf777d000 0x00000000 0x01000 0x07000 R E 0x1000
LOAD 0xf68f4000 0xf7784000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68f5000 0xf7785000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68f6000 0xf7786000 0x00000000 0x01000 0x12000 R E 0x1000
LOAD 0xf68f7000 0xf7798000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68f8000 0xf7799000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68f9000 0xf77a8000 0x00000000 0x02000 0x02000 RWE 0x1000
LOAD 0xf68fb000 0xf77aa000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68fc000 0xf77ab000 0x00000000 0x01000 0x1e000 R E 0x1000
LOAD 0xf68fd000 0xf77c9000 0x00000000 0x01000 0x01000 R E 0x1000
LOAD 0xf68fe000 0xf77ca000 0x00000000 0x01000 0x01000 RWE 0x1000
LOAD 0xf68ff000 0xf77cb000 0x00000000 0x8700000 0x8700000 RWE 0x1000
LOAD 0xfefff000 0xfff94000 0x00000000 0x3c000 0x3c000 RWE 0x1000
二、内核对于虚拟地址空间的选择
sys_mmap->sys_mmap_pgoff-->>do_mmap_pgoff-->>get_unmapped_area--->>arch_get_unmapped_area_topdown
unsigned long
arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
const unsigned long len, const unsigned long pgoff,
const unsigned long flags)
addr = mm->mmap_base-len;
do {
/*
* Lookup failure means no vma is above this address,
* else if new region fits below vma->vm_start,
* return with success:
*/
vma = find_vma(mm, addr);
if (!vma || addr+len <= vma->vm_start)
/* remember the address as a hint for next time */
return mm->free_area_cache = addr;
/* remember the largest hole we saw so far */
if (addr + mm->cached_hole_size < vma->vm_start)
mm->cached_hole_size = vma->vm_start - addr;
/* try just below the current vma->vm_start */
addr = vma->vm_start-len;
} while (len < vma->vm_start);
bottomup:
/*
* A failed mmap() very likely causes application failure,
* so fall back to the bottom-up function here. This scenario
* can happen with large stack limits and large mmap()
* allocations.
*/
mm->cached_hole_size = ~0UL;
mm->free_area_cache = TASK_UNMAPPED_BASE;
addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
/*
* Restore the topdown base:
*/
mm->free_area_cache = mm->mmap_base;
mm->cached_hole_size = ~0UL;
return addr;
#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? 0xc0000000 : 0xFFFFe000)
#define TASK_SIZE (test_thread_flag(TIF_IA32) ? IA32_PAGE_OFFSET : TASK_SIZE64)
#define TASK_SIZE_OF(child) ((test_tsk_thread_flag(child, TIF_IA32)) ? IA32_PAGE_OFFSET : TASK_SIZE64)
#define TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE/3)
三、内核对于虚拟地址空间的选择
static unsigned long mmap_base(void)
{
unsigned long gap = current->signal->rlim[RLIMIT_STACK].rlim_cur;
if (gap < MIN_GAP)
gap = MIN_GAP;
else if (gap > MAX_GAP)
gap = MAX_GAP;
return PAGE_ALIGN(TASK_SIZE - gap - mmap_rnd());
}
/* This decides where the kernel will search for a free chunk of vm
* space during mmap's.
*/
#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? \
0xc0000000 : 0xFFFFe000)
#define TASK_SIZE (test_thread_flag(TIF_IA32) ? \
IA32_PAGE_OFFSET : TASK_SIZE_MAX)
#define TASK_SIZE_OF(child) ((test_tsk_thread_flag(child, TIF_IA32)) ? \
IA32_PAGE_OFFSET : TASK_SIZE_MAX)
四、32bits executable的TASK_SIZE的选择
tsecer@harry: readelf -h a.out
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048370
Start of program headers: 52 (bytes into file)
Start of section headers: 3480 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 36
Section header string table index: 33
tsecer@harry: hexdump -C a.out | head
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 02 00 03 00 01 00 00 00 70 83 04 08 34 00 00 00 |........p...4...|
从0x12开始的03表示的就是Intel 80386。内核中对于这种运行在32位elf格式的加载代码在compat_binfmt_elf.c中
#undef ELF_ARCH
#undef elf_check_arch
#define elf_check_arch compat_elf_check_arch
……
#ifdef COMPAT_SET_PERSONALITY
#undef SET_PERSONALITY
#define SET_PERSONALITY COMPAT_SET_PERSONALITY
#endif
相关宏定义展开之后
#define compat_elf_check_arch(x) elf_check_arch_ia32(x)
#define elf_check_arch_ia32(x) \
(((x)->e_machine == EM_386) || ((x)->e_machine == EM_486))
#define EM_386 3
#define EM_486 6 /* Perhaps disused */
#define COMPAT_SET_PERSONALITY(ex) set_personality_ia32()
……
void set_personality_ia32(void)
{
/* inherit personality from parent */
/* Make sure to be in 32bit mode */
set_thread_flag(TIF_IA32);
current->personality |= force_personality32;
/* Prepare the first "return" to user space */
current_thread_info()->status |= TS_COMPAT;
}
binfmt_elf.c
static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
……
/* First of all, some simple consistency checks */
if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out;
if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
goto out;
if (!elf_check_arch(&loc->elf_ex))
goto out;
if (!bprm->file->f_op||!bprm->file->f_op->mmap)
goto out;
……
/* Do this immediately, since STACK_TOP as used in setup_arg_pages
may depend on the personality. */
SET_PERSONALITY(loc->elf_ex);
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
current->personality |= READ_IMPLIES_EXEC;
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
current->flags |= PF_RANDOMIZE;
结合
#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? \
0xc0000000 : 0xFFFFe000)
#define TASK_SIZE (test_thread_flag(TIF_IA32) ? \
IA32_PAGE_OFFSET : TASK_SIZE_MAX)
我们知道进程堆栈上限为0xFFFFe000,但是由于load_elf_binary函数中的
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
current->flags |= PF_RANDOMIZE;
我们看到的最高地址并不是严格的这个值,
static unsigned long randomize_stack_top(unsigned long stack_top)
{
unsigned int random_variable = 0;
if ((current->flags & PF_RANDOMIZE) &&
!(current->personality & ADDR_NO_RANDOMIZE)) {
random_variable = get_random_int() & STACK_RND_MASK;
random_variable <<= PAGE_SHIFT;
}
#ifdef CONFIG_STACK_GROWSUP
return PAGE_ALIGN(stack_top) + random_variable;
#else
return PAGE_ALIGN(stack_top) - random_variable;
#endif
}
堆栈随机的好处在wiki上有说明,大致来说是为了避免一些和用户交互的、有bug程序被overflow攻击导致安全问题。
五、用户态mmap的下限
unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flags, unsigned long pgoff)
……
error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
……
int cap_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags,
unsigned long addr, unsigned long addr_only)
{
int ret = 0;
if (addr < dac_mmap_min_addr) {
ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO,
SECURITY_CAP_AUDIT);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */
if (ret == 0)
current->flags |= PF_SUPERPRIV;
}
return ret;
}
/* amount of vm to protect from userspace using CAP_SYS_RAWIO (DAC) */
unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
tsecer@harry: zcat /proc/config.gz | grep MMAP_MIN_ADDR
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
tsecer@harry: cat /proc/sys/vm/mmap_min_addr
4096
六、验证
也就是说,内核只限制了4K页面不能给用户态使用,其它的都是可以的。但是简单的使用malloc没有达到这个下限:
tsecer@harry: cat testmalloc.c
#include <stdlib.h>
int main()
{
while(malloc(0x1000))
{
}
}
tsecer@harry: tail -15 trace.malloc.log
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x57605000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x47604000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x37603000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x27602000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x17601000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
brk(0) = 0x91c9000
brk(0x191ea000) = 0x91c9000
mmap2(NULL, 268570624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap2(NULL, 2097152, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x17401000
munmap(0x17401000, 1044480) = 0
munmap(0x17600000, 4096) = 0
mprotect(0x17500000, 135168, PROT_READ|PROT_WRITE) = 0
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
exit_group(0) = ?
tsecer@harry:
由于malloc是以268439552为最小单位mmap的,所以不能达到理论下限0x1000。
直接使用mmap
tsecer@harry: cat testmmap.c
#include <sys/mman.h>
int main()
{
while(mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) != MAP_FAILED)
{
}
}
tsecer@harry: tail -15 trace.malloc.log
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x57605000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x47604000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x37603000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x27602000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x17601000
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
brk(0) = 0x91c9000
brk(0x191ea000) = 0x91c9000
mmap2(NULL, 268570624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap2(NULL, 2097152, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x17401000
munmap(0x17401000, 1044480) = 0
munmap(0x17600000, 4096) = 0
mprotect(0x17500000, 135168, PROT_READ|PROT_WRITE) = 0
mmap2(NULL, 268439552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
exit_group(0) = ?
tsecer@harry: wc trace.malloc.
trace.malloc.4Kunit.log trace.malloc.log
tsecer@harry: wc trace.malloc.4Kunit.log
1048138 8385058 95308230 trace.malloc.4Kunit.log
tsecer@harry: tail -10 trace.malloc.4Kunit.log
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffff6000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffff7000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffff8000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffff9000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffffa000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffffb000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffffc000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffffffffffd000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
exit_group(4294967295) = ?
tsecer@harry: cat -n trace.malloc.4Kunit.log | grep -v 0xffff | grep 0x1000 -w
1013321 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x1000
tsecer@harry:
这里看到的问题是mmap返回的起始地址的确可以达到一个页面,另一方面验证了arch_get_unmapped_area_topdown的执行逻辑首先是从高地址到低地址查找空闲内存地址空间,在失败之后再通过arch_get_unmapped_area自低地址向高地址开始查找。