为什么在这个C代码中会出现堆栈溢出?

4

我很抱歉我对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;
    };

再次感谢大家。

如果你欺骗编译器,它会报复你的。 - Jonathan Leffler
4个回答

6
这就是你的问题所在:
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个字符空间的数组。然后你在该缓冲区上使用snprintf,传递了一个太大的值128

我会这样重新写:

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);

副要点:

为什么每写一行都要打开和关闭文件?这样非常低效......

最好在程序开始时打开文件,写入所有行后再关闭它。这也会使代码更简单。


0

您可以通过使用fopen来确认您正在获取有效的FILE*

通过显式检查NULL指针。


0

正如Evan Teran所回答的那样。但我想提出几种检测此类问题的方法:

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

谢谢Peeyush,这让我对使用gcc调试有了新的见解。 - user1416287

0
问题出在以下这行代码:
char objString[] = "# plane def\n";

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

既然您已经使用了常量值128,那么怎么样:

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

请注意,strcpy也可能破坏堆栈,因此请确保目标具有足够空间来复制任何内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接