【问题标题】:Segmentation fault core dumped when reading file读取文件时转储​​分段错误核心
【发布时间】:2015-01-15 22:35:30
【问题描述】:

我在第 24 行 (fgets) 中遇到了分段错误(核心转储)错误。我对c不是很熟悉,但我必须为我的课程制作一个程序。我有以下代码:

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

int main(int argc, char* argv[]){
   FILE *fd1, *fd2;
   char *str1, *str2;
   char *salt, *hash, *key, *key1;
   char buf[13], word[200], pass[200];

   if(argc != 2){
       fprintf(stderr, "Usage: %s <file shadow>\n", argv[0]);
       exit(1);
   }

   str1 = (char*) malloc(100);
   str2 = (char*) malloc(100);

   fd1 = fopen(argv[1], "r");

   fprintf(stderr, "Please, wait...\n");

   while(fgets(str1, 100, fd1) != NULL){
       str2 = strstr(str1, "$1$");
       if(str2 != NULL){
           key = strtok(str2, ":");
           snprintf(pass, sizeof(pass), "%s", key);
           printf("pass=%s (%lu)\n", pass, strlen(pass));

           strtok(key, "$");
           salt = strtok(NULL, "$");
           hash = strtok(NULL, "\0");

           snprintf(buf, sizeof(buf), "$1$%s$", salt);

           fd2 = fopen("polish.txt", "r");

           while(fgets(word, 200, fd2) != NULL){
               (&word[strlen(word)])[-1] = '\0';

           key1 = crypt(word, buf);

           if(!strncmp(key1, pass, strlen(key1))){
               printf("OK!, The password is: %s\n\n", word);
               break;
           }


           }
       }

   }
   fclose(fd1);
   fclose(fd2);
   free(str1);
   free(str2);

   return 0;
 }

当我尝试读取 /etc/shadow 文件时,它会引发分段错误(也尝试使用自定义 txt 文件)。有人可以看看这个吗?

【问题讨论】:

  • 你做了什么调试?它在哪里崩溃?如果您不知道如何使用调试器读取核心转储,那么您应该学习这一点,以便您可以调试此类问题。
  • 他在这样一个古老的 unix 系统上,调试仍然被认为是只为弱者服务的 ;)
  • 次要:为什么使用"%lu" 而不是"%u" 或正确的"%zu"
  • 1) 幻数(在本例中为 100 和 200)是维护的噩梦。建议对每个数字使用#define,并用#define 名称替换硬编码的幻数。 2) 始终检查 strtok 的返回值以确保操作成功。 3) 始终检查 fopen() 的返回值以确保操作成功。
  • 建议每行声明一个(1)局部变量,带有初始值和注释,这样(稍后)你和(现在)我就知道变量的用途。当有人(我)试图对你的代码进行逆向工程以试图帮助你时,这样的 cmets 会很有帮助

标签: c segmentation-fault fgets


【解决方案1】:

您可以在代码中改进几处,

  1. 您不需要投射 malloc,它不是必需的,并且可能会导致无法跟踪的错误。

  2. 对于固定大小的局部变量,您无需使用 malloc

  3. 您永远不会检查任何在失败时返回 NULL 的函数的返回值,在您的情况下是:

    1. malloc()
    2. fopen()
    3. strok()

所有这些函数在失败时返回NULL

  1. malloc 并分配指针 str2,但随后您将其覆盖在

     str2 = strstr(str1, "$1$");
    

这样做没有意义,这意味着您不了解指针的工作原理。 strstr() 返回指向传递给它的相同字符串的指针,只是递增以指向您要查找的子字符串的开头,如果未找到,则返回 NULL

  1. 你有

     key = strtok(str2, ":");
     /* some other code */
     strtok(key, "$");
    

这是错误的,因为您将递增的指针传递给相同的字符串,您必须这样做,阅读strtok()

    strtok(NULL, "$");
  1. fclosefd2I/O 流在while 循环之外,但你fopen() 它在里面,你可能有fopened 它比你fclosed 它的次数多得多。

您有两个选择,要么将fclose() 移到while 循环内,要么将fopen() 移到while 循环外,当然,第二种更好。

如您所见,您有很多潜在的未定义行为,特别是在您的代码中取消引用 NULL 指针,如果您想阻止 SEGMENTATION FAULT,您应该修复所有这些问题。

看到程序员忽略这些事情是很常见的,但你应该确保你的程序不会崩溃,只是要小心。

  • 检查每个取消引用的指针,除非很明显它不是NULL
  • 将每个指针 NULL 置于声明处或紧随其后,这样您就可以确保如果它还没有指向任何东西,那么它就是 NULL,您可以对此进行检查。

处理指针是一件非常困难的事情,但是一旦你养成了这些好习惯,你就再也不会有愚蠢的错误了 -> 嗯,几乎从来没有

【讨论】:

  • 为什么你需要说“你的代码有很多错误”
  • 我认为指出这一点并不失礼。我只是直接。请随时编辑该部分。虽然,我认为这样说并没有什么错,但我知道这可以被视为粗鲁的评论。
  • 我更新了答案。所以你现在可以删除这条评论,我也会删除我的。
【解决方案2】:

问题在于这一行缺少错误检查:

 fd1 = fopen(argv[1], "r");

fopen() returns 指向填充的FILE 对象的指针,或者NULL 如果无法打开文件(文件不存在,或者用户没有足够的权限读取它) .

因此,NULL 指针被传递到第 24 行对 fgets() 的调用中。

你应该检查它是否为 NULL:

