【问题标题】:0xDEADBEEF equivalent for 64-bit development?0xDEADBEEF 相当于 64 位开发?
【发布时间】:2009-08-11 01:24:19
【问题描述】:

用于 32 位系统的 C++ 开发(无论是 Linux,Mac OS 或 Windows,PowerPC 或 x86)我已经初始化了指针 否则将是未定义的(例如,他们不能立即 得到一个合适的值)像这样:

int *pInt = reinterpret_cast<int *>(0xDEADBEEF);

(为了节省打字和成为DRY,右侧通常会 保持不变,例如BAD_PTR。)

如果 pInt 在获得正确值之前被取消引用,那么 它会在大多数系统上立即崩溃(而不是 当某些内存被覆盖或消失时,会在很久以后崩溃 进入一个很长的循环)。

当然,行为取决于底层 硬件(从奇数中获取 4 字节整数 来自用户进程的地址 0xDEADBEEF 可能是完美的 有效),但是对于所有的崩溃都是 100% 可靠的 到目前为止我开发的系统(Mac OS 68xxx,Mac OS PowerPC, Linux Redhat Pentium, Windows GUI Pentium, Windows 控制台奔腾)。例如在 PowerPC 上它是非法的(总线 fault) 从一个奇数地址获取一个 4 字节整数。

这在 64 位系统上有什么好的价值?

【问题讨论】:

  • 我见过一些系统,其中前 1K 内存被定义为无效。因此,如果取消引用 NULL 指针,该进程将很快死亡。 0xDEADBEEF 可能是一个有效的位置。
  • @Robert:我见过中断向量从 0 开始的系统,因此取消引用 NULL 函数指针似乎只是重新启动系统(但不会重新初始化堆栈等)。任何地址都可能是某物的有效位置。
  • @bk1e:IVT 应该从不可以从用户模式访问。但是您是正确的,因为没有理由无法映射地址 0。在 Linux 中,通过更改内核中的选项很容易映射到地址 0。无论如何,这里学到的教训是不要使用愚蠢的模式将指针标记为无效,使用null 或结构中的单独标志。假设它会崩溃是完全不负责任和无知的,如果你幸运的话,你只会得到一个段错误,这很可能会导致远程代码执行,并且过去已经有很多很多次了。
  • @bk1e,但 C 标准保证 NULL(以及分别转换为指针的 0)将是您无法取消引用的无效指针。
  • @iconiK:“不能取消引用”是什么意思?您当然可以取消引用 NULL,但结果是未定义的行为(根据 C99 第 6.5.3.2 节)。空指针保证与任何对象或函数比较不相等(根据 C99 第 6.3.2.3 节),因此如果地址 0 处存在 C 对象或 C 函数,编译器应该将 (void*)0 转换为指向除地址 0。但是,中断向量表不是 C 对象或 C 函数,所以我认为编译器没有义务保证 NULL 不指向中断向量表。

标签: c++ debugging 64-bit defensive-programming


【解决方案1】:

0xBADC0FFEE0DDF00D

【讨论】:

  • 这个特殊的优点是你需要 yottabytes 的内存才能真正使这个值成为一个合理的值,因为内核堆在增长,而用户堆却在增长。
  • @Joshua: 1. 我可以在任何我喜欢的地方映射我的页面。 2. 这是一个非常危险的假设(在 x86 设置上您当前的操作系统可能确实如此,但这取决于实现),如果堆栈从 0xBADCOFFEE0DFOOD-0x1000 开始怎么办?堆栈已经从随机位置开始,它这样做是为了尝试使利用更加困难,除此之外仅出于技术原因。
  • 你是怎么得出这个数字的?
【解决方案2】:

According to Wikipedia,BADC0FFEE0DDF00D 用于 IBM RS/6000 64 位系统,表示未初始化的 CPU 寄存器。

