You're allocating a contiguous chunk of virtual memory, and that's the biggest hole in the address space.
(Needless to say, the address space of your program is 48 bits, i.e. 256TiB.)
当您的进程运行时,您可以通过查看/proc/self/maps
(或/proc/<pid>/maps
)来看到这一点.下面是一个非常懒惰的测试用例,它直接调用system()
:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
int main(void)
{
char *chars = NULL;
size_t nbytes = 0;
char *prev_chars = NULL;
size_t prev_bytes = 0;
while(1)
{
prev_chars = chars;
prev_bytes = nbytes;
nbytes += 0x10000000000; // 1TB
chars = mmap(
NULL,
nbytes,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1,
0
);
if(chars == MAP_FAILED)
{
break;
}
munmap(chars, nbytes);
}
printf("Largest successful allocation: %p %zx\n", prev_chars, prev_bytes);
printf("/proc/self/maps:\n");
char buf[32];
snprintf(buf, sizeof(buf), "cat /proc/%u/maps", getpid());
system(buf);
return EXIT_FAILURE;
}
如果我用cc -o t t.c -Wall -O3
编译它并运行它,那么我会得到以下输出:
Largest successful allocation: 0xaad16a0000 aa0000000000
/proc/self/maps:
aaaad16a0000-aaaad16a1000 r-xp 00000000 103:02 391637 /tmp/t
aaaad16bf000-aaaad16c0000 r--p 0000f000 103:02 391637 /tmp/t
aaaad16c0000-aaaad16c1000 rw-p 00010000 103:02 391637 /tmp/t
aaaae67e1000-aaaae6802000 rw-p 00000000 00:00 0 [heap]
ffff88150000-ffff882d7000 r-xp 00000000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882d7000-ffff882ec000 ---p 00187000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882ec000-ffff882f0000 r--p 0018c000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882f0000-ffff882f2000 rw-p 00190000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882f2000-ffff882ff000 rw-p 00000000 00:00 0
ffff88317000-ffff8833e000 r-xp 00000000 103:02 132368 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff88350000-ffff88352000 rw-p 00000000 00:00 0
ffff88352000-ffff88354000 r--p 00000000 00:00 0 [vvar]
ffff88354000-ffff88355000 r-xp 00000000 00:00 0 [vdso]
ffff88355000-ffff88357000 r--p 0002e000 103:02 132368 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff88357000-ffff88359000 rw-p 00030000 103:02 132368 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
fffff69e0000-fffff6a01000 rw-p 00000000 00:00 0 [stack]
作为参考,我在MacOS主机下的arm64 Debian VM上运行这段代码,但这在这里几乎无关紧要.
您在这里可以看到,正在使用的最低地址是0xaaaad16a0000
,其中映射了进程本身的主要二进制文件的段.这是地址0x0
的170到171TiB之间的某个地方.紧随其后的是一些堆内存,然后是另一个很大的缺口,直到0xffff88150000
,其中映射了libc.这一差距构成了另外85至86TiB的差距.
如果您分别请求170TiB和85TiB的两个单独分配,则这两个调用都应该成功,从而使您的总分配达到255TiB.但是您不能获得连续的映射,因为地址空间的中间有东西.
当然,这是ASLR的错.如果您运行上面的代码,地址在不同的运行中会有所不同,但是映射主二进制的地址应该始终以0xaaaa
开头,然后再跟8位数字.为了获得一致的结果,您可以通过使用setarch $(uname -m) -R ./[binary_name]
调用ASLR来关闭测试二进制文件的ASLR.那么您的二进制代码应该始终映射到0xaaaaaaaa0000
.
但它仍然会位于地址空间的中间.如果你想改变这一点,你需要用-no-pie
来编译.如果对上面的代码执行该操作并运行它,您将看到现在可以获得更大的连续分配:
Largest successful allocation: 0xff93880000 ff0000000000
/proc/self/maps:
00400000-00401000 r-xp 00000000 103:02 391637 /tmp/t
0041f000-00420000 r--p 0000f000 103:02 391637 /tmp/t
00420000-00421000 rw-p 00010000 103:02 391637 /tmp/t
0a425000-0a446000 rw-p 00000000 00:00 0 [heap]
ffff93880000-ffff93a07000 r-xp 00000000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a07000-ffff93a1c000 ---p 00187000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a1c000-ffff93a20000 r--p 0018c000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a20000-ffff93a22000 rw-p 00190000 103:02 132374 /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a22000-ffff93a2f000 rw-p 00000000 00:00 0
ffff93a53000-ffff93a7a000 r-xp 00000000 103:02 132368 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff93a8c000-ffff93a8e000 rw-p 00000000 00:00 0
ffff93a8e000-ffff93a90000 r--p 00000000 00:00 0 [vvar]
ffff93a90000-ffff93a91000 r-xp 00000000 00:00 0 [vdso]
ffff93a91000-ffff93a93000 r--p 0002e000 103:02 132368 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff93a93000-ffff93a95000 rw-p 00030000 103:02 132368 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
fffff6806000-fffff6827000 rw-p 00000000 00:00 0 [stack]
其中0xff0000000000
是255TiB.您可以在此基础上再次添加setarch $(uname -m) -R
以防止堆地址随机化,但在这一点上,这几乎无关紧要.
当然,这些东西取决于内核和使用的工具链的任意缺省值,所以很可能这篇文章中没有一个是guaranteed的--但在这个时间点上,它是这样工作的.