0

I've come across a weird issue where I receive a segmentation fault when trying to close the file. Data is being written to the file correctly, could there be some sort of race condition happening between fflush and fclose?

//main.c

#define filename "/home/test.txt";
char fbuf[255];
sprintf(fbuf, "%s, %f, %f,%f \n", "big", 8.0,0.0,0.8);

sendFile(&fbuf, sizeof(fbuf), (void *)filename);

static void
sendFile( void *data, int size, char *pName)
{
    FILE *pFile = fopen(pName,"a");
    char *buf = NULL;
    buf = (char *)malloc(255);
    memcpy(buf, data, sizeof(char *)*size);

    if(pFile == NULL) {
        logger(LOG_INFO, "Error opening file\n");
}
    else {
        fwrite(buf, 1, strlen(buf), pFile);
        fflush(pFile);
        fclose(pFile);
    }
    free (buf);
}

Any help or suggestions are greatly appreciated.

txcotrader
  • 585
  • 1
  • 6
  • 21
  • 2
    Why do you think it's`fclose` that is causing the segfault? – Eugene Sh. Sep 03 '15 at 17:05
  • 3
    Please don't cast the result of malloc. – Martin Sep 03 '15 at 17:06
  • 1
    Why are you allocating 255 bytes for `buf`, while `size` is readily available? Couldn't it overflow it? – Eugene Sh. Sep 03 '15 at 17:07
  • I removed my debug statements - the seg fault occurs when fclose(pFile) is called – txcotrader Sep 03 '15 at 17:09
  • @txcotrader It doesn't mean it is the *real* cause. – Eugene Sh. Sep 03 '15 at 17:09
  • @EugeneSh.Worse, the later `strlen()` is not guaranteed to be terminated. I don't even see the point of the `memcpy()`. – Andrew Henle Sep 03 '15 at 17:09
  • Ok, I think there is much to fix for now. Come back when done :) – Eugene Sh. Sep 03 '15 at 17:10
  • You have passed `size` to the function and done quite strange things with it. In any case the data size is `1+sprintf(...)`, not the size of its buffer. – Weather Vane Sep 03 '15 at 17:12
  • 7
    @WeatherVane - Why even bother with the `malloc()` and `memcpy()`? Given the logic posted, a simple `fwrite( data, 1, strlen( data ), pFile );` does what the code posted seems to be intended to do. – Andrew Henle Sep 03 '15 at 17:15
  • Or if there were some advantage to copying the data prior to writing it, then a static array declaration (`char buf[255];`) is much better than `char *buf = malloc(255); ... free(buf);`. – John Bollinger Sep 03 '15 at 17:31
  • I didn't realize just providing an address location to strlen would return the string length. Thanks for your response WeatherVane, your snippet solved the issue – txcotrader Sep 03 '15 at 17:31
  • 2
    I think you mean @AndrewHenle whose comment begins with my name. – Weather Vane Sep 03 '15 at 17:34
  • Thank you Andrew Henle, you are smart and awesome! – txcotrader Sep 03 '15 at 17:40
  • 1
    You should read more about #define and preprocessor s, until then remove the semicolon. – Michi Sep 03 '15 at 18:49
  • When asking a question about a runtime problem, always post code that cleanly compiles. The posted code is missing all the required #include statements. Are we to guess which header files are being included in your actual code? – user3629249 Sep 04 '15 at 17:34
  • @AndrewHenle, the call to `strlen` will return 3 because the first item into the buffer is 'b','i', 'g', '\0' – user3629249 Sep 04 '15 at 18:49

2 Answers2

4

I think the problem is in memcpy(buf, data, sizeof(char *)*size).

Shouldn't it be simply memcpy(buf, data, size)?

Looking at your example, fbuf (i.e. data) is 255 chars, and buf is 255 chars as well. But the memcpy is copying more than one thousand chars effectively writing garbage into the heap, with unpredictable results.

