【问题标题】:Stack Smashing while using strcpy and strcat使用 strcpy 和 strcat 时的堆栈粉碎
【发布时间】:2017-01-18 19:36:09
【问题描述】:

我已经尝试调试了一段时间,仍然无法弄清楚为什么这会导致堆栈粉碎错误(我认为错误代码是 6,或者中止。本质上这个函数需要一个目录,打开一个文件然后将该文件放入一个函数中以便它可以使用该文件,然后输出它通过该函数的次数。

int map(char* dir, void* results, size_t size, int (*act)(FILE* f, void* res, char* fn))
 {
      printf("%s\n", dir);
      char copyDirectory[strlen(dir)+1];
      //adds the slash
      strcpy(copyDirectory, dir);
      strcat(copyDirectory, "/");
      //before the psuedocode, get all the files in the directory
      int numFiles = nfiles(copyDirectory);
      DIR* directory = opendir(copyDirectory);
      //if there aren't any files, then we exit
      if(numFiles == 0)
      {
           closedir(directory);
           return -1;
      }
      //reads the file from the directory
      struct dirent* readFile = readdir(directory);
      int output = 0;
      while(readFile!=NULL)
      {
           if(readFile->d_type==DT_REG)
           {
               //step 2: obtain filepath
               char* fileName = readFile->d_name;
               int filePathLength = strlen(dir) + strlen(fileName) + 1;//add one for the slash
               char filePath[filePathLength];
               memset(filePath, 0, filePathLength); //allocat ememory for file path
               strcpy(filePath, strcat(dir, fileName));
               //step 3: open file
               FILE* file = fopen(filePath, "r");
               //if the file is unreachable, exit
               if(file==NULL)
               {
                    closedir(directory);
                    return -1;
               }
               //step 4: perform some action and store result
               strcpy(dir, copyDirectory);
               act(file, results, fileName);
               //step 5: close file
               fclose(file);
               //to go through loop: increment the readFile
                    ++output;
                }
                readFile = readdir(directory);
      }
      closedir(directory);
      return output;
 }

映射函数与示例。

int map(char* dir, void* results, size_t size, int (*act)(FILE* f, void* res, char* fn))
{
     char* copyDirectory = strdup(dir);
     DIR* directory = opendir(dir);
     int output = 0;
     struct dirent* readFile = readdir(directory);
     while(readFile!=NULL)
     {
         if(readFile->d_type==DT_REG)
         {
             //step 2: obtain filepath
             char* fileName = readFile->d_name;
             int filePathLength = strlen(dir) + strlen(fileName) +2;//add one for the slash
             char filePath[filePathLength+1];
             memset(filePath, 0, filePathLength); //allocat ememory for file path
             strcpy(filePath, strcat(dir, fileName));
             //step 3: open file
             FILE* file = fopen(filePath, "r");
             //if the file is unreachable, exit
             if(file==NULL)
             {
                 closedir(directory);
                 return -1;
             }
             //step 4: perform some action and store result
             strcpy(dir, copyDirectory);
             act(file, results, fileName);
             //step 5: close file
             fclose(file);
             //to go through loop: increment the readFile
             ++output;
         }
         readFile = readdir(directory);
     }  
     closedir(directory);
     return output;
}
//Sample Map function action: Print file contents to stdout and returns the number bytes in the file.
int cat(FILE* f, void* res, char* filename) {
    char c;
    int n = 0;
    printf("%s\n", filename);
    while((c = fgetc(f)) != EOF) {
        printf("%c", c);
        n++;
    }
    printf("\n");
    return n;
}
int main(int argc, char const *argv[])
{
    char directory[]= "../rsrc/ana_light/";
    size_t size = 100;
    void* results[500]; 
    int mapCat = map(directory, results, size, cat);
    printf("The value of map is %d.\n", mapCat);
    return EXIT_SUCCESS;
}

失败的地方是在它执行并打印到输出之后。该功能应打印出您拥有的文件的内容。目录列表的末尾需要有一个“/”。目前它打印文件内容并以它读取的文件数量的值退出,但在退出堆栈粉碎错误后它会丢失。

EDIT1:编辑代码以反映我所做的更改。

EDIT2:我认为是按照 MCVE 标准完成的?如果我没记错的话应该跑。

【问题讨论】:

  • char copyDirectory[strlen(dir)+1]; strcpy(copyDirectory, dir); strcpy() 使用每个分配的字节,但是你在strcat(copyDirectory, "/"); 写超出了数组的末尾。这会破坏堆栈。
  • 嗯,甚至在这发生之前,它就已经在破坏堆栈了。即使我将“/”硬编码到 main 方法中,它也会产生错误。
  • 请查看如何创建 MCVE (minimal reproducible example),然后添加(最少的)必要代码,以便我们可以执行您展示的内容。您应该将其添加为您的代码的新版本 - 保持原始可见,因为您有一个解决原始代码中实际问题的答案。您在哪个平台上使用哪个编译器?您是否使用valgrind 来帮助诊断问题?您可以...吗? (并非在所有平台上都可用。)
  • 给你一个严肃的问题:如果你省略了act 参数和对函数的调用,你还会遇到崩溃吗?如果是这样,请忽略它;它将大大简化您的 MCVE。
  • 抱歉,没听说过 MCVE 标准。修复了代码以解决此问题。它需要与行为部分配合使用。它确实可以使用它,但最后它会以检测到堆栈粉碎错误而终止。

标签: c char strcpy strcat


【解决方案1】:

第一个问题:改变

    char copyDirectory[strlen(dir)+1];

    char copyDirectory[strlen(dir)+2];

第二个问题:改变

       char filePath[filePathLength];

       char filePath[filePathLength+1];

第三个问题(第一次阅读时似乎没有变化):

     //strcpy(copyDirectory, dir);
     strcat(copyDirectory, dir);

注释掉的代码是正确的:

    strcpy(copyDirectory, dir);

您忘记了尾随空字符的空格。

第四个问题:你忘了处理 opendir 失败。

第五题:这段代码错了:

        memset(filePath, 0, filePathLength); //allocat ememory for file path
        strcpy(filePath, strcat(dir, fileName));

改为:

         strcpy(filePath, copyDirectory);
         strcat(filePath, fileName);

不要在此处写入您的输入变量。这是一个非常糟糕的主意。

在为 copyDirectory 撤消(泄漏的)strdup 并将您非常创新的本地缓冲区放回之后,我能够让代码运行到完成。

【讨论】:

  • 我想我明白了,我需要为空终止符添加另一个空格,但它似乎没有帮助,错误仍然存​​在。
  • 然后尝试 MVCE。我需要一些东西才能跑得更远。
  • 好吧,如果你把它放在 main 中,它仍然会通过,它只是在 main 之后终止。哎呀,还没写完。我将在几秒钟内编辑代码以显示错误发生的位置并举例说明。
  • 编辑了代码。您可以将目录替换为任何目录,只需以“/”结尾即可。它打印得很好,就在 main 结束后它以错误代码终止。
  • 非常感谢!我会修复你提到的其他事情,但是是的,这确实消除了错误!我非常感谢,这可能花了我 10 多个小时的时间,我把头撞在墙上试图弄清楚。至于本地缓冲区,我并没有在实际项目中使用本地缓冲区,只是为了简单起见。
猜你喜欢
  • 2014-06-26
  • 2011-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多