我是靠谱客的博主 细心黄蜂,最近开发中收集的这篇文章主要介绍linux内核mmap地址,为什么我不能在64位内核上mmap(MAP_FIXED)32位Linux进程中的最高虚拟页面?...,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

回声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拒绝它,为什么这样设计呢?这是确保创建指向one-past-an-object的指针不是wrap around and break pointer comparisons的一部分,因为ISO C和C允许创建指向一个接一个的结尾的指针,但不允许创建指向对象之外的指针.

我在64位内核(Arch Linux上的4.12.8-2-ARCH)下运行,因此32位用户空间可以提供整个4GiB. (与64位内核上的64位代码不同,或者与32位内核不同,其中2:2或3:1用户/内核拆分会使高页成为内核地址.)

我没有试过一个最小的静态可执行文件(没有CRT启动或libc,只是asm),因为我认为这不会有所作为. CRT启动系统调用中没有一个看起来可疑.

在断点处停止时,我检查了/ 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区域没有显示在仍然说服内核拒绝该地址的地图中?我查看了linux / mm / mmapc中ENOMEM的出现次数,但是要阅读的代码很多,所以也许我错过了一些东西.保留一定范围的高地址,还是因为它位于堆栈旁边的东西?

以其他顺序进行系统调用没有帮助(但是PAGE_ALIGN和类似的宏都是小心写入的,以避免在屏蔽之前环绕,所以这不太可能.)

完整源代码,使用gcc -O3 -fno-pie -no-pie -m32 address-wrap.c编译:

#include

//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的部分,因为它只是在mmap失败时的段错误.)

解决方法:

mmap函数最终调用do_mmap或do_brk_flags来完成满足内存分配请求的实际工作.这些函数依次调用get_unmapped_area.在该函数中进行检查以确保不能分配超出用户地址空间限制的内存,该限制由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的指定取决于当前进程是32位32位,64位32位,64位64位.对于32位进程,保留两个页面;一个用于vsyscall page,另一个用作防护页面.实质上,vsyscall页面无法取消映射,因此用户地址空间的最高地址实际上是0xFFFFe000.对于64位进程,保留一个保护页面.这些页面仅保留在64位Intel和AMD处理器上,因为仅在这些处理器上使用SYSCALL机制.

以下是在get_unmapped_area中执行的检查:

if (addr > TASK_SIZE - len)

return -ENOMEM;

标签:linux,assembly,linux-kernel,x86,mmap

来源: https://codeday.me/bug/20190627/1305483.html

最后

以上就是细心黄蜂为你收集整理的linux内核mmap地址,为什么我不能在64位内核上mmap(MAP_FIXED)32位Linux进程中的最高虚拟页面?...的全部内容,希望文章能够帮你解决linux内核mmap地址,为什么我不能在64位内核上mmap(MAP_FIXED)32位Linux进程中的最高虚拟页面?...所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部