Mario Zannone
  • 2,843
  • 12
  • 18
  • That won't make a difference. One real problem is what happens if `size` is greater than 255 divided by the size of a `char *`. – Andrew Henle Sep 03 '15 at 17:10
  • 1
    @AndrewHenle: well, it will make a difference, since `sizeof(char*)*size` is much much bigger that `size` (4 or 8 times `size`, depending on the implementation) – Mario Zannone Sep 03 '15 at 17:17
  • 2
    Yeah, but he's also using `strlen()` while not guaranteeing the copied data is terminated. Just write the string to the file. There are lots of issues with just the code posted. – Andrew Henle Sep 03 '15 at 17:23
  • 2
    The incorrect size parameter in `memcpy()` does not overwrite the stack but the heap and this most likely causes the FILE * data structures to be overwritten -> coredump in `fclose()`. – schily Sep 03 '15 at 18:49
  • @schily: oops! Thanks. You are right. I will fix the answer. – Mario Zannone Sep 03 '15 at 18:52
  • this answer is not the only reason the OPs code causes a seg fault event. Amongst other things, opening a file in the `/home` directory and calling free() when the call to fopen() fails – user3629249 Sep 04 '15 at 18:45
1

Here is a version of the code that works, with embedded comments about why things are different from the posted code.

Note: On linux (and other OSs) the /home directory is not writeable without administrative privileges. so the call to fopen() will always fail.

//main.c

#include <stdio.h>  // fwrite, fopen, fclose, perror
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string.h> // strlen, sprintf, memcpy

// note: no comments at end of #define statement as the comment would be copied into the code
// note no ';' at end of #define statement
#define filename "/home/test.txt"
 // to avoid using 'magic' numbers in code
 // and this name used throughout the code
#define BUF_SIZE (255)

// prototypes
void sendFile( char *, char *); // so compiler does not make incorrect assumptions
                                // and no second parameter needed

char fbuf[BUF_SIZE] = {'\0'}; // avoid 'magic' numbers
                              // avoid placing trash into output file
                              // however, will place many NUL bytes

// main() function to make executable platform for testing
int main( void )
{
    sprintf(fbuf, "%s, %f, %f,%f \n", "big", 8.0, 0.0, 0.8);
     // changed 3rd parameter type to match actual function parameter type
    sendFile(fbuf, filename); // no '&' because in C
                              // array name degrades to address of array
    return 0;
}

void sendFile( char *data, char *pName) // use actual parameter types
{
    FILE *pFile = fopen(pName,"a");
    if(pFile == NULL)  // always check for error immediately after call to system function, not later
    { // then fopen failed
         perror( "fopen failed" );
        //logger(LOG_INFO, "Error opening file\n");
        exit( EXIT_FAILURE); // exit, so no other code executed
    }

    // implied else, fopen successful

    char *buf = NULL;
    if(NULL == (buf = malloc(BUF_SIZE) ) ) // don't cast returned value
    { // then malloc failed   -- always check for error immediately after call to system function
        perror( "malloc for output buffer failed");
        //logger(LOG_INFO, "malloc for BUF_SIZE failed");
        fclose( pFile); // cleanup
        exit(EXIT_FAILURE);  // exit, so no other code executed
    }

    // implied else, malloc successful

    memcpy(buf, data, BUF_SIZE); // using original 3rd parameter would move 4 times as many bytes,
                             // I.E. past the end of the source buffer and past the end of the destination buffer
                             // resulting in undefined behaviour, leading to a seg fault event
                             // this memcpy() and the destination buffer
                             // are unneeded as first passed in parameter
                             // contains ptr to the source buffer
                             // which can be used in the call to fwrite()

    fwrite(buf, BUF_SIZE, 1, pFile); // buf will have NUL byte immediately after 'big' so strlen() would return 3
                                     // and the syntax for fwrite is source buffer, size of one item, number of items, FILE*
                                     // and this parameter order is best
                                     // because, if error checking,
                                     // can just compare returned value to 1
    fflush(pFile);  // not actually needed as the fclose() performs a flush
    fclose(pFile);

    free (buf);     // original code did this even if malloc had failed
                    // and even if the fopen had failed
                    // which would have corrupted the 'heap' leading to a seg fault event
}
user3629249
  • 16,402
  • 1
  • 16
  • 17