【问题标题】:"*** stack smashing detected ***" with file read and writes“***堆栈粉碎检测到***”与文件读取和写入
【发布时间】:2020-12-23 10:36:43
【问题描述】:

我正在使用一个从单个文件读取并使用sprintf 将该数据写入多个文件的 C 程序,我在某个地方出错了,但我真的不知道在哪里以及这会导致错误:

*** stack smashing detected ***

这里给出了可用于复制的代码:

FILE * source=fopen("card.raw","r");// defines source I will read from

char array[512];
int active_read=0;
char * filename;
sprintf(filename, "%03i.jpg",i);
FILE *image=fopen(filename, "a"); //my understanding of sprintf to create a file

while(fread(array,sizeof(char *),512,source)==512) // if 512 characters can be detected
{
    if(array[0]==0xff && array[1]==0xd8 && array[2]==0xff && (array[3] & 0xf0==0))
    {
        if(active_read==1)
            fclose(image);
        active_read=1;
        sprintf(filename, "%03i.jpg",i);
        image=fopen(filename, "a");
        fwrite(array, sizeof(char *),512, image);
        i++;
    }
    else if(active_read==1)
        fwrite(array, sizeof(char *), 512, image);
}

我用我的调试器(CS50 调试器)对代码进行了排序。而且我发现if 条件甚至从未被检查过。它从while 循环跳转到else if 条件,从不执行任何操作然后返回错误。

【问题讨论】:

  • 1) filename 未初始化,因此第一个 sprintf 已经给出了 UB。 2)fread(array,sizeof(char *),512,source) 为什么是sizeof(char *)arraychar[],所以应该是 sizeof(char) 或简单的 1
  • filename 应该是一个数组,而不是一个指针。
  • char * filename; -> char filename[64];
  • @Mr.JohnnyDoe 这只是为了说明原理,即选择一个足够高的数字,这样您就不会溢出缓冲区。在“真实”代码中,您应该使用snprintf
  • active_read 有点奇怪。我们看不到它是如何初始化的,它只被赋值为 1。您需要发布定义和初始化它的代码

标签: c file pointers cs50


【解决方案1】:

除了char * filename; 的问题之外,它应该是一个数组,例如char filename[64];,这里有问题:

(array[3] & 0xf0==0)

如何评估?

((array[3] & 0xf0)==0) 还是(array[3] & (0xf0==0))

结帐https://en.cppreference.com/w/c/language/operator_precedence,您会看到== 的优先级高于&。因此,您首先执行0xf0 == 0。那总是假的(又名零),所以你的表达总是假的。因此允许编译器(并且可能会)优化生成的代码,以便在运行时不对表达式进行评估。相反,它总是直接转到else if 部分。

换句话说 - 允许编译器将您的代码视为:

while(fread(array,sizeof(char *),512,source)==512) 
{
    if(active_read==1)
        fwrite(array, sizeof(char *), 512, image);
                      ^^^^^^^^^^^^^^
}

编辑:还要注意这应该 是 sizeof(char) 或只是 1(因为 sizeof(char) 总是 1) 正如@Jabberwocky 在评论中所注意到的那样。

顺便说一句:小心sprintf,因为它可能会溢出目标缓冲区。考虑改用snprintf

【讨论】:

  • (array[3] & 0xf0)==0xe0 我做了这个改变,然后开始工作。问题不在于 if 返回 true 还是 false。问题是编译器从不真正检查值是真还是假
  • @Mr.JohnnyDoe 编译器可以看到你的表达式总是错误的,所以它只是优化了代码......当结果已知时没有理由检查条件
  • @4386427 不应该也是sizeof(char) 而不是sizeof(char *)
  • 所以我得再看看 if 语句
  • @Jabberwocky 好点...我会更新答案..
【解决方案2】:

要添加到@4386427 的答案,如果您的char 类型已签名,则比较array[0]==0xff 将始终为假,因为seen here(也是一个专业提示:尝试启用编译器提供的每一个警告) .

原因是0xFF255,它被提升为int,然后与同样被提升为int的signed char进行比较,但取值范围可能从-128到127.

如果charsigned,要确​​保您的代码正常工作,请将array 声明更改为使用unsigned char,或在比较之前强制转换为无符号:

 if ( (unsigned char)array[0] == 0xff && 
      (unsigned char)array[1] == 0xd8 &&
      (unsigned char)array[2] == 0xff &&
      ((unsigned char)array[3] & 0xf0) == 0 )
 ...

【讨论】:

    【解决方案3】:

    所以看起来我的 if 条件的一般概念导致了问题:

    if(array[0]==0xff && array[1]==0xd8 && array[2]==0xff)

    因为我一直在将字符数组中的单个字符与一个字节进行比较。我把array的类型改成了BYTE *,问题就解决了

    【讨论】:

      猜你喜欢
      • 2012-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-21
      相关资源
      最近更新 更多