C 代码的 x86 反汇编生成:orq $0x0, %(rsp)
The x86 disassembly for C code generates: orq $0x0, %(rsp)
我写了下面的C代码:
它简单地分配了一个由1000000个整数和另一个整数组成的数组,并将数组的第一个整数设置为0
我使用 gcc -g test.c -o test -fno-stack-protector
编译了这个
它给出了一个非常奇怪的反汇编:
显然它一直在循环中在堆栈上分配 4096 字节,并且每 4096 个字节与 0 进行“或”运算,然后一旦达到 3997696 字节,它就会进一步分配 2184 字节。然后它继续将第 4000000 个字节(从未分配)设置为 5.
为什么它不分配请求的全部 4000004 字节?为什么它每第 4096 个字节与 0“或”,这是一条无用的指令?
我是不是理解错了?
注意:这是使用 gcc 9.3 版编译的。 gcc 7.4 版不执行循环和“或”每 4096 个字节与 0,但它确实只分配 3997696+2184=3999880 字节但仍将第 4000000 个字节设置为 5
这里有两件事:
“no-op”OR 读和写到每个 堆栈中的页面。这些是必需的,因为堆栈通常是映射的,因此在堆栈下面有一个 guard page/pages。当保护页被触及时,堆栈会向下展开。但是,如果您触摸保护页下方的内存,则会发生 SIGSEGV。
x86-64 System-V ABI 指定128-byte red zone下方的堆栈指针。编译器也可以自由使用该区域来存储局部变量。如果将 128 添加到 3997696,您将得到 4000008。请注意,堆栈始终必须至少对齐 8,而不是 4,以便任何 int64_t 或双精度对齐(如 Peter Cordes 所述,更大的数组需要 16-byte-aligned,因此整个堆栈也需要 16 字节对齐),所以 40000004 是完全错误的!
这是针对 Stack Clash class 漏洞的缓解措施,该漏洞自 90 年代或更早时期就已为人所知,但在 2017 年才被广泛宣传。(请参阅 stack-clash.txt and this blog entry.)
如果攻击者可以安排一个具有 attacker-controlled 大小的 VLA 的函数来执行,或者可以安排一个具有大 fixed-size 数组的函数在攻击者控制数量时执行堆栈已经以其他方式使用,它们可能导致堆栈指针被调整为指向其他内存的中间,从而导致函数破坏所述内存,通常导致任意代码执行。
GCC 在此处发出的机器代码是 堆栈冲突保护 功能的一部分。它通过(粗略地)降低风险,每当将堆栈指针调整超过最小页面大小时,一次将其增量移动一个 minimum-page-sized 单位并在每次调整后访问内存。这确保了,如果至少存在一个 guard page(页面映射 PROT_NONE
),访问将出错并在调整到不相关的内存之前生成一个信号。主线程总是有保护页,默认情况下新创建的也有保护页(大小可以在 pthread 线程创建属性中配置)。
我有同样的问题,唯一可以禁用这个“奇怪的”汇编代码的标志是 -fno-stack-clash-protection
我写了下面的C代码:
它简单地分配了一个由1000000个整数和另一个整数组成的数组,并将数组的第一个整数设置为0
我使用 gcc -g test.c -o test -fno-stack-protector
它给出了一个非常奇怪的反汇编:
显然它一直在循环中在堆栈上分配 4096 字节,并且每 4096 个字节与 0 进行“或”运算,然后一旦达到 3997696 字节,它就会进一步分配 2184 字节。然后它继续将第 4000000 个字节(从未分配)设置为 5.
为什么它不分配请求的全部 4000004 字节?为什么它每第 4096 个字节与 0“或”,这是一条无用的指令?
我是不是理解错了?
注意:这是使用 gcc 9.3 版编译的。 gcc 7.4 版不执行循环和“或”每 4096 个字节与 0,但它确实只分配 3997696+2184=3999880 字节但仍将第 4000000 个字节设置为 5
这里有两件事:
“no-op”OR 读和写到每个 堆栈中的页面。这些是必需的,因为堆栈通常是映射的,因此在堆栈下面有一个 guard page/pages。当保护页被触及时,堆栈会向下展开。但是,如果您触摸保护页下方的内存,则会发生 SIGSEGV。
x86-64 System-V ABI 指定128-byte red zone下方的堆栈指针。编译器也可以自由使用该区域来存储局部变量。如果将 128 添加到 3997696,您将得到 4000008。请注意,堆栈始终必须至少对齐 8,而不是 4,以便任何 int64_t 或双精度对齐(如 Peter Cordes 所述,更大的数组需要 16-byte-aligned,因此整个堆栈也需要 16 字节对齐),所以 40000004 是完全错误的!
这是针对 Stack Clash class 漏洞的缓解措施,该漏洞自 90 年代或更早时期就已为人所知,但在 2017 年才被广泛宣传。(请参阅 stack-clash.txt and this blog entry.)
如果攻击者可以安排一个具有 attacker-controlled 大小的 VLA 的函数来执行,或者可以安排一个具有大 fixed-size 数组的函数在攻击者控制数量时执行堆栈已经以其他方式使用,它们可能导致堆栈指针被调整为指向其他内存的中间,从而导致函数破坏所述内存,通常导致任意代码执行。
GCC 在此处发出的机器代码是 堆栈冲突保护 功能的一部分。它通过(粗略地)降低风险,每当将堆栈指针调整超过最小页面大小时,一次将其增量移动一个 minimum-page-sized 单位并在每次调整后访问内存。这确保了,如果至少存在一个 guard page(页面映射 PROT_NONE
),访问将出错并在调整到不相关的内存之前生成一个信号。主线程总是有保护页,默认情况下新创建的也有保护页(大小可以在 pthread 线程创建属性中配置)。
我有同样的问题,唯一可以禁用这个“奇怪的”汇编代码的标志是 -fno-stack-clash-protection