简体   繁体   English

C程序-尝试从文件中读取时出现“分段错误”

[英]C Program - Getting “Segmentation Fault” when trying to read from a file

Please bear with me since I'm still new in programming. 由于我还是编程新手,请多多包涵。 I'm trying to read a file and store its context as a variable, here's my code I'm sorry if it's rather long: 我正在尝试读取文件并将其上下文存储为变量,这是我的代码,如果很长的话我很抱歉:

#define _BSD_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

// CEK ROUTER MODEL
char* router_model;
char* model() {
    char filename[] = "/proc/cpuinfo";
    char* key = "system type";
    char* value;
    FILE *file = fopen(filename, "r");

    if (file != NULL) {
        char line[1000];

        while (fgets(line, sizeof line, file) != NULL) /* read a line from a file */ {
            //fprintf(stdout, "%s", line); //print the file contents on stdout.
            if (strncmp(line, key, strlen(key)) == 0) {
                char* value = strchr(line, ':');
                value += 2;
                router_model = strdup(value);
                break;   // once the key has been found we can stop reading
            }
        }
        fclose(file);
    }
    else {
        perror(filename); //print the error message on stderr.
    }
    return router_model;
}

// TULIS SERIAL NUMBER KE FILE
char tulis(char p[100]) {
    // Write a serial number to a file
    char sn[30];
    char encrypt_sn[50];
    printf("Serial Number:\n");
    scanf("%s", sn);
    FILE *f = fopen("/usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c", "w");
    if (f == NULL) {
        printf("Error opening file!\n");
        exit(1);
    }
    fprintf(f,"Serial Number: %s", sn);
    fclose(f);
    sprintf(encrypt_sn, "ccrypt -e /usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c -K %s", p);
    system(encrypt_sn);
    system("mv /usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c.cpt /usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c");
    printf("Serial number is saved in /usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c\n");
    return 0;
}

// BACA SERIAL NUMBER & SIMPAN DALAM SEBUAH VARIABLE
char baca(char p[100]) {
    // Store the serial number from a file in a variable
    char line[50];
    char decrypt_sn[50];
    char key[30] = "Serial Number";
    char *serial_number;
    if( access( "/usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c", F_OK ) != -1 ) {
        system("cp /usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c /tmp/");
        system("mv /tmp/fsn-55cfc8770b69cc07268fae7f25ee444c /tmp/fsn-55cfc8770b69cc07268fae7f25ee444c.cpt");
        sprintf(decrypt_sn, "ccrypt -d /tmp/fsn-55cfc8770b69cc07268fae7f25ee444c.cpt -K %s", p);
        system(decrypt_sn);
        FILE *file = fopen("/tmp/fsn-55cfc8770b69cc07268fae7f25ee444c", "r");
        if (file == NULL) {
            printf("Error opening file!\n");
            exit(1);
        }
        while (fgets(line, sizeof line, file) != NULL) /* read a line from a file */ {
            //fprintf(stdout, "%s", line); //print the file contents on stdout.
            if (strncmp(line, key, strlen(key)) == 0) {
                char* value = strchr(line, ':');
                value += 2;
                serial_number = strdup(value);
                break;   // once the key has been found we can stop reading
            }
        }
        fclose(file);
        //printf("Your hardware serial number is: (%s)\n", serial_number);
        remove("/tmp/fsn-55cfc8770b69cc07268fae7f25ee444c");
    }
    else {
        printf("fsn not found\n");
        return -1;
    }
    return 0;
}

int main(int argc, char* argv[]) {
    char *r;
    char *del;
    char *decrypt;
    int ret;
    char input[30];
    char *p;
    char *original_sn;
    p = "MmI4MTUxM2FjMjRlMDkzYmRkZGQyMjcwMjQ4OWY3MDAwNGZiYTM0MWNkZGIxNTdlYzAxN2";
    //tulis(p);
    original_sn = baca(p);
    printf("SN: %s", original_sn);
    return 0;
}

