当前位置: 首页 > 知识库问答 >
问题:

指针比较在64位x86中应该签名还是不签名?

颜文康
2023-03-14

所以我不确定应该如何处理它们:有符号的优点是在0附近是安全的,因为那里没有间断,而无符号的优点是在2^63附近,因为那里没有间断。然而,在实践中,您看不到任何接近2^63的地址,因为当前商品硬件的虚拟地址空间被限制在50位以下。这是指签名吗?

1...,有时堆和其他映射区域不靠近地址空间的底部或顶部。

共有1个答案

郗福
2023-03-14

TL:DR:intptr_t在某些情况下可能是最好的,因为有符号溢出边界位于“非规范洞”的中间。如果可以从零换行到0xff...ff或反之亦然,那么将值视为负值而不是巨大值可能会更好,但是任何有效大小的指针+size都不能将值从int64_max换行到int64_min

否则,您可能希望“high half”(high bit set)的无符号值与下半部分的值相比较。

这完全取决于你想知道关于两个指针的什么!

是的,对于从具有合理大小的相同对象派生的“相关”指针,有符号比较在当前硬件上是安全的,并且只能在不太可能的/遥远的未来的机器上中断,这些机器具有对完全64位虚拟地址的硬件支持。如果两个指针都在规范范围的下半部分,重叠检查也是安全的,我认为所有主流x86-64操作系统上的用户空间地址都是这样。

正如您所指出的,如果ptrb-c换行(无符号换行),则无符号ptra 可能“失败”。实际上,对于比 C大小更接近0的静态地址,可能会发生这种情况。

通常,低64kib是不可映射的(例如,在Linux上,大多数distro都附带sysctlvm.mmap_min_addr=65536,或者至少4096。但是有些系统对WINE来说=0)。不过,我认为内核不给你一个零页是正常的,除非你特别请求这个地址,因为它可以防止NULL deref出错(出于安全性和可调试性的原因,这通常是非常理想的)。

那么,它什么时候会出现带签名的比较失败呢?当ptrb-c在溢出时签名环绕时。或者,如果您曾经有指向high-half对象的指针(例如,指向Linux的vDSO页面),那么比较high-half和low-half地址可能会给您一个意想不到的结果:您将看到“high-half”地址小于“low-half”地址。即使ptrb-c计算没有换行,也会发生这种情况。

(我们只直接讨论asm,不讨论C,所以没有UB,我只是使用C表示sublea/cmp/JL。)

有符号环绕只能发生在0x7fff...0x8000...之间的边界附近。但这一界限与任何规范地址相去甚远。我将从另一个答案中重现x86-64地址空间的图表(对于虚拟地址为48位的当前实现)。请参见为什么在64bit中,虚拟地址比物理地址(52 bit长)短4 bit(48 bit长)?。

+----------+
| 2^64-1   |   0xffffffffffffffff
| ...      |                       high half of canonical address range
| 2^64-2^47|   0xffff800000000000
+----------+
|          |
| unusable |   Not to scale: this is 2^15 times larger than the top/bottom ranges.
|          |
+----------+
| 2^47-1   |   0x00007fffffffffff
| ...      |                       low half of canonical range
| 0        |   0x0000000000000000
+----------+

Intel为57位虚拟地址提出了一个5级页表扩展(即另一个9位级别的表),但这仍然使大部分地址空间不规范。也就是说,任何规范地址离带签名的环绕仍然有2^63-2^57的距离。

根据操作系统的不同,您的所有地址可能是在低的一半或高的一半。例如,在x86-64 Linux上,高(“负”)地址是内核地址,而低(带符号的正)地址是用户空间。但是请注意,Linux将内核vDSO/vsyscall页面映射到靠近虚拟地址空间顶部的用户空间中。(但是它在顶部保留了未映射的页面,例如,FFFFFFFFFF600000-FFFFFFFFF601000[vsyscall]在我的桌面上的64位进程中,但是vDSO页面靠近底部一半规范范围的顶部,0x00007FFF...。即使在理论上整个4GIB可由用户空间使用的32位进程中,vDSO也位于最高页面的下方,而MMAP(MAP_FIXET)在最高页面上不起作用。可能是因为C允许一个过去结束指针吗?)

如果在vsyscall页面中获取函数或变量的地址,可以混合使用正地址和负地址。(我不认为有人会这么做,但这是可能的。)

因此,如果没有区分正负符号的内核/用户拆分,那么有符号的地址比较可能是危险的,并且您的代码在遥远的将来运行时,/if x86-64已经扩展到完全的64位虚拟地址,因此对象可以跨越边界。后者似乎不太可能,如果你能从假设它不会发生中获得加速,这可能是一个好主意。

这意味着符号比较对于32位指针来说已经很危险了,因为64位内核使得整个4GIB可以由用户空间使用。(32位内核可以配置为3:1的内核/用户拆分)。没有不可用的规范范围。在32位模式下,对象可以跨越带符号环绕的边界。(或者在ILP32 x32 ABI中:长模式下的32位指针。)

Core2只能宏融合cmp与无符号的比较,而不能宏融合有符号的比较,但是Core2在64位模式下根本不能宏融合。(顺便说一句,它可以在32位模式下宏融合test和有符号比较。)

Nehalem可以宏融合testcmp与有符号或无符号比较(包括在64位模式下)。

资料来源:Agner Fog的microarch PDF。

 类似资料:
  • 但我会得到另一个例外 java.lang.UnsatisfiedLinkError:dlopen失败:“/data/app/com.example.user.project/lib/x86/libtracker.so”是64位而不是32位 我可以看到我的库已经成功构建,这是它在构建时显示的跟踪消息 这是我的CMakeLists 这是我的母语-lib.cpp 那么,Android studio无法从

  • 问题内容: 我有一个网络应用程序,允许用户上传pkcs12。我将pkcs12作为二进制存储在数据库中。有什么办法让我知道pkcs12中的证书是自签名的还是CA签名的? 我正在tomcat上运行Java Web应用程序,并且可以使用openssl。 问题答案: 但是,我认为还有一些更重要的事情要解决- 为什么 人们想了解自签名证书。目标是什么?解决了什么问题?在大多数情况下,可能尝试将证书分为自签名

  • 问题内容: 在Java中,是否保证int始终为32位,而长为64位,而不管体系结构是32位还是64位? 问题答案: Java是平台无关的。所以是32位,并且是64位的。

  • 比较标签用于简单的变量比较,复杂的判断条件可以用if标签替换,比较标签是一组标签的集合,基本上用法都一致,如下: <比较标签 name="变量" value="值"> 内容 </比较标签> 或 <比较标签 name="变量" value="值"> 内容 <else/> 内容2 </比较标签> TP5支持的比较标签分别是: | 标签 | 含义 | | --- | --- | | eq或者 equ

  • 请参考:http://www.kancloud.cn/manual/thinkphp/1809

  • 比较标签用于简单的变量比较,复杂的判断条件可以用if标签替换,比较标签是一组标签的集合,基本上用法都一致,如下: <比较标签 name="变量" value="值"> 内容 </比较标签> 或 <比较标签 name="变量" value="值"> 内容 <else/> 内容2 </比较标签> TP5支持的比较标签分别是: | 标签 | 含义 | | --- | --- | | eq或者 equ