user18051276
user18051276

Reputation:

Pointer passed to function which allocates memory to it only works if pointer NULL intialized

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

Answers (1)

0___________
0___________

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

Related Questions