堆栈粉碎不火。为什么?

时间:2013-10-18 09:54:02

标签: c++ c glibc stack-smash

我正在尝试让glibc检测到堆栈粉碎,我使用以下代码:

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

 static const int n = 5;

 int main(int argc, char *argv[])
 {
  if (argc != 2)
  {
     printf("usage: %s string\n", argv[0]);
     return -1;
  }
  printf("%s, len = %d\n", argv[1], strlen(argv[1]));
  unsigned char a[n][n];
  unsigned char * b = a[n - 1];
  memcpy(b, argv[1], (strlen(argv[1]) + 1) * sizeof(unsigned char));
  return 0;
 }

如果argv [1]长度大于5,我希望检测到堆栈粉碎错误,但是,我没有,并且valgrind检测到没有错误。我应该改变什么来得到这个错误? (数组a必须是二维的)

2 个答案:

答案 0 :(得分:3)

默认情况下,GCC只会添加代码来检测堆栈粉碎,当您执行特别危险的操作时,如alloca(或gets,如您在评论中提到的那样),或者声明一个大型自动数组。 / p>

如果要为所有功能启用保护,请使用-fstack-protector-all选项。您还可以使用-Wstack-protector请求有关不受保护的功能的警告。

答案 1 :(得分:1)

似乎gcc中决定何时启用堆栈保护的逻辑有点棘手。来自文档的第一个注释:

  

-fstack保护器

     

发出额外的代码来检查缓冲区溢出,例如堆栈粉碎攻击。这是通过向具有易受攻击对象的函数添加保护变量来完成的。这包括调用alloca的函数,以及缓冲区大于8字节的函数。输入功能时会初始化防护装置,然后在功能退出时进行检查。如果防护检查失败,则会打印错误消息并退出程序

因此,我们应该期望一个小于8字节的本地缓冲区的函数不受保护。例如,这个:

int unprotected() {
    char a[5];
    strcpy(a, "this is much too long");
    return a[0];
}

使用gcc -fstack-protector -Wstack-protector编译,会发出类似

的警告
  

警告:堆栈保护器不保护功能:所有本地阵列长度小于8个字节[-Wstack-protector]

因此,您可能认为您的char[5][5]将受到保护,因为它长度超过8个字节。但是,当我将其编译为汇编程序时,我没有收到警告堆栈保护(您可以在this Dr. Dobbs article中找到要查找的汇编程序)。似乎gcc将其视为5个缓冲区,每个缓冲区为5个字节,而不是25个字节的单个缓冲区。

您可以通过向gcc显示大于8字节的单个缓冲区来说服gcc启用堆栈保护:

void protected(char *arg) {
    union {
        char dummy[5 * 5];
        char a[5][5];
    } u;
    memcpy(u.a[4], arg, (strlen(arg) + 1));
}

或只使用-fstack-protector-all