Search code examples
ccrashfreedynamic-memory-allocation

Calling the free command to deallocate memory crashes my program. Why?


I wrote a program that encryts and decrypts message using vigenere ciphers. The encrypting part works fine, the problem is the decrypting part: When I want to deallocate the char* using free() my program crashes. When I do not deallocate it, the program works fine and doesn't crash however. Here is the program code:

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

int main(void) {
    int choice, check, error;
    char ch;
    printf("What do you want to do?\n"
           "1...Decode a message that is saved in a ciphertext.txt file.\n"
           "2...Encode a message and save it in a ciphertext.txt file.\n");
    do {
        error = 0;
        check = scanf("%d%c", &choice, &ch);
        if (check != 2 || ch != '\n' || choice < 1 || choice > 2) {
            printf("Error: Invalid input!\n");
            error = 1;
        }
        fflush(stdin);
    } while (error);
    if (choice == 2) {
        FILE *fp = fopen("ciphertext.txt", "w+");
        char buffer[1000];
        char *p, *q, *r;
        char *encryption_text;
        char *encrypted;
        char *decrypted;
        printf("What are bad characters?\n");
        printf("-Everything except A-Z and a-z.\n");
        printf("Plaintext: ");
        gets(buffer);
        decrypted = (char *)malloc(strlen(buffer));
        strcpy(decrypted, buffer);
        printf("Key: ");
        gets(buffer);
        encryption_text = (char *)malloc(strlen(buffer));
        strcpy(encryption_text, buffer);
        encrypted = (char *)malloc(strlen(decrypted));
        strcpy(encrypted, decrypted);
        putchar('\n');
        p = decrypted;
        q = encryption_text;
        r = encrypted;
        if (strlen(q) > strlen(p)) {
            printf("The key has to be shorter or equal length as the plaintext.");
            return 0;
        }
        while (*p != '\0') {
            if (!(*p >= 'A' && *p <= 'Z') && !(*p >= 'a' && *p <= 'z')) {
                printf("Bad characters.");
                return 0;
            }
            if (*q == '\0') {
                q = encryption_text;
            } else if (!(*q >= 'A' && *q <= 'Z') && !(*q >= 'a' && *q <= 'z')) {
                printf("Bad characters.");
                return 0;
            }
            if (*p >= 'a' && *p <= 'z') *p -= ' ';
            if (*q >= 'a' && *q <= 'z') *q -= ' ';
            *r = (*p - 'A' + *q - 'A') % 26 + 'A';
            p++;
            q++;
            r++;
        }
        printf("Ciphertext: ");
        puts(encrypted);
        fputs(encryption_text, fp);
        fprintf(fp, ";");
        fputs(encrypted, fp);
        free(decrypted);
        free(encrypted);
        free(encryption_text);
        fclose(fp);
    } else {
        FILE *fp = fopen("ciphertext.txt", "r+");
        char buffer[1000];
        char *encryption_text;
        char *encrypted;
        char *decrypted;
        char delimeter[2] = ";";
        char *token;
        char *p, *q, *r;
        fgets(buffer, 1000, fp);
        encrypted = (char *)calloc(strlen(buffer) * sizeof(char), sizeof(char));
        encryption_text = (char *)calloc(strlen(buffer) * sizeof(char), sizeof(char));
        token = strtok(buffer, delimeter);
        strcpy(encryption_text, token);
        encrypted = strrchr(buffer, '\0');
        if (encrypted != NULL) {
            strcpy(encrypted, encrypted + 1);
        }
        decrypted = (char *)calloc(strlen(buffer) * sizeof(char), sizeof(char));
        for (p = decrypted, q = encryption_text, r = encrypted;
                *r != '\0'; p++, r++, q++) {
            if (*q == '\0') {
                q = encryption_text;
            }
            *p = (*r - 'A' - (*q - 'A') + 26) % 26 + 'A';
        }
        printf("Plaintext: ");
        puts(decrypted);
        printf("Key: ");
        puts(encryption_text);
        printf("Ciphertext: ");
        puts(encrypted);
        if (decrypted) free(decrypted);
        if (encrypted) free(encrypted);
        if (encryption_text) free(encryption_text);
        fclose(fp);
    }
    return 0;
}

I hope you can tell me why it crashes. Thanks!


Solution

  • The main reason for the crash is probably the following line,

        encrypted = strrchr(buffer, '\0');
    

    in which you let encrypted point somewhere in the memory represented by local variable buffer. This is most likely memory on the stack, but at least this memory has not been allocated through malloc/calloc.

    Hence freeing encrypted later on will most likely crash at the free-operation.

    BTW: as mentioned in the comments, rethink your malloc/calloc-statements when using strcpy in order to have space for the terminating '\0'.

    Further, note that strrchr(buffer, '\0') returns a pointer to the end of the string contained in buffer; copying memory from a position right after the terminating '\0' using strcpy may be critical, since it is not guaranteed that there will be a second string terminating character.

    encrypted = strrchr(buffer, '\0');
            if (encrypted != NULL) {
                strcpy(encrypted, encrypted + 1);
            }