【发布时间】:2016-12-22 23:55:58
【问题描述】:
在兼容 AMD64 的架构上,地址需要采用规范形式才能被取消引用。
来自Intel manual, section 3.3.7.1:
在 64 位模式下,地址被认为是规范形式,如果 地址位 63 到最高有效位由 微架构设置为全 1 或全零。
现在,在当前操作系统和架构上实现的最重要的位是第 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 是:
0000 0000 0000 0000 0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100
将it设置为intArray并增加一次it后,考虑sizeof(int) == 4,会变成:
0000 0000 0000 0000 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
第 47 位以粗体显示。这里发生的是指针算术检索到的第二个指针无效,因为不是规范形式。正确的地址应该是:
1111 1111 1111 1111 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
程序如何处理这个问题?操作系统是否保证永远不会分配地址范围不随第 47 位变化的内存?
【问题讨论】:
-
请不要直接发布你的家庭作业问题,即使是为了掩饰这一事实,这也是显而易见的。 (仅供参考,通常不会向专家发布问题,而是从定义什么是规范开始的问题开始)
-
a) 任何使用指针标记的人都应该受到他们所遭受的所有痛苦。 b) 通常负地址(解释为负的二进制补码)是为操作系统保留的,与用户无关。
-
@Bruce 这个问题来自我与软件公司团队的另一位成员的讨论。我添加了有关规范寻址的信息,因为许多人不熟悉这个概念。我真的不明白你怎么能如此确定这是家庭作业,但你可以在直接指责我“没有努力掩饰这一事实”之前问过。我希望我有这样的功课。
标签: x86-64 access-violation memory-address pointer-arithmetic aslr