神秘的记忆管理

时间:2015-07-05 09:12:34

标签: c security compiler-optimization

我正在玩简单的缓冲区溢出。但是,我发现这样的编译器行为非常有趣:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(char *arg1) {
    int authenticated = 0;

    char buffer[4];

    strcpy(buffer, arg1);

    if(authenticated) {
        printf("HACKED !\n");
    } else {
        printf("POOR !\n");
    }
    return;
}

int main() {
    char* mystr = "abcdefghijkl";
    func(mystr);
    printf("THANK YOU!\n");
    return 0;
}

让我怀疑的是,我需要为arg1分配13个元素的缓冲区,而不是5个元素,以覆盖认证变量。

GDB确认:

(gdb) print &authenticated 
$1 = (int *) 0x7fffffffe75c
(gdb) print &buffer
$33 = (char (*)[4]) 0x7fffffffe750

地址之间的差异为12。 为什么在这种情况下编译不是最优的?

在重构这个函数的情况下,差异在变化,但为什么不总是差异为4,这似乎是最佳解决方案。

谢谢

2 个答案:

答案 0 :(得分:3)

变量之间的距离比您预期的要大,因为它们是alignedoptimize performance。某些操作要求变量的内存位置是某个数字的整数倍(通常是变量的大小)。因此,例如,8字节double可以放在内存中的0x1000位置,或0x1008,但不是0x1004。这里是你的堆栈最终看起来如何(没有优化等),数字表示从堆栈底部的偏移量:

-16: char[] buffer (4 bytes)
-12: padding (8 bytes)
 -4: int authenticated (4 bytes)

int可以理解为以4个字节对齐,但为什么char缓冲区对齐为16个字节?为了能够利用SSE instructions进行字符串操作。这些需要16位存储器对齐。编译禁用SSE的程序(-mno-sse与gcc)导致此布局:

 -8: char[] buffer (4 bytes)
 -4: int authenticated (4 bytes)

这样就可以确认额外的填充是由于SSE造成的。

答案 1 :(得分:1)

您正在调用未定义的行为。任何事情都可能发生。

优化编译器会注意到在strcpy之后没有使用缓冲区,因此可以删除strcpy操作。没有未定义的行为,它不会有任何可检测的副作用。

优化(或非优化)编译器会注意到&#34;经过身份验证的&#34;除非存在未定义的行为,否则始终为0并且永远不会更改,并且编译器始终可以假定没有未定义的行为。因此,总是打印出来是绝对没问题的!\ n&#34;。

因此,在存在未定义行为的情况下,您尝试从实验中得出的任何结论都是100%无根据的。