Reputation:
I was writing a function to read a file into memory and encountered a strange problem. The returned pointer could only be free()'ed successfully if it was intialized to NULL before passing it to the function. This is the code,
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
ssize_t
file_size(const char *path)
{
struct stat stats;
if (stat(path, &stats))
return (-1);
return (stats.st_size);
}
ssize_t
read_file(const char *path, char *data)
{
data = NULL;
FILE *fp;
ssize_t tmp;
size_t fl;
tmp = file_size(path);
if (tmp < 0) goto err;
fl = (size_t)tmp;
data = malloc(fl);
if (data == NULL) goto err;
fp = fopen(path, "rb");
if (fp == NULL) goto err;
if (fread(data, 1L, fl, fp) != fl) goto err;
if (ferror(fp)) goto err;
fclose(fp);
return (fl);
err:
if (fp)
{
int errno_save = errno;
fclose(fp);
errno = errno_save;
}
free(data);
return (-1);
}
int
main(void)
{
char *fdata; // If this is initialized to NULL, then the program works fine
size_t fdata_len;
fdata_len = read_file("/home/varad/wallpaper.jpg", fdata);
printf("read %zu bytes.\n", fdata_len);
free(fdata); // Segfault happens over here
return (0);
}
If the variable 'fdata' in the main function is not initialized to NULL, the program fails with the following output:
read 2599298 bytes.
Segmentation fault (core dumped)
However, if it is initialize to NULL, then it apparently runs as expected with the following output:
read 2599298 bytes.
I have tried to debug this program with gdb, and the segmentation fault happens at the free() call at the end of the main function. Any help will be appreciated.
Thanks in advance, Varad
Upvotes: 0
Views: 50
Reputation: 67476
read_file(const char *path, char *data)
data
is a local variable. You changes are only local to this function. You need to pass reference to pointer.
ssize_t
read_file(const char *path, char **data)
{
FILE *fp = NULL;
ssize_t tmp;
size_t fl;
if(!data) goto err;
tmp = file_size(path);
if (tmp < 0) goto err;
fl = (size_t)tmp;
*data = malloc(fl);
if (*data == NULL) goto err;
fp = fopen(path, "rb");
if (fp == NULL) goto err;
if (fread(*data, 1L, fl, fp) != fl) goto err;
if (ferror(fp)) goto err;
fclose(fp);
return (fl);
err:
if (fp)
{
int errno_save = errno;
fclose(fp);
errno = errno_save;
}
if(data) free(*data);
return (-1);
}
and call accordingly:
fdata_len = read_file("/home/varad/wallpaper.jpg", &fdata);
Upvotes: 1