【问题标题】:Why is stack smashing occuring in this C code?为什么在这个 C 代码中会发生堆栈粉碎?
【发布时间】:2012-05-31 14:22:24
【问题描述】:

对于我缺乏关于 C 中堆栈粉碎的知识,我提前道歉。 我正在使用 Code::Blocks 进行 ubuntu 12.04 编辑。 我编写了一个导致堆栈粉碎的简单 C 程序,但互联网搜索几乎没有发现关于为什么会发生这种情况的有用建议。

示例 C 代码:

#include<stdio.h>

struct point3
    {float x, y, z;};

struct quadPolygon
    {struct point3 vert1, vert2, vert3, vert4;};

int writeLine(const char * objString)
    {FILE *file; file = fopen("aPlane.obj","a+"); fprintf(file,"%s",objString); fclose(file); return 0;};

int writeOBJ(struct quadPolygon myPoly)
    {
    char objString[] = "# plane def\n";  writeLine(objString);

    snprintf(objString, 128, "v %f %f %f \n", myPoly.vert1.x, myPoly.vert1.y, myPoly.vert1.z);  writeLine(objString);
    snprintf(objString, 128, "v %f %f %f \n", myPoly.vert2.x, myPoly.vert2.y, myPoly.vert2.z);  writeLine(objString);
    snprintf(objString, 128, "v %f %f %f \n", myPoly.vert3.x, myPoly.vert3.y, myPoly.vert3.z);  writeLine(objString);
    snprintf(objString, 128, "v %f %f %f \n", myPoly.vert4.x, myPoly.vert4.y, myPoly.vert4.z);  writeLine(objString);

    char objStringSmooth[] = "s off\n";  writeLine(objStringSmooth);
    char objStringFace[] = "f 1 2 3 4\n";  writeLine(objStringFace);
    return 0;
    };

int main()
{
    struct quadPolygon myPoly1 =
    {
    .vert1.x=1.0, .vert1.y=-1.0, .vert1.z=0.0,
    .vert2.x=1.0, .vert2.y=1.0, .vert2.z=0.0,
    .vert3.x=-1.0, .vert3.y=1.0, .vert3.z=0.0,
    .vert4.x=-1.0, .vert4.y=-1.0, .vert4.z=0.0
    };
    writeOBJ(myPoly1);
    return 0;
};

为什么堆栈会崩溃,我该如何更改我的代码以避免这种情况?这与在上面的代码中错误地使用指针有关吗?你可能会说我对 C 有点陌生,但对其他语言有一些编程经验。

我读过“堆栈粉碎实际上是gcc用来检测缓冲区溢出攻击的一种保护机制”和“这意味着您以非法方式写入堆栈上的某些变量,很可能是缓冲区的结果溢出”。

感谢您的任何回复/回答。

更新 - 根据 Evan 的评论,这里是修改后的代码。也许这可能对其他人有所帮助。

#include<stdio.h>

struct point3
    {float x, y, z;};

struct quadPolygon
    {struct point3 vert1, vert2, vert3, vert4;};

int writeOBJ(struct quadPolygon myPoly)
    {
    FILE *file; file = fopen("aPlane.obj","a+");
    fprintf(file,"%s","# plane def\n");
    char objString[128];
    snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert1.x, myPoly.vert1.y, myPoly.vert1.z);
        fprintf(file,"%s",objString);
    snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert2.x, myPoly.vert2.y, myPoly.vert2.z);
        fprintf(file,"%s",objString);
    snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert3.x, myPoly.vert3.y, myPoly.vert3.z);
        fprintf(file,"%s",objString);
    snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert4.x, myPoly.vert4.y, myPoly.vert4.z);
        fprintf(file,"%s",objString);
    char objStringSmooth[] = "s off\n";
        fprintf(file,"%s",objStringSmooth);
    char objStringFace[] = "f 1 2 3 4\n";
        fprintf(file,"%s",objStringFace);
    fclose(file);
    return 0;
    };

int main()
    {
    struct quadPolygon myPoly1 =
        {
        .vert1.x=1.0, .vert1.y=-1.0, .vert1.z=0.0,
        .vert2.x=1.0, .vert2.y=1.0, .vert2.z=0.0,
        .vert3.x=-1.0, .vert3.y=1.0, .vert3.z=0.0,
        .vert4.x=-1.0, .vert4.y=-1.0, .vert4.z=0.0
        };

    writeOBJ(myPoly1);
    return 0;
    };

再次感谢大家。

【问题讨论】:

  • 如果你对你的编译器撒谎,它会自动回复你。

标签: c stack


【解决方案1】:

Evan Teran 已经回答了这个问题。但我想提出一些检测此类问题的方法:

  • 尝试使用gcc -fno-stack-protector stack.c 编译并检查它是否仍然给您stack smash detected
  • 为 gnu 调试器使用 -g 标志。作为gcc -g stack.c,然后是gdb a.out -> run。它将在出现问题的地方停止执行。然后你可以在gdb中输入where,看看哪一行代码是问题的根源。

【讨论】:

  • 谢谢 Peeyush,这让我对使用 gcc 进行调试有了新的认识。
【解决方案2】:

问题出在以下行:

char objString[] = "# plane def\n";

这只会为字符串“# plane def\n”分配足够的空间,然后,您将更长的字符串写入其中(使用 snprintf)。

既然您已经在使用常量值 128,那么如何:

char objString[128];
strcpy(objString, "# plane def\n");
writeLine(objString);
/* continue as before */

请注意,strcpy 也可以破坏堆栈,因此请确保目标有足够的空间用于您要复制的任何内容。

【讨论】:

    【解决方案3】:

    您可以通过fopen.. 确认您获得的是有效的FILE*..

    通过显式检查NULL 指针。

    【讨论】:

      【解决方案4】:

      这就是你的问题所在:

      char objString[] = "# plane def\n";  writeLine(objString);
      
      snprintf(objString, 128, "v %f %f %f \n", myPoly.vert1.x, myPoly.vert1.y, myPoly.vert1.z);  writeLine(objString);
      snprintf(objString, 128, "v %f %f %f \n", myPoly.vert2.x, myPoly.vert2.y, myPoly.vert2.z);  writeLine(objString);
      snprintf(objString, 128, "v %f %f %f \n", myPoly.vert3.x, myPoly.vert3.y, myPoly.vert3.z);  writeLine(objString);
      snprintf(objString, 128, "v %f %f %f \n", myPoly.vert4.x, myPoly.vert4.y, myPoly.vert4.z);  writeLine(objString);
      

      objString 是一个包含strlen("# plane def\n") + 1 空格字符的数组。然后你在传递128 的缓冲区上使用snprintf(这太大了)。

      我会这样重写:

      writeLine("# plane def\n");
      
      char objString[128]
      snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert1.x, myPoly.vert1.y, myPoly.vert1.z);  writeLine(objString);
      snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert2.x, myPoly.vert2.y, myPoly.vert2.z);  writeLine(objString);
      snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert3.x, myPoly.vert3.y, myPoly.vert3.z);  writeLine(objString);
      snprintf(objString, sizeof(objString), "v %f %f %f \n", myPoly.vert4.x, myPoly.vert4.y, myPoly.vert4.z);  writeLine(objString);
      

      支点:

      你为什么要为每一行打开和关闭文件?效率太低了……

      最好在程序启动时打开文件一次,写下所有行,然后在完成后关闭它。这也将使代码更简单。

      【讨论】:

      • 优秀的回答埃文。谢谢!
      猜你喜欢
      • 1970-01-01
      • 2023-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-01
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多