在符合AMD64标准的体系结构中,地址需要在取消引用之前采用规范形式。
来自Intel manual, section 3.3.7.1:
在64位模式下,如果地址被认为是规范形式 地址位63到最重要的实现位 微体系结构设置为全部或全部为零。
现在,当前操作系统和体系结构中最有意义的实现位是第47位。这给我们留下了48位的地址空间。
特别是当启用ASLR时,用户程序可能会收到第47位设置的地址。
如果使用指针标记等优化并且高位用于存储信息,则程序必须确保将第48位至第63位设置回取消引用地址之前的第47位。
但请考虑以下代码:
int main()
{
int* intArray = new int[100];
int* it = intArray;
// Fill the array with any value.
for (int i = 0; i < 100; i++)
{
*it = 20;
it++;
}
delete [] intArray;
return 0;
}
现在考虑intArray
,比如说:
将it
设置为intArray
并增加it
一次,然后考虑sizeof(int) == 4
,它将成为:
第47位是粗体。这里发生的是由指针算术检索的第二个指针是无效的,因为不是规范形式。正确的地址应该是:
1111 1111 1111 1111 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
程序如何处理这个问题?操作系统是否保证永远不会分配内存范围不会因第47位而异的内存?
答案 0 :(得分:7)
规范地址规则意味着64位虚拟地址空间中存在巨大的漏洞。 2 ^ 47-1 不与其上方的下一个有效地址连续,因此单个mmap
将不包含任何不可用的64-范围位地址。
+----------+
| 2^64-1 | 0xffffffffffffffff
| ... |
| 2^64-2^47| 0xffff800000000000
+----------+
| |
| unusable |
| |
+----------+
| 2^47-1 | 0x00007fffffffffff
| ... |
| 0 | 0x0000000000000000
+----------+
换句话说:
操作系统是否保证永远不会分配内存范围不会因第47位而异的内存?
是。当前硬件支持的48位地址空间是实现细节。规范地址规则确保未来的系统可以支持更多的虚拟地址位,而不会破坏任何重要程度的向后兼容性。你只需要一个compat标志,让操作系统不给任何具有高位的内存区域,而不是完全相同。未来的硬件不需要支持任何类型的标志来忽略高地址位,因为高位中的垃圾当前是错误的。
有趣的事实:Linux默认将堆栈映射到较低范围的有效地址的顶部。
e.g。
$ gdb /bin/ls
...
(gdb) b _start
Function "_start" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_start) pending.
(gdb) r
Starting program: /bin/ls
Breakpoint 1, 0x00007ffff7dd9cd0 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) p $rsp
$1 = (void *) 0x7fffffffd850
(gdb) exit
$ calc
2^47-1
0x7fffffffffff