【讨论】:

    【解决方案3】:

    当前大多数 64 位系统只允许您使用地址空间的最低 248–252 位;地址的高位必须全为零。某些芯片(例如 amd64)还允许您使用最高 248–252。这些范围之外的地址永远不能映射到可访问的内存;硬件根本不允许。

    因此,我建议您使用接近 263 的值,这与任何一个可能可用的空间都相去甚远。如果前四位十六进制数字是 7ff8,则该值将是双精度浮点 NaN,这很方便。所以我建议的可爱的十六进制短语是 0x7FF8BADFBADFBADF。

    顺便说一句,你真的不想使用接近 0 的值,因为这使得很难从毒模式的取消引用。

    【讨论】:

    • 那个«最新的 64 位系统只允许您使用地址空间的最低 48-52 位»并且«硬件根本不允许它»。
    • 每个感兴趣的 CPU 的架构手册会告诉你有多少位地址空间是可用的。例如,AMD64 Architecture Programmer's Manual, Volume 2 (System Programming) 在 1.1.3 节中说,并非所有 64 位地址空间都必须可用,不可用的高位必须全为零或全一,然后在 5.3.3 节中向您展示当前的实现只允许使用低 48 位。
    • 我不知道有任何 overview 列出了具有 64 位寻址的 CPU 及其可用地址范围,但我的唯一例外我能找到的原始断言是散列页表模式下的 PowerPC。对于树形结构的页表,每增加 6 到 8 位的虚拟地址空间就会增加一个树级别,并使 TLB 未命中速度变慢,因此架构设计人员不愿意提供比人们实际需要更多的可用空间,目前是 48-52 位(除了异国情调的single address space operating systems)。
    【解决方案4】:

    通常,确切您编写什么模式并不重要,重要的是您可以识别该模式以确定问题发生在哪里。碰巧的是,在 Linux 内核中,通常会选择这些地址,以便在取消引用地址时将它们捕获。

    查看include/linux/poison.h 的 Linux 内核。该文件包含许多不同内核子系统的不同毒值。没有一个合适的毒值。

    此外,您还可以查看 Linux 内核源代码树中的每个架构包含文件,以获取有关特定架构所使用内容的信息。

    【讨论】:

      【解决方案5】:

      我假设您已经打折了 NULL(即 0 没有类型转换)。这绝对是最安全的选择,因为理论上,一个有效的指针可以指向内存地址 0xDEADBEEF(或任何其他非 NULL 内存地址)。

      【讨论】:

      • 对于指针,应始终使用 NULL/0。 0xDEADBEEF 和其他十六进制值用于填充由堆管理器函数处理的最近分配但未初始化的、最近释放的、不应访问的内存区域(这也可能是真的对于堆栈)...
      • 没有人见过 NULL 的内存映射,因为编译器保证 NULL 内存位置永远不会被使用:“......一个空指针绝对没有指向任何地方;它不是任何对象的地址或功能。”见:c-faq.com/null/null1.html
      • @Mike Koval,这根本不是真的。我已将内存映射为 NULL。事实上,这并不少见,快速谷歌搜索“空指针取消引用利用”应该会给你很多例子。这看起来可能有一些高级信息:blog.cr0.org/2009/06/bypassing-linux-null-pointer.html
      • @Mike:确实如此,但是 C 规范没有定义尝试取消引用 NULL 指针的行为。因此,平台接受对 *NULL 的读取和写入是合法的(尽管不典型)。
      • NULL/0 指针在内存中不一定表示为 0 字节。虽然我不知道有什么不同的系统。
      【解决方案6】:

      0xDEADBEEFBAADF00D 可能会起作用。

      【讨论】:

        【解决方案7】:

        我没有给你一个好的选择,但这里有一个list of hex words,你可以用它来制作你的短语。

        【讨论】:

          【解决方案8】:

          我认为两个 0xDEADBEEF 应该足够了..

          【讨论】:

            【解决方案9】:

            我看到几个答案声称 NULL 是一个不错的选择,但我不同意。

            NULL 通常用作函数的有效返回值。它表示失败返回或未知值。这与“未初始化的指针”含义不同。

            在代码上使用调试器并看到 NULL 会留下两种可能性:指针从未初始化或内存分配失败。

            将未初始化的指针设置为 0xDEADBEEF 或 64 位等效值意味着 NULL 指针表示有意的值。

            【讨论】:

            • 仅当您需要未初始化的指针时才适用。这就是我不同意的地方:除非你做嵌入式的东西或其他对性能非常敏感的东西,否则初始化指向NULL 的指针是不可能的,在非常极少数情况下你需要一个指针无法将其初始化为合理的值,将其初始化为 NULL 可以解决问题。我认为多年来我不需要一个未初始化的指针。
            • "That was never initialized" 值在调试时非常有用 - 不会显式以这种方式初始化任何给定的内存位置,而是用它填充整个内存块(在malloc() 并再次以不同的模式在 free()) 上检测程序应该初始化变量但没有初始化的位置。
            【解决方案10】:

            当然,这取决于操作系统和环境。我也不认为 0xDEADBEEF 在任意 32 位系统中一定是一个坏指针。

            实际上,任何现代操作系统都应该对进程内存的前几页进行访问保护,因此 NULL 应该是一个很好的无效指针值。方便的是,它已经为您预先定义了。

            【讨论】:

            • 同意 NULL 是一个非常有用的选择。 0xDEADBEEF 不是无效指针,但它不仅在实践中极不可能,而且它没有在 4 或 2 字节边界上对齐。除了作为 char* 或可能的 bool* 之外,它不会自然发生,除非你有一些严重混乱的内存语义。
            • 无论如何它几乎总是在内核内存映射区域中。
            【解决方案11】:

            0x42 可以在 32 位和 64 位上运行吗? (它仍然应该触发崩溃,因为它与 NULL 指针足够接近,并且鉴于它相当大,您可能不会在结构指针为 NULL 的结构字段的常规取消引用中使用它。

            【讨论】:

            • 其实并没有那么大...有很多超过 66 字节的结构。
            【解决方案12】:

            由于我做的系统基本运行在x86_64平台上,所以我使用的值是:

            0xDEADBEEFDEADBEEF
            

            原因是:

            • 在 x86_64 平台上,当前实现中只有低 48 位用于虚拟内存地址,这意味着任何大于 2^48 的值都可以工作:https://en.wikipedia.org/wiki/X86-64
            • 由于 0xDEADBEEF 在 32 位中已为此目的而广为人知,因此 64 位中的 0xDEADBEEFDEADBEEF 更加“向后兼容”

            【讨论】:

              猜你喜欢
              • 2013-09-22
              • 1970-01-01
              • 2011-04-23
              • 1970-01-01
              • 1970-01-01
              • 2011-07-31
              • 1970-01-01
              • 1970-01-01
              • 2010-12-23
              相关资源
              最近更新 更多