The file I'm trying to read is /usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c and after being decrypted with ccrypt , the content of that file is Serial Number: 1866203214226041 . 我尝试读取的文件是/usr/share/terminfo/f/fsn-55cfc8770b69cc07268fae7f25ee444c并使用ccrypt解密后,该文件的内容是Serial Number: 1866203214226041 I want to store 1866203214226041 as a variable but I'm getting Segmentation Fault when running that code, how do I fix it ? 我想将1866203214226041存储为变量,但是运行该代码时遇到了Segmentation Fault ,该如何解决?

Just look at this snippet extracted from your code: 只需查看从您的代码中提取的以下代码段:

    while (fgets(line, sizeof line, file) != NULL) /* read a line from a file */ {
        //fprintf(stdout, "%s", line); //print the file contents on stdout.
        if (strncmp(line, key, strlen(key)) == 0) {
            char* value = strchr(line, ':');
            value += 2;
            router_model = strdup(value);
            break;   // once the key has been found we can stop reading
        }
    }

First of all, you don't check the value returned from strchr(line, ':'); 首先,您无需检查从strchr(line, ':');返回的值strchr(line, ':'); as you will get NULL in case the string begins with system type but doesn't have a ':' char on it. 因为如果字符串以system type开头但没有':'字符,您将得到NULL You assume it will always find a ':' and increment the value by two (if it doesn't find the char, it will generate a pointer into position 2 of page zero of mem (which normally implies a SIGSEGV signal on later access from strdup(3) call). Also, after incrementing by 2 , you can just skip the last \\0 character, if the ':' character happens to be the last of the line. 你认为它总是会找到一个':'和两个增加值(如果它没有找到的字符,它会产生一个指向到位2 MEM的零页的(这通常意味着SIGSEGV来自后接入信号strdup(3)调用)。此外,如果':'字符恰好是该行的最后一行,则在将其递增2 ,您可以跳过最后一个\\0字符。

Note 注意

A final comment. 最后的评论。 As it is true that having key initialized at top makes the while loop more efficient, calling strlen(key) inside the loop makes the code to find the end of the string pointed to by key at each loop iteration, to get the string length. 诚然,将key初始化在顶部可以使while循环更高效,在循环内部调用strlen(key)可以使代码在每次循环迭代中查找key指向的字符串的结尾,以获取字符串长度。 Better to precalculate strlen(key) outside the loop (as it is constant) and use that value on the if statement, like this: 最好在循环外预先计算strlen(key) (因为它是常量),并在if语句上使用该值,如下所示:

    size_t key_length = strlen(key);
    while (fgets(line, sizeof line, file) != NULL) /* read a line from a file */ {
        //fprintf(stdout, "%s", line); //print the file contents on stdout.
        if (strncmp(line, key, key_length) == 0) {
            char* value = strchr(line, ':');
            if (value) { // only if value is != NULL should we continue
                value += 1; // we are not sure if after ':' we'll have a null char `\0` so better do a +1 increment.
                router_model = strdup(value);
                break;   // once the key has been found we can stop reading
            }
        }
    }

Another thing is: Don't use a so short string array for encrypt_sn (only 50 chars space for a max 49 length null terminated string) when you are printing that so large strings (I think only the format is already longer than that [edit] around 75 chars [/edit]) as you are running in a buffer overflow with your own code. 另一件事是:打印这么大的字符串时,不要对encrypt_sn使用如此短的字符串数组(最大长度为49的空终止字符串只能使用50个字符空间)(我认为只有格式已经比该长度长[编辑]大约75个字符[/ edit]),因为您正在使用自己的代码在缓冲区溢出中运行。 This can be another source for SIGSEGV . 这可能是SIGSEGV另一个来源。

No more checks have been done to your code, but ther can be more. 您的代码没有做更多检查,但是可以做更多的检查。 I think the same problem happens with decrypt_sn later on. 我认为稍后会出现同样的问题,使用decrypt_sn

One more question: Why do you tag your source as _BSD_SOURCE if it is not bsd source anymore? 还有一个问题:如果源不再是bsd源,为什么_BSD_SOURCE将其标记为_BSD_SOURCE (no procfs in bsd systems, no /proc/cpuinfo in bsd systems) I've tried to check my debian amd64 system's /proc/cpuinfo file and no system type field found there. (在bsd系统中没有procfs在bsd系统中没有/proc/cpuinfo )我试图检查我的debian amd64系统的/proc/cpuinfo文件,并且没有找到system type字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM