用于调试的未初始化内存的常用值是什么?

What are common values for uninitialized memory for debugging?

本文关键字:常用值 是什么 内存 初始化 调试 用于      更新时间:2023-10-16

很久以前,我学会了用0xDEADBEEF填充未使用/未初始化的内存,以便在调试器或崩溃报告中,如果我看到该值,我知道我正在查看未初始化的内存。我从崩溃报告中看到iOS使用0xBBADBEEF

人们还使用了哪些创造性的价值?有什么特定的价值有什么特定的好处吗?

将值转换为单词的最明显的好处是,至少对大多数人来说,如果这些单词是他们的语言,它们很容易突出,而一些严格的数字值则不太可能突出。

但是,也许选择数字还有其他原因?例如,奇数可能会使处理器崩溃(68000),例如在某些内存访问上,因此选择0x0BADBEEF可能比0xBADBEEF0更好。它们是否有任何其他值(可能是特定于处理器的)对未初始化内存有具体的好处?

一般来说,您想要的值在解释为整数、指针或字符串时不太可能"工作"。这里有一些约束:

  • 不要使用目标架构中最小"通常"对齐倍数的值。对于x86,它是4(字节),所以没有能被4整除的值。这确保了如果值被解释为指针,它将是明显不正确的。如果您在非x86架构上,您甚至可以使用一个值,如果将其用作指针,将导致对齐陷阱。

  • 不要使用合理的小(正或负)整数的值。在C程序中,典型的"int"变量永远不会大于1000左右,所以不要使用小的数字作为空数据填充。

  • 不要使用完全由有效ASCII字符组成的值。确保至少有一个字节设置了高位。现在,您需要确保它们不是有效的UTF-8或可能的UTF-16值。

  • 值中不包含任何零字节。有太多的情况下,这将被证明是"有助于"防止程序崩溃-终止字符串,给非int字段一个合理的值,等等。

  • 不要使用单个(或两个)字节值,反复重复。使用全字长度模式可以更容易地确定野指针最终指向的位置,至少可以缩小从模式开始偏移它的操作范围。

  • 不要使用映射到"典型"进程的有效地址的值。如果设置了最高位,通常需要大量的malloc()才能使进程变得足够大,使其成为一个有效的地址。

也许不足为奇,像0xDEADBEEF这样的模式基本上满足了所有这些要求。

这类值的一个专业术语是"有毒值"。

组成英语单词的十六进制数字称为Hexspeak。维基百科的Hexspeak文章几乎回答了这个问题,编目了许多已知的常量用于各种事情,包括一些用作有毒值/金丝金丝鸟/完整性检查,以及其他用途,如错误代码或IPv6地址。

我似乎记得0xBADF00D的一些变化。(可能是重复的字母,就像你的第二个例子)。

还有0xDEADC0DE。(谷歌一下我在哪里见过这种用法,找到了上面链接的维基百科文章)。


我见过的其他十六进制英语单词:Java .class文件使用0xCAFEBABE作为幻数(文件的前4个字节)。在此基础上,我猜Jikes JVM使用0xDEADBABE作为一个完整性检查常量。

显然Java不是0xCAFEBABE的第一个用户。维基百科上说:"NeXTSTEP最初是由NeXTSTEP的开发者创造的,用来指代皮特咖啡公司的咖啡师。在他们想到"Java"这个名字之前,开发Java 的人就使用了这个词。所以它并不是来自于Java ->咖啡(如果有什么相反的话),它只是一种古老的非女权主义的科技文化。(


re: update: 选择一个好的值。对于有害值(不是错误代码),您希望所有字节都是不同的,而不是0x000xFF,因为这些可能是错误的单字节存储最有可能的值。这尤其适用于像堆栈金丝雀(检测缓冲区溢出)这样的事情,或者其他检测它没有被覆盖很重要的情况。

你关于选择奇数值的猜测很有意义。在典型进程的虚拟内存布局中不是一个有效的内存地址是一个很大的优势。尽可能早地出现故障是调试的最佳选择。无论如何,这可能意味着设置高位是一个好主意,所以0x0...可能不是一个好主意。