Home > Net >  Invalid free() only when running larger text files through C program
Invalid free() only when running larger text files through C program

Time:03-04

Ive been working on a project which takes whole files as single strings and manipulates them in various ways, but keep getting stuck on a valgrind error when running text files bigger than around ~500 characters. Some code for reference:

My Program:

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

#define MAX_LENGTH 20
#define MAX_WORDS 50

// REQUIRED PROTOTYPES

char * readFile (char * filename);

char * stretchMe (char * aStringToStretch);

int splitMe (char * aStringToSplit, char static2D [MAX_WORDS][MAX_LENGTH]);

int shrinkMe (char * aStringToShrink);

bool isItAPalindrome (char * aString);

void printSuffixes (char * aString, int whichWord, char * desiredSuffix);

// Custom Functions

int checkPunctuation(char x);

// Main

int main(int argc, char **argvs)
{ 
    if(argc < 2)
    {
        puts("Wrong usage when executing");
        exit(EXIT_FAILURE);
    }
    
    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    printf("Txt File: [%s]\n", argvs[1]);
    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    char *ioFileString;
    ioFileString = readFile(argvs[1]);
    printf("%s", ioFileString);

    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    /*
    char *stretchedIoFileString;
    stretchedIoFileString = stretchMe(ioFileString);
    printf("%s", stretchedIoFileString);
    free(stretchedIoFileString);
    */
    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    char static2D [MAX_WORDS][MAX_LENGTH];
    int wordsCounted = splitMe(ioFileString, static2D);
    printf("Word Count :[%d]", wordsCounted);

    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    free(ioFileString);
    return EXIT_SUCCESS;
}

char * readFile (char * filename)
{
    FILE *fp = NULL; // Initialize file pointer

    fp = fopen(filename, "r"); // Open file

    if(fp == NULL) // Check if file was found
    {
        printf("Error: Could not find file %s, please try again", filename);
        exit(-1); // Error
    }

    // First count number of characters in file
    fseek(fp, 0, SEEK_END); // Seek to end of file
    int cCount = ftell(fp); // Counts amount of characters in file, add one for endline.
    fseek(fp, 0, SEEK_SET); // Seek back to the beginning of the file

    char *buffer = calloc((cCount 1), sizeof(char));

    if(buffer == NULL)
    {
        puts("Malloc Failed, exiting");
        exit(EXIT_FAILURE);
    }

    int numRead = fread(buffer, sizeof(char), cCount, fp);

    buffer[cCount] = '\0';

    if(numRead != cCount)
    {
        puts("Did not read correctly, exiting.");
        exit(EXIT_FAILURE);
    }

    fclose(fp);

    return buffer;
}

char * stretchMe (char * aStringToStretch)
{
    const int stringLength = strlen(aStringToStretch);

    int *userInput = calloc(stringLength, sizeof(int));

    int newStringLength = 0;

    printf("Please enter %d integers sequentially:\n", stringLength);

    int inUser;

    for (int i = 0; i < stringLength; i  )
    {
        //scanf("%d", &inUser);

        inUser = 2;

        userInput[i] = inUser;

        if(userInput[i] < 1)
        {
            printf("\nInvalid value: values must be positive\n");
            i--;
        }
        else
        {
            newStringLength = newStringLength   userInput[i];
        }
    }

    char *stretchedString = malloc(sizeof(char)*(newStringLength   1));

    int index = 0;

    for (int i  = 0; i < stringLength; i  )
    {
        for(int j = 0; j < userInput[i]; j  )
        {
            stretchedString[index] = aStringToStretch[i];
            index  ;
        }
    }

    stretchedString[index] = '\0';

    free(userInput);

    return stretchedString;
}

int splitMe (char * aStringToSplit, char static2D [MAX_WORDS][MAX_LENGTH])
{
    const int stringLength = strlen(aStringToSplit);
    const char delim[] = " \n";

    char *buffer = calloc(stringLength 1, sizeof(char)); // Alloc memory for buffer for strtok();
    strcpy(buffer, aStringToSplit); // Copy string to buffer

    char *token;
    token = strtok(buffer, delim);

    int wordCount = 0;
    while(token != NULL)
    {
        puts("Loops");
        printf("%d", wordCount);
        strcpy(static2D[wordCount], buffer);
        wordCount  ;

        token = strtok(NULL, delim);
    }

    free(buffer);

    return wordCount;
}