fd1 = fopen(argv[1], "r");
if (fd1 == NULL)
{
    fprintf(stderr, "Couldn't open file for reading\n");
    exit(1);
} 

我们讨论的是 NULL 指针,您还应该检查对 malloc() 的调用。这些不太可能失败,但也可能导致第 24 行的崩溃。

【讨论】:

  • 以及调用strtok的结果
【解决方案3】:

C 没有像 Java 这样的异常机制。获取有关错误信息的唯一方法是检查函数返回的值,有时还可以获取更多信息errno

在您的代码中,您应该检查fopen 是否成功,如果没有则退出并显示错误信息。

您的程序崩溃了,因为您可能尝试读取您没有读取权限的文件并且由于fopen 返回NULL。它的修补程序是以 root 或 sudo 运行程序。

编辑

我试过了,你的程序崩溃了:

程序收到信号SIGSEGV,分段错误。 _IO_fgets (buf=0x7fffffffd6a0 "", n=200, fp=0x0) at iofgets.c:50 50 iofgets.c: 没有这样的文件或目录。

因为我添加的时候没有polish.txt,所以运行流畅没有错误

【讨论】:

    【解决方案4】:
    // always check returned values from system I/O functions
    // always enable all warnings so problems are addressed
    // do not use 'magic' numbers
    // initialize local variables that are pointers
    //   (and generally all local variables)
    // when an error is encountered during execution,
    //    then release allocated memory, close files before exiting
    // always look for and handle errors during execution
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <crypt.h> // <-- added for crypt() function
    
    #define MAX_WORD_LEN (200)
    #define MAX_STR_LEN  (100)
    #define MAX_BUF_LEN  (13)
    
    int main(int argc, char* argv[])
    {
        FILE *fd1 = NULL;
        FILE *fd2 = NULL;
    
        char *str1 = NULL;
        char *str2 = NULL;
    
        char *salt, *hash, *key, *key1;
    
        char buf[MAX_BUF_LEN]   = {'\0'};
        char word[MAX_WORD_LEN] = {'\0'};
        char pass[MAX_WORD_LEN] = {'\0'};
    
        if(argc != 2)
        {
            fprintf(stderr, "Usage: %s <file shadow>\n", argv[0]);
            exit(1);
        }
    
        // implied else, command line parameter count correct
    
        if( NULL == (str1 = malloc(MAX_STR_LEN) ))
        { // then, malloc failed
            perror( "malloc for str1 failed" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, malloc successful
    
        if( NULL == (str2 = malloc(MAX_STR_LEN) ))
        { // then, malloc failed
            perror( "malloc for str2 failed" );
            free( str1 ); // cleanup
            exit( EXIT_FAILURE );
        }
    
        // implied else, malloc successful
    
        if( NULL == (fd1 = fopen(argv[1], "r") ) )
        { // then fopen failed
            perror( "fopen for parameter file name failed" );
            free( str1 ); // cleanup
            free( str2 );
            exit( EXIT_FAILURE );
        }
    
        // implied else, fopen successful
    
        fprintf(stderr, "Please, wait...\n");
    
        while( fgets(str1, MAX_STR_LEN, fd1) )
        {
            if( NULL == (str2 = strstr(str1, "$1$") ) )
            { // then, strstr failed
                perror( "strstr for $1$ failed" );
                continue;
            }
    
            // implied else, strstr successful
    
            if( NULL != (key = strtok(str2, ":") ) )
            { // then, strtok failed
                perror( "strtok for : failed" );
                continue;
            }
    
            // implied else, strtok successful
    
            snprintf(pass, sizeof(pass), "%s", key);
            printf("pass=%s (%lu)\n", pass, strlen(pass));
    
            if( NULL == strtok(key, "$") )
            { // then strtok failed
                perror( "strtok for $ failed" );
                continue;
            }
    
            // implied else, strtok successful
    
            if( NULL == (salt = strtok(NULL, "$") ) )
            { // then strtok failed
                perror( "strtok for salt failed" );
                continue;
            }
    
            // implied else, strtok successful
    
            if( NULL == (hash = strtok(NULL, "\0") ) )
            { // then strtok failed
                perror( "strtok for hash failed" );
                continue;
            }
    
            // implied else, strtok successful
    
            snprintf(buf, sizeof(buf), "$1$%s$", salt);
    
            if( NULL == (fd2 = fopen("polish.txt", "r") ) )
            { // then fopen failed
                perror( "fopen for polish.txt failed" );
                fclose(fd1); // cleanup
                free(str1);
                free(str2);
                exit( EXIT_FAILURE );
            }
    
            // implied else, fopen successful
    
            while( fgets(word, MAX_WORD_LEN, fd2) )
            {
                (&word[strlen(word)])[-1] = '\0'; // what last char is being dropped?
    
                key1 = crypt(word, buf);
    
                if(!strncmp(key1, pass, strlen(key1)))
                {
                    printf("OK!, The password is: %s\n\n", word);
                    break;
                }
    
                else
                {
                    printf("Oh No, The password did not match\n\n");
                } // end if
            } // end while
    
            // prep for next test sequence
            fclose( fd2 );
            fd2 = NULL;
        } // end while
    
        fclose(fd1);
        fclose(fd2);
    
        free(str1);
        free(str2);
    
        return 0;
    } // end function: main
    

    【讨论】:

      猜你喜欢
      • 2018-12-09
      • 2016-02-25
      • 1970-01-01
      • 1970-01-01
      • 2012-10-12
      • 1970-01-01
      • 2020-07-14
      • 2015-06-25
      相关资源
      最近更新 更多