memory corruption in malloc()



  • *GELÖST*

    Servus liebe Leut,
    ich hab seit langem mal wieder ein Projekt angefangen. Ich will eine Turingmaschine einlesen und per C-Programm ausführen. Ich lese es von einer csv-Datei ein. Ein Beispielprogramm habe ich unten angefügt.

    Ich hoffe, ihr findet eine Lösung. Wenn ich etwas besser kommentieren soll, sagt es.

    Nun zum eigentlichen Problem. Ich will mit "r -word=aba" und anschließend "r -word=aba" die Maschine zweimal einlesen und mit dem Eingabewort ausführen. Doch ich bekomme beim zweiten mal nen Segfault. gdb sagt mir, dass das in Zeile 185 passiert. Wenn ich mein Programm mit valgrind ausführe, taucht kein Fehler auf o.O.
    Ich allokiere in der Zeile mittels calloc Speicher, welcher einem Pointer in einer verketteten Liste zugewiesen wird. ABER: der pointer, der in einer dynamischen Struktur liegt wurde kurz davor allokiert. Also sollte ich keinen Speicherbereich einem nicht allokiertem Pointer zuweisen, worauf der Fehler im ersten Blick schließen ließe.

    Leider habe ich keine Möglichkeit darin gesehn, das Problem an einem kleineren Programm zu demonstrieren.
    /*bla.c*/

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    #ifdef __unix__
        #define clrscr() printf("\x1B[2J")
    #elif __BORLANDC__ && __MSDOS__
        #include <conio.h>
    #elif __WIN32__ || _MSC_VER
        #define clrscr() system("cls")
    #else
        #define clrscr() printf("clrscr() - Fehler!!\n")
    #endif
    
    /***
     * let n be the amount of rows in the table - 1
     * We got n possibilities of what we can read from the word
     * let q be the amount of columns in the table -1
     * We got q possibilities of what we can jump to
    ***/
    
    struct state {
        char *write;
        char *head;
        int *targets;
        struct state *previous;
        struct state *next;
    };
    
    typedef struct state STATE;
    
    static STATE *start;
    
    void print_help(); 
    char *load_table(char *tablename);
    char *csv_to_buf(char *tablename, char *separator); 
    char *scan_word(char *wordptr);        
    int *add_break(char *break_pos, int *breakpoints);
    int run_mode(int *noclear, int *max, int *counter, int *breakpoints, char *word, char *vars);
    void step_mode(int *noclear, int *max, int *counter, int *breakpoints, char *word, char *vars);
    void clear_structs(char *vars);
    char *strtok_r(char *s, const char *delim, char **save_ptr);
    
    int main(void) {
        int *breakpoints = NULL;
        char *searchptr;
        char *word;
        char *vars;
        char input[512];
        int noclear = 1;
        int max = 0;
        int counter = 0;
    
        print_help();
        while(1) {
            if(!noclear)
                clrscr();
            memset(input, '\0', 512);
            printf("<turing> ");
            fgets(input, 511, stdin);
            input[strlen(input) - 1] = '\0';
            if((word = strstr(input, "-word=")) != NULL) 
                word = scan_word(word); //save the word to an allocated string
            if((searchptr = strstr(input, "break")) != NULL) 
                breakpoints = add_break(searchptr, breakpoints);
            else if(strstr(input, "-noclear") != NULL)
                noclear = 0;
            else if((searchptr = strstr(input, "set max=")) != NULL) {
                input[strlen(input) - 1] = '\0';
                max = atoi((searchptr + 8));
            }
            else if((input[0] == 'h') || (strstr(input, "help") != NULL))
                print_help();
            else if((strstr(input, "exit")) != NULL) 
                break;
                /*here begin*/
            else if((searchptr = strstr(input, "-table=")) != NULL)
                load_table((searchptr + 7)); 
            else if(input[0] == 'r') {
                if(start != NULL) {
                    run_mode(&noclear, &max, &counter, breakpoints, word, vars);
                }
                else {
                    vars = load_table("turing.csv");
                    run_mode(&noclear, &max, &counter, breakpoints, word, vars);
                }
                clear_structs(vars);
            }
            else if(input[0] == 's') {
                if(start != NULL) {
                    step_mode(&noclear, &max, &counter, breakpoints, word, vars);
                }
                else {
                    vars = load_table("turing.csv");
                    run_mode(&noclear, &max, &counter, breakpoints, word, vars);
                }
                word = NULL;
                clear_structs(vars);
            }
            else 
                fprintf(stderr, "Wrong input %s\n", input);
        }
        free(breakpoints);
        return 0;
    }
    
    void print_help() {
        printf("\n\nUsage: (r | s) -word=[WORD] (-table=[FILENAME.csv] | -t=[AMOUNT] | -noclear)\n");
        printf("       break=[STATE]; set max=[AMOUNT]; h; exit;\n");
        printf("\t\n¡States only in ascending order or my program won't work!\n\n"); //maybe we implement that, maybe not. It slows the program down
        printf("\tr or run \t\tdoes simply run, takes the table name turing.csv in the actual working directory and shows you no state, just the end\n");
        printf("\ts or step \t\tis stepping mode where you can step whenever you want. \n");
    //    printf("\tb \t\t\tmove one step backwards\n"); //maybe we implement that, maybe not. 
        printf("\t-table=[FILENAME.csv] \tparses the file from the specified path\n");
        printf("\t-t=[AMOUNT] \t\tsets a sleep between the steps (default is 2 but then just with -t option)\n");
        printf("\t-noclear \t\tdoesn't clear the screen between the steps\n");
        printf("\tset max=[AMOUNT] \tsets maximal steps to halt surely and prints the current state and command\n");
        printf("\tbreak [state] \t\tbreaks on a state you want\n");
        printf("\th or help \t\tprints this help\n");
        printf("\texit \t\t\texits the whole turing program\n\n");
    }
    
    char *csv_to_buf(char *tablename, char *separator) {
        FILE *table_file;
        char *table_str;
        int filesize;
        int i = 0;
    
        if((table_file = fopen(tablename, "r")) == NULL) {
            perror(tablename);
            return NULL;
        }
        fseek(table_file, 0L, SEEK_END);
        filesize = ftell(table_file);
        rewind(table_file);
        table_str = (char *) malloc(filesize + 1);
        fread(table_str, 1, filesize, table_file);
        table_str[filesize] = '\0';
    
        /*what is the separator of our csv file? */
        if(!(sscanf(table_str, "sep=%c\n", &separator[0]))) {
            for(i = 0; (i < filesize) && (table_str[i] != ',') && (table_str[i] != ';') && (table_str[i] != ':') &&
                (table_str[i] != '\t') && (table_str[i] != ' '); i++) 
                ; //stops, if separator found
            separator[0] = table_str[i];
            separator[1] = '\0';
        }
    
        fclose(table_file);
        return table_str;
    }
    char *load_table(char *tablename) {
        char separator[2]; //we need that format for strtok
        char *cur_str;
        char *table_str;
        char *cur_line;
        char *saveptr[3];
        char *vars;
        int counter;
        STATE *cur_state;
        STATE *buf_ptr;
    
        table_str = csv_to_buf(tablename, separator);
        /***
          char *write;
          char *head;
          int *targets;
          struct state *previous;
          struct state *next;
        ***/
        cur_line = strtok_r(table_str, "\n", &saveptr[1]);
        cur_str = strtok_r(cur_line, separator, &saveptr[0]);
        for(counter = 0; strtok_r(NULL, separator, &saveptr[0]) != NULL;counter++) 
            ; //counts the amount of states
        start = (STATE *) malloc(sizeof(STATE));
        start->write = (char *) calloc(1, 2);
        start->head = (char *) calloc(1, 2);
        start->targets = (int *) calloc (1, 2);
        start->next = (STATE *) malloc(sizeof(STATE));
        cur_state = start->next; 
        cur_state->previous = start;
        for(int i = 1; i < counter; i++) {
            cur_state->write = (char *) calloc(1, 1);
            cur_state->head = (char *) calloc(1, 1);
            cur_state->targets = (int *) calloc(1, 1);
            cur_state->next = (STATE *) malloc(sizeof(STATE));
            memset(cur_state->next, 0, sizeof(STATE)); //just 4 valgrind
            buf_ptr = cur_state;
            cur_state = cur_state->next;
            cur_state->next = NULL;
            cur_state->previous = buf_ptr;
        }
    
        /*And here is where the maaaaagiiiic begins. strtok_r makes it all. I'm just using it*/
        vars = (char *) calloc(1, 1);
        counter = 1;
        while((cur_line = strtok_r(NULL, "\n", &saveptr[1])) != NULL) {
            vars = realloc(vars, strlen(vars) + 2); //the different vars in the alphabet
            vars[strlen(vars) + 1] = vars[strlen(vars)]; //last sign should be \0
            vars[strlen(vars)] = cur_line[0]; //By every table the first element is the chosen alpha
            cur_str = strtok_r(cur_line, separator, &saveptr[2]); //second sign
            cur_state = start;
            while((cur_str = strtok_r(NULL, separator, &saveptr[2])) != NULL) {
                cur_state->write = (char *) realloc(cur_state->write, counter + 1);
                cur_state->write[counter] = '\0';
                cur_state->head = (char *) realloc(cur_state->head, counter + 1);
                cur_state->head[counter] = '\0';
                cur_state->targets = (int *) realloc(cur_state->targets, sizeof(int) * counter);
                if(cur_str[0] == '"') {
                    cur_state->write[counter - 1] = cur_str[1];
                    cur_str = strtok_r(NULL, ",", &saveptr[2]);
                    for(;isspace(cur_str[0]); cur_str++)
                        ; //Null statement
                    cur_state->head[counter - 1] = cur_str[0];
                    cur_str = strtok_r(NULL, ",", &saveptr[2]);
                    for(;isspace(cur_str[0]) || isalpha(cur_str[0]); cur_str++)
                        ; //Null statement
                    cur_state->targets[counter - 1] = atoi(cur_str);
                }
                else {
                    cur_state->write[counter - 1] = '-';
                    cur_state->head[counter - 1] = '-';
                    cur_state->targets[counter - 1] = -1;
                }
                cur_state = cur_state->next;
            } 
            counter++;
        } 
        /*later I will maybe support other state descriptions as numbers. At the moment every state has to follow by the next*/
        free(table_str);
        return vars;
    }
    
    char *scan_word(char *wordptr) {
        char *word;
        int size;
        wordptr += 6;
        word = wordptr;
        /*word to the last element*/
        while((!isspace(word[0])) && (word[0] != 0))
            word++;
        size = word - wordptr;
        word = wordptr;
        wordptr = (char *) malloc(size + 1);
        strncpy(wordptr, word, size);
        wordptr[size] = '\0';
        return wordptr;
    }
    
    int *add_break(char *break_pos, int *breakpoints) {
        int i;
        static int amount = 0;
        break_pos += 6;
        while(!isdigit(break_pos[0]))
            break_pos++;
        for(i = 0; isdigit(break_pos[i]); i++)
            ; //go to the end of the word
        break_pos[i] = '\0';
        if(breakpoints == NULL) {
            if((breakpoints = (int *) malloc(sizeof(int) * 2)) == NULL) {
                fprintf(stderr, "No memory allocatable\n");
                exit(0);
            }
            breakpoints[0] = atoi(break_pos);
        }
        else {
            if((breakpoints = (int *) realloc(breakpoints, sizeof(int) * (amount + 2))) == NULL) { 
                fprintf(stderr, "No memory allocatable\n");
               exit(0);
            }
            breakpoints[amount] = atoi(break_pos);
        }
        breakpoints[amount + 1] = -1;
        amount++;
        return breakpoints;
    }
    
    int run_mode(int *noclear, int *max, int *counter, int *breakpoints, char *word, char *vars) {
        STATE *cur_pos = start;
        int i, j;
        int pos = 0;
        int target_buf;
        char *head = word;
        int steps = 0;
        if(word == NULL) {
            fprintf(stderr, "No word specified\n");
            return 0;
        }
        while(1) {
            /*breakpoint implementation*/
            if(breakpoints != NULL) {
                for(j = 0; (breakpoints[j] != pos) && (breakpoints[j] != -1); j++)
                    ; //is the actual position in the breakpoints?
                if(pos == breakpoints[j]) {
                    printf("breakpoint reached with word %s\n", word);
                    getchar();
                }
            }
            /*word[0] in q_n*/
            for(i = 0; (vars[i] != head[0]) && (i < (strlen(word) + 1)); i++)
                ; //Find my element and save it to the counter
            if(i == (strlen(word) + 1)) {
                fprintf(stderr, "Not an accepted word\n");
                printf("%c not recognized in state %d\n", head[0], pos);
                return 0;
            }
            /*Can we halt?*/
            if(cur_pos->write[i] == '-') {
                printf("Program halted with word: %s and %d steps\n", word, steps);
                free(word);
                return 0;
            }
            /*write the sign, which the read sign wants*/
            head[0] = cur_pos->write[i];
            if(cur_pos->head[i] == 'R') {
                /*Is there place to the right?*/
                if(head[1] == '\0') {
                    /*hm, maybe ptrptr is few nicer*/
                    word = realloc(word, strlen(word) + 2);
                    /*go to the end of the allocated string*/
                    head = word + strlen(word) - 1;
                    head[1] = '#';
                    head[2] = '\0';
                }
                head++;
            }
            else if(cur_pos->head[i] == 'L') {
                /*Is there place to the left?*/
                if((head - 1) < word) {
                    word = realloc(word, strlen(word) + 2);
                    word[strlen(word) + 1] = '\0'; //just 4 valgrind
                    memmove(word + 1, word, strlen(word));
                    head = word + 1;
                    word[0] = '#';
                }
                head--;
            }
            /*go to the targeted state*/
            if(pos < cur_pos->targets[i]) {
                /*if I change the cur_pos, I'll change the abort condition*/
                target_buf = cur_pos->targets[i];
                for(; pos < target_buf; pos++) {
                    cur_pos = cur_pos->next;
                }
            }
            else if(pos > cur_pos->targets[i]) {
                /*if I change the cur_pos, I'll change the abort condition*/
                target_buf = cur_pos->targets[i];
                for(; pos > target_buf; pos--) {
                    cur_pos = cur_pos->previous;
                }
            }
        steps++;
        }
        return 0; //never reached
    }
    
    void step_mode(int *noclear, int *max, int *counter, int *breakpoints, char *word, char *vars) {
        STATE history;
        STATE *cur_state = start;
        char *head = word;
        char input = 's';
        int i = 0;
        int j = 0;
        printf("Welcome to the stepping mode\n");
        printf("Quit stepping mode with q or quit\n");
        printf("Exit program with exit\n");
       /* run_mode();
        char *write;
        char *head;
        int *targets;
        struct state *previous;
        struct state *next;*/
        /*history for stepping back*/
        history.write = (char *) malloc(2);
        history.head = (char *) malloc(2);
        history.targets = (int *) malloc(2);
        do {
            history.write = (char *) realloc(history.write, j + 1);
            history.head = (char *) realloc(history.head, j + 1);
            history.targets = (int *) realloc(history.targets, j + 1);
            if(input == 's') {
                history.write[j] = cur_state->write[i];
                history.head[j] = cur_state->head[i];
                history.targets[j] = cur_state->head[i];
            }
            else {
            }
            scanf("%c", &input);
            /*todo clear \n from buf*/
    
        } while((input == 's') || (input == 'b'));
        /*show the position with the ^ sign*/
        printf("\t\t%s\n", word);
        printf("\t\t");
        for(int count = 0; count < (head - word - 1); count ++) 
            printf(" ");
        printf("^\n");
    }
    
    void clear_structs(char *vars) {
        STATE *next_start;
        while(NULL != start) { 
               next_start = start->next;
               free(vars);
               free(start->write);
               free(start->head);
               free(start->targets);
               free(start);
               start = next_start;
        }
    }
    
    /*copied from the GNU C library to comile the source in NON_POSIX_SYSTEMS*/
    #ifdef __USE_GNU
    # define __rawmemchr rawmemchr
    #else
    # define __rawmemchr strchr
    #endif
    
    char *strtok_r (char *s, const char *delim, char **save_ptr)
    {
      char *token;
    
      if (s == NULL)
        s = *save_ptr;
    
      /* Scan leading delimiters.  */
      s += strspn (s, delim);
      if (*s == '\0')
        {
          *save_ptr = s;
          return NULL;
        }
    
      /* Find the end of the token.  */
      token = s;
      s = strpbrk (token, delim);
      if (s == NULL)
        /* This token finishes the string.  */
        *save_ptr = __rawmemchr (token, '\0');
      else
        {
          /* Terminate the token and make *SAVE_PTR point past it.  */
          *s = '\0';
          *save_ptr = s + 1;
        }
      return token;
    }
    

    /*turing.csv*/

    D,q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11,q12,q13,q14,q15,q16,q17,q18,q19,q20,q21,q22,q23,q24
    #,---,"#, L, q3","#, L, q4","a, L, q18","b, L, q18","a, L, q9","b, L, q9","a, L, q14","b, L, q14","a, R, q13","a, R, q13",---,---,"#, L, q18","b, R, q13","b, R, q13",---,---,"#, L, q19","#, R, q20","#, R, q23","a, R, q20","b, R, q20","#, N, q24",---
    a,"#, R, q1","a, R, q1","a, R, q2","a, R, q5","a, R, q7",---,---,---,---,"a, L, q10","a, R, q11","a, L, q9","b, L, q9","#, R, q0","a, L, q15","a, R, q16","a, L, q14","b, L, q14","a, L, q19","a, L, q18","a, R, q21",---,---,"a, R, q21",---
    b,"#, R, q2","b, R, q1","b, R, q2","b, R, q6","b, R, q8",---,---,---,---,"b, L, q10","b, R, q12","a, L, q9","b, L, q9","#, R, q0","b, L, q15","b, R, q17","a, L, q14","b, L, q14","b, L, q19","b, L, q18","b, R, q22",---,---,"b, R, q22",---
    


  • Hallo,

    du rufst free(vars) in der Mehtode clear_structs(char* vars) wiederholt auf, was nach der manpage von free:

    or if free(ptr) has already been called before, undefined
    behavior occurs

    tut.

    Ansonsten: Schönes Programm :).



  • Kopf -> Wand
    Danke für das Feedback, anscheinend ist mir das free verrutscht. Wie konnte ich das nur nicht sehen...



  • Bastel dir einfach eine kleine malloc und free funktion. In der lässt du dann einen Zähler bei jedem malloc erhöhen und bei free erniedrigen. Damit erschlägt man ganz leicht eine Menge dieser Fehler und es ist kein großer Aufwand.

    Oder du gehst in die Leere bei knivil, dann lernst du 100% fehlerfreie Software zu schreiben!!!



  • Lehre natürlich(scheiß Rechtschreibprüfung)



  • memcounter schrieb:

    Bastel dir einfach eine kleine malloc und free funktion. In der lässt du dann einen Zähler bei jedem malloc erhöhen und bei free erniedrigen. Damit erschlägt man ganz leicht eine Menge dieser Fehler und es ist kein großer Aufwand.

    Eigentlich macht das (und viel mehr) ja bereits Valgrind fuer einen. Warum auch immer das beim Threadstarter nicht funktioniert hat.



  • Valgrind hat mir den Fehler schon rausgeschmissen. Ich hab ihn aber nicht ganz verstanden. Mein Programm jedoch hat mit valgrind funktioniert.



  • Dass valgrind keine Fehler anzeigt heißt nicht, dass das Programm fehlerfrei ist.
    Was soll an diesem Programm "schön" sein?
    Was passiert bei leeren Feldern mit strtok_r?
    Was soll der Unsinn mit !sscanf?


Anmelden zum Antworten