/*int shrinkMe (char * aStringToShrink)
{
    int puncCount = 0;
    int tempIndex = 0;

    int stringLength = strlen(aStringToShrink);

    char *tempShrinked = malloc(sizeof(char)*stringLength);

    for(int i = 0; aStringToShrink[i] != '\0'; i  )
    {
        if(checkPunctuation(aStringToShrink[i]) == 1)
        {
            puncCount  ;
        }
        else
        {
            tempShrinked[tempIndex] = aStringToShrink[i];
            tempIndex  ;
        }
    }

    tempShrinked[tempIndex] = '\0';
    
    strcpy(aStringToShrink, tempShrinked);

    printf("%s", tempShrinked);
    printf("%s", aStringToShrink);
    return puncCount;
}

bool isItAPalindrome (char * aString)
{
    return true;
}

void printSuffixes (char * aString, int whichWord, char * desiredSuffix)
{

}*/

int checkPunctuation(char x)
{
    switch (x)
    {  
    case '.':
    case ':':
    case ';':
    case '?':
    case '!':
        return 1; // If any of the above cases are found, the case flows down the line to the last
        break;

    default:
        return 0;
        break;
    }
}

I get no errors when calling readFile(); by itself, it allocates and frees fine. It is only when it is a larger file and the function splitMe(); is called, Valgrind reports 2 errors:

==19545== Invalid free() / delete / delete[] / realloc()
==19545==    at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==19545==    by 0x109335: main (main.c:35)
==19545==  Address 0x65685404a26730 is not stack'd, malloc'd or (recently) free'd
==19545== 
==19545== 
==19545== HEAP SUMMARY:
==19545==     in use at exit: 733 bytes in 1 blocks
==19545==   total heap usage: 7 allocs, 7 frees, 15,627 bytes allocated
==19545== 
==19545== Searching for pointers to 1 not-freed blocks
==19545== Checked 67,600 bytes
==19545== 
==19545== 733 bytes in 1 blocks are definitely lost in loss record 1 of 1
==19545==    at 0x4837B65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==19545==    by 0x1093E0: readFile (functions.c:24)
==19545==    by 0x10928B: main (main.c:17)
==19545== 
==19545== LEAK SUMMARY:
==19545==    definitely lost: 733 bytes in 1 blocks
==19545==    indirectly lost: 0 bytes in 0 blocks
==19545==      possibly lost: 0 bytes in 0 blocks
==19545==    still reachable: 0 bytes in 0 blocks
==19545==         suppressed: 0 bytes in 0 blocks
==19545== 
==19545== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==19545== 
==19545== 1 errors in context 1 of 2:
==19545== Invalid free() / delete / delete[] / realloc()
==19545==    at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==19545==    by 0x109335: main (main.c:35)
==19545==  Address 0x65685404a26730 is not stack'd, malloc'd or (recently) free'd
==19545== 
==19545== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

(The 733 bytes is the space allocated by that first calloc in readFile)

Im assuming maybe it has something to do with a combination of the calloc(); in readFile and the strcpy(); in splitMe? Any help is appreciated. Thanks.

CodePudding user response:

Well for a start you make the huge assumption that you can fit the entire file into memory. But dont check that it worked

// First count number of characters in file
fseek(fp, 0, SEEK_END); // Seek to end of file
int cCount = ftell(fp); // Counts amount of characters in file, add one for endline.
fseek(fp, 0, SEEK_SET); // Seek back to the beginning of the file


char* buffer = calloc((cCount   1), sizeof(char));

int numRead = fread(buffer, sizeof(char), cCount, fp);

You have to check the return from calloc

then at the start of splitME even if that one worked you copy the entire file to another heap allocation with no test if it worked.

const int stringLength = strlen(aStringToSplit);
const char delim[] = " \n";

char* buffer = calloc(stringLength   1, sizeof(char)); // Alloc memory for buffer for strtok(); <<<<=== check this retunrn
strcpy(buffer, aStringToSplit); // Copy string to buffer

so you are attempting to hold 2 copies of the file in memory at once

With a 500 byte file this is probably OK, but this is very poor code

The actual reason you are failing is because you don't check to see if you have > MAX_WORDS of if a word is larger than MAX_LENGTH

Also note that you program won't work on windows. You find the length of the file including all the CRLFs at the end of the line, but open the file in text mode, fread will drop the LFs so your test to see if you read the correct number of chars will fail

  • Related