2

这是 Linux 内核函数的编译器输出(使用 编译-mno-red-zone):

load_balance:
.LFB2408:
        .loc 2 6487 0
        .cfi_startproc
.LVL1355:
        pushq   %rbp    #
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp      #,
        .cfi_def_cfa_register 6
        pushq   %r15    #
        pushq   %r14    #
        pushq   %r13    #
        pushq   %r12    #
        .cfi_offset 15, -24
        .cfi_offset 14, -32
        .cfi_offset 13, -40
        .cfi_offset 12, -48
        movq    %rdx, %r12      # sd, sd
        pushq   %rbx    #
.LBB2877:
        .loc 2 6493 0
        movq    $load_balance_mask, -136(%rbp)  #, %sfp
.LBE2877:
        .loc 2 6487 0
        subq    $184, %rsp      #,
        .cfi_offset 3, -56
        .loc 2 6489 0
     ....

注意编译器已经溢出到堆栈之后的“subq $184, %rsp” (溢出是疯狂的,顺便说一句,因为它溢出了一个常量值!)

Linus 2 天前向 gcc 报告了这个错误。但我不明白错误是什么。 为什么这是subq错的?

编辑:错误报告在这里:抱歉在 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61904之前没有包含这个

4

2 回答 2

3

我不明白为什么这subq是错误的?

问题是它相对于movq $load_balance_mask, -136(%rbp)指令的顺序。通过subq修改堆栈指针在堆栈上分配空间,并movq写入该分配区域内的位置。但在这种情况下,它movq出现在 之前subq,即它正在写入(到目前为止)未分配的堆栈空间。现在如果在 和 之间发生中断,movq并且subq中断处理程序试图触及堆栈的同一区域怎么办?结果可能会发生各种奇怪的事情,其中​​大部分可能是坏事。

movq在存在红色区域的情况下,拥有第一个就可以了。引用维基百科:

红色区域是内存中超出堆栈指针但尚未“分配”的固定大小区域。这个内存区域不能被中断/异常/信号处理程序修改。这允许将空间用于临时数据,而无需修改堆栈指针的额外开销。x86-64 ABI 要求一个 128 字节的红色区域。

然而,正如 Linus 在关于这个错误的电子邮件线程中所写的那样:“但是我们使用 -mno-red-zone 构建内核。我们*不*遵循 x86-64 ABI wrt redzoning”
并且在禁用红色区域的情况下,不应允许代码生成器movqsubq.

于 2014-07-30T08:05:06.037 回答
1

我认为那里没有问题。常量并没有真正溢出,它正在初始化一个局部变量。红色区域是堆栈指针下方的 128 个字节,因此-136(%rbp)在限制范围内,因为在五次推送之前的rbprsp将其递减 40。允许编译器随时调整rsp。这也可能是一种alloca调用。

您可以提供链接或至少提供错误报告的摘要。我在 gcc bugzilla 中找不到任何相关内容。原始的 C 源代码也很有用。

于 2014-07-29T21:35:14.113 回答