【问题标题】:Is it possible for a binary integer operation that results in an overflow to overwrite adjacent memory?导致溢出的二进制整数运算是否有可能覆盖相邻的内存?
【发布时间】:2011-02-24 18:19:24
【问题描述】:

这个问题与我目前看到的任何错误无关,它更多的是关于理论和接受有关硬件架构设计和实施变化的教育。


场景 1: 假设 16 位处理器具有 16 位寄存器、16 位寻址和 sizeof(int) = 16 位:
unsigned int a, b, c, d;
a=0xFFFF;
b=0xFFFF;
c=a+b;

c 旁边的内存位置是否可以被覆盖?
(在这种情况下,我希望在 add 操作期间会引发溢出标志,并且c 要么保持不变,要么被未定义的数据填充。)


场景 2: 假设 32 位处理器具有 32 位寄存器,32 位寻址,sizeof(int) = 32 位,sizeof(short int)=16 位:
unsigned int a, b;
unsigned short int c, d;
a=0xFFFF;
b=0xFFFF;
c=a+b;

c 旁边的内存位置是否可以被覆盖?
(我希望在添加操作期间不会引发溢出标志,但是在分配操作期间是否引发内存访问或溢出标志将取决于硬件的实际设计和实现。如果d位于同一 32 位地址位置的高 16 位(可能甚至不可能使用 32 位寻址),它可能会被覆盖。)


场景3:假设32位处理器有32位寄存器,16位寻址,sizeof(int) = 32位,sizeof(short int)=16位:
unsigned int a, b;
unsigned short int c, d;
a=0xFFFF;
b=0xFFFF;
c=a+b;

c 旁边的内存位置是否可以被覆盖?
(我希望在类型转换和赋值操作期间会引发一些溢出标志或内存违规标志。)


场景 4: 假设 32 位处理器具有 32 位寄存器、32 位寻址和 sizeof(int) = 32 位:
unsigned int a, b;
struct {
    unsigned int c:16;
    unsigned int d:16;
} e;
a=0xFFFF;
b=0xFFFF;
e.c=a+b;

是否可以覆盖c 旁边的内存位置,即d? (在这种情况下,由于 cd 预计驻留在同一个 32 位地址中,并且两者在技术上都是 32 位整数,因此可以想象在加法或赋值期间不会引发溢出标志,并且 d可能会受到影响。)


我没有尝试在实际硬件上对此进行测试,因为我的问题更多是关于理论以及设计和实现的可能变化。任何见解将不胜感激。
  • 导致溢出的二进制整数运算是否可能覆盖相邻的内存?
  • 目前是否有任何硬件实现遇到类似的内存覆盖问题,或者过去是否有系统存在此问题?
  • 典型的处理器使用什么设备来防止相邻内存被算术和赋值操作覆盖?

【问题讨论】:

  • 这不是更多关于特定语言的实现(看起来像 C)如何处理将整数分配给较小整数类型的变量吗?
  • 我会说答案是“不,对于任何合理的架构和编程语言” - 但请参阅 The Story of Mel 以了解在旧架构上可能发生的情况 可能... :-)
  • @delnan:是的,这个问题可能在很大程度上依赖于语言,但由于所有语言在运行之前都必须转换为机器代码,因此在硬件级别上它们都有一个共同点。这些示例使用类似 C 的语法,因为我相信大多数 SO 读者最容易使用这种语法。类似汇编的语法可能更适合这些问题,但可能无助于理解我的观点......然后再一次,用机器代码构建它可能只会给我提供“啊哈!”我正在寻找的洞察力。
  • 但是所有语言,甚至 C 语言,都定义了自己的一套规则。有些人可能会指定非常严格的截断规则,而另一些人可能会未定义。

标签: memory-leaks theory overflow cpu-architecture


【解决方案1】:

您的 1,2,3,4 都不会导致任何内存损坏,或写入“过去”您正在执行算术运算的整数位置的存储。 C 指定无符号整数溢出时应该发生的情况。这是假设编译器生成它应该生成的代码。没有什么可以保护您免受错误编译器的影响,该编译器生成的代码会将 4 个字节复制到 2 个字节的变量中。

C99 (6.2.5) 是这样说的:

“涉及无符号操作数的计算永远不会溢出, 因为不能用生成的无符号整数类型表示的结果是 以比最大值大一的数字为模减少,可以是 由结果类型表示。”

因此,当您尝试“溢出”无符号整数时会发生什么,这已经很明确了。

现在,如果您的整数是有符号整数,那就另当别论了。根据 C,溢出有符号整数会导致未定义的行为。未定义的行为意味着可能发生任何事情,包括内存损坏。不过,我还没有看到一个 C 编译器会破坏有关溢出整数的任何内容。

典型的处理器使用什么设备来防止相邻内存被算术和赋值操作覆盖?

在赋值和算术运算方面,没有针对相邻内存的防范措施。处理器简单地执行给它的机器代码指令(如果这些指令覆盖了内存,它不应该像用高级语言表达的那样做,处理器不在乎)。

在稍微不同的级别上,如果 CPU 无法执行操作(例如,操作指定的内存位置不存在)或尝试执行一些非法操作(例如除以零,或遇到处理器不理解的操作码,或尝试对数据进行非对齐访问)。

【讨论】:

    【解决方案2】:

    加法运算在处理器afaik内部处理,所以无论你做什么,加法运算都将在CPU内部完成(更准确地说是在ALU中) 溢出寄存器将在溢出的情况下设置,结果仍将在寄存器中,然后复制回您的内存位置,而不会有损坏相邻内存的风险。 这就是代码在 asm 中(某种程度上)被翻译的方式:

    mov ax, ptr [memory location of a]
    mov bx, ptr [memory location of b]
    add ax,bx
    mov ptr [memory location of c], ax
    

    如您所见,无论是否发生溢出,c 都只会保存 ax 中的内容(大小已知且固定)

    【讨论】:

    • 我所知道的所有相当现代的处理器(即不到 40 年)都是这种情况。这不一定适用于所有可能的处理器(尽管 C 和 C++ 定义了无符号整数溢出,因此不可能进行额外的覆盖)。
    • 您知道不这样做的处理器吗?我从来没有见过任何人,虽然我从来没有见过超过 30 岁的人,那是在我出生之前很久 ;-)
    【解决方案3】:

    在 C 中,无符号类型的溢出行为是明确定义的。溢出导致标准预测之外的任何结果的任何实现都是不合规的。

    带符号类型的溢出行为未定义。虽然溢出的最常见影响是分配一些错误的值或彻底崩溃,但 C 标准中的任何内容都不能保证处理器不会以编译代码试图从中恢复的方式触发溢出错误,但由于某种原因,它会破坏堆栈变量或寄存器的内容。溢出也可能导致标志以代码不期望的方式设置,并且这样的标志可能会导致未来计算的错误行为。

    对于发生溢出时发生的情况,其他语言可能有不同的语义。

    注意:我见过在溢出时陷阱的处理器,与外部中断同时发生的意外陷阱可能导致数据损坏的处理器,以及意外无符号溢出可能导致后续计算关闭的处理器一。我不知道有符号溢出会在哪里锁定会干扰后续计算的标志,但是一些 DSP 具有有趣的溢出行为,所以如果存在溢出行为我不会感到惊讶。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-12
      • 2011-05-15
      • 2019-08-28
      • 1970-01-01
      • 2021-03-19
      • 2011-05-11
      相关资源
      最近更新 更多