在Linux上嘗試在用戶空間中測試Is it allowed to access memory that spans the zero boundary in x86?時(shí),我寫了一個(gè)32位測試程序,試圖映射32位虛擬地址空間的低頁和高頁.
回聲0 |后sudo tee / proc / sys / vm / mmap_min_addr,我可以映射零頁面,但我不知道為什么我不能映射-4096,即(void *)0xfffff000,最高頁面.為什么mmap2((void *) – 4096)返回-ENOMEM?
strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffe08827c10 /* 65 vars */) = 0
strace: [ Process PID=1407 runs in 32 bit mode. ]
....
mmap2(0xfffff000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0
另外,什么檢查在linux/mm/mmap.c 拒絕它,為什么這樣設(shè)計(jì)呢?這是確保創(chuàng)建指向one-past-an-object的指針不是wrap around and break pointer comparisons的一部分,因?yàn)镮SO C和C允許創(chuàng)建指向一個(gè)接一個(gè)的結(jié)尾的指針,但不允許創(chuàng)建指向?qū)ο笾獾闹羔?
我在64位內(nèi)核(Arch Linux上的4.12.8-2-ARCH)下運(yùn)行,因此32位用戶空間可以提供整個(gè)4GiB. (與64位內(nèi)核上的64位代碼不同,或者與32位內(nèi)核不同,其中2:2或3:1用戶/內(nèi)核拆分會使高頁成為內(nèi)核地址.)
我沒有試過一個(gè)最小的靜態(tài)可執(zhí)行文件(沒有CRT啟動或libc,只是asm),因?yàn)槲艺J(rèn)為這不會有所作為. CRT啟動系統(tǒng)調(diào)用中沒有一個(gè)看起來可疑.
在斷點(diǎn)處停止時(shí),我檢查了/ proc / PID / maps.首頁尚未使用.堆棧包括第二高頁面,但首頁未映射.
00000000-00001000 rw-p 00000000 00:00 0 ### the mmap(0) result
08048000-08049000 r-xp 00000000 00:15 3120510 /home/peter/src/SO/a.out
08049000-0804a000 r--p 00000000 00:15 3120510 /home/peter/src/SO/a.out
0804a000-0804b000 rw-p 00001000 00:15 3120510 /home/peter/src/SO/a.out
f7d81000-f7f3a000 r-xp 00000000 00:15 1511498 /usr/lib32/libc-2.25.so
f7f3a000-f7f3c000 r--p 001b8000 00:15 1511498 /usr/lib32/libc-2.25.so
f7f3c000-f7f3d000 rw-p 001ba000 00:15 1511498 /usr/lib32/libc-2.25.so
f7f3d000-f7f40000 rw-p 00000000 00:00 0
f7f7c000-f7f7e000 rw-p 00000000 00:00 0
f7f7e000-f7f81000 r--p 00000000 00:00 0 [vvar]
f7f81000-f7f83000 r-xp 00000000 00:00 0 [vdso]
f7f83000-f7fa6000 r-xp 00000000 00:15 1511499 /usr/lib32/ld-2.25.so
f7fa6000-f7fa7000 r--p 00022000 00:15 1511499 /usr/lib32/ld-2.25.so
f7fa7000-f7fa8000 rw-p 00023000 00:15 1511499 /usr/lib32/ld-2.25.so
fffdd000-ffffe000 rw-p 00000000 00:00 0 [stack]
是否有VMA區(qū)域沒有顯示在仍然說服內(nèi)核拒絕該地址的地圖中?我查看了linux / mm / mmapc中ENOMEM的出現(xiàn)次數(shù),但是要閱讀的代碼很多,所以也許我錯過了一些東西.保留一定范圍的高地址,還是因?yàn)樗挥诙褩E赃叺臇|西?
以其他順序進(jìn)行系統(tǒng)調(diào)用沒有幫助(但是PAGE_ALIGN和類似的宏都是小心寫入的,以避免在屏蔽之前環(huán)繞,所以這不太可能.)
完整源代碼,使用gcc -O3 -fno-pie -no-pie -m32 address-wrap.c編譯:
#include <sys/mman.h>
//void *mmap(void *addr, size_t len, int prot, int flags,
// int fildes, off_t off);
int main(void) {
volatile unsigned *high =
mmap((void*)-4096L, 4096, PROT_READ | PROT_WRITE,
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
volatile unsigned *zeropage =
mmap((void*)0, 4096, PROT_READ | PROT_WRITE,
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
return (high == MAP_FAILED) ? 2 : *high;
}
(我省略了試圖deref(int *) – 2的部分,因?yàn)樗皇窃趍map失敗時(shí)的段錯誤.) 解決方法: mmap函數(shù)最終調(diào)用do_mmap或do_brk_flags來完成滿足內(nèi)存分配請求的實(shí)際工作.這些函數(shù)依次調(diào)用get_unmapped_area.在該函數(shù)中進(jìn)行檢查以確保不能分配超出用戶地址空間限制的內(nèi)存,該限制由TASK_SIZE定義.我引用代碼:
* There are a few constraints that determine this:
*
* On Intel CPUs, if a SYSCALL instruction is at the highest canonical
* address, then that syscall will enter the kernel with a
* non-canonical return address, and SYSRET will explode dangerously.
* We avoid this particular problem by preventing anything executable
* from being mapped at the maximum canonical address.
*
* On AMD CPUs in the Ryzen family, there's a nasty bug in which the
* CPUs malfunction if they execute code from the highest canonical page.
* They'll speculate right off the end of the canonical space, and
* bad things happen. This is worked around in the same way as the
* Intel problem.
#define TASK_SIZE_MAX ((1UL << __VIRTUAL_MASK_SHIFT) - PAGE_SIZE)
#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? 0xc0000000 : 0xFFFFe000)
#define TASK_SIZE (test_thread_flag(TIF_ADDR32) ? IA32_PAGE_OFFSET : TASK_SIZE_MAX)
在具有48位虛擬地址空間的處理器上,__ VIRTUAL_MASK_SHIFT為47.
請注意,TASK_SIZE的指定取決于當(dāng)前進(jìn)程是32位32位,64位32位,64位64位.對于32位進(jìn)程,保留兩個(gè)頁面;一個(gè)用于vsyscall page,另一個(gè)用作防護(hù)頁面.實(shí)質(zhì)上,vsyscall頁面無法取消映射,因此用戶地址空間的最高地址實(shí)際上是0xFFFFe000.對于64位進(jìn)程,保留一個(gè)保護(hù)頁面.這些頁面僅保留在64位Intel和AMD處理器上,因?yàn)閮H在這些處理器上使用SYSCALL機(jī)制.
以下是在get_unmapped_area中執(zhí)行的檢查:
if (addr > TASK_SIZE - len)
return -ENOMEM;
來源:https://www./content-3-273251.html
|