Unsuccessful fread() of int stored in binary file, segmentation fault [closed]

There seem to be of the order of 10 questions and (mostly) successful answers solving segmentation faults cause by misused fread()'s in C. That being said, I am having such a problem but have not found a solution.

I have a binary file containing an int (call it nbins) and an array of floats (of size nbins). When I try to read this file, it successfully opens and points to the file handle, but then gives a segmentation fault error when reading the nbins int. Here is a minimal example:

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

#define BPATH "/path/to/file"

int main(int agrc, char **argv)
{
    FILE *fd;
    int num;
    char fname[500]={};

    int nbins;
    float *coords;

    num = 5;
    sprintf(fname,"%s/file%d.dat", BPATH, num);

    if(!(fd=fopen(fname,"rb")))
    {
        printf("Can't open file: %s\n\n",fname);
        exit(0);
    }    

    printf("Reading input file:\n");
    printf("%p: %s\n", fd, fname);       // prints successfully

    fread(&nbins, sizeof(int), 1, fd);   
    printf("nbins = %d", nbins);         // seg faults before this print

    /* EDIT: the above print isn't properly flushed without an \n    
     * The seg fault was not caused by the fread(), but the lack of  
     * the above print lead to the confusion                         */

    coords = malloc(nbins * sizeof(float));
    fread(coords, sizeof(float), nbins, fd);

    fclose(fd);
    free(coords);

    return(0);
}

The file was created with the following formatting:

int nbins[1];
nbins[0] = 5;                          // this 5 is just an example...
fwrite(nbins, sizeof(int), 1, file_ptr);
fwrite(coords, sizeof(float), nbins[0], file_ptr);

I have also tried using:

int *nbins = malloc(sizeof(int));
fread(nbins, sizeof(int), 1, fd);

but this did not solve the problem. The file does exist and is readable; I can read it just fine using Python, with NumPy's fromfile(). Am I missing something obvious? Thanks!


Solution 1:

You may have undefined behavior, with the following scenario:

  • int nbins; does not initialize nbins, so it contains junk data, potentially a very large number.

  • fread(&nbins, sizeof(int), 1, fd); is not tested so could fail and keep nbins uninitialized. Read about fread.

  • printf("nbins = %d", nbins); has no \n and is not followed by an explicit fflush so don't show anything (since stdout is usually line-buffered).

  • coords = malloc(nbins * sizeof(float)); would request a huge amount of memory, so would fail and get NULL in coords

  • fread(coords, sizeof(float), nbins, fd); writes to the NULL pointer, giving a segmentation violation, since UB

You are very lucky. Things could be worse (we all could be annihilated by a black hole). You could also experiment some nasal demons, or even worse, have some execution which seems to apparently work.

Next time, please avoid UB. I don't want to disappear in a black hole, so bear with us.

BTW, if you use GCC, compile with all warnings and debug info : gcc -Wall -Wextra -g. It would have warned you. And if it did not, you'll get the SEGV under the gdb debugger. On Linux both valgrind and strace could have helped too.

Notice that useless initialization (e.g. an explicit int nbins = 0; in your case) don't harm in practice. The optimizing compiler is likely to remove them if they are useless (and when they are not useless, as in your case, they are very fast).

Mandatory read

Lattner's blog: What Every C Programmer should know about UB. Related notion: the As-if rule.

Read also the documentation of every function you are using (even as common as printf).