简体   繁体   中英

How do I prevent asprintf writing over variables on heap?

I'm using asprintf to dynamically allocate memory and load strings to store information about files in working directory.

In the 273rd (exactly and consistently) call of function parse_entry this line gets executed: file->filename_len = asprintf(&file->filename, "%s", entry->d_name); and overwrites the structure pointed to by files[0] .

This is the output from gdb as I ran the 273rd iteration line by line:

before the execution of aforementioned line:

p *files[0]
{filename = 0x60b8f0 ".", filename_len = 1, user = 0x60b8b0 "root", user_len = 4}

after the execution:

p *files[0]
{filename = 0x746e6175716d7070 <Address 0x746e6175716d7070 out of bounds>, 
filename_len = 6340608, user = 0x60c080 "\260\300`", user_len = 70433}

The code is attached bellow. Note that this is kind of minimal example to demonstrate the problems I am having.

How do I prevent this from happening?

#define _GNU_SOURCE
#include <dirent.h>
#include <pwd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

#define SIZE 255

typedef struct {
    char *filename;
    int filename_len;
    char *user;
    int user_len;
} file_t;

void free_file(file_t *f)
{
    if (!f) {
        return;
    }
    if (f->filename) {
        free(f->filename);
    }
    if (f->filename) {
        free(f->user);
    }
    free(f);
}

void free_files(file_t **files, size_t count)
{
    if (!files) {
        return;
    }
    for (size_t i = 0; i < count; i++) {
        free_file(files[i]);
    }
    free(files);
}

file_t *parse_entry(struct dirent *entry)
{
    file_t *file = malloc(sizeof(file_t));

    file->filename_len = asprintf(&file->filename, "%s", entry->d_name);
    if (file->filename_len == -1) {
        perror("While allocating memory for filename: ");
        file->filename = NULL;
        goto fail;
    }

    struct stat info;
    if (stat(file->filename, &info)) {
        perror("Can't stat file: ");
        goto fail;
    }

    struct passwd *passwd = getpwuid(info.st_uid);
    if (passwd) {
        file->user_len = asprintf(&file->user, "%s", passwd->pw_name);
    } else {
        perror("While getting username. Using uid instead.");
        file->user_len =
            asprintf(&file->user, "%ju", (intmax_t) info.st_uid);
    }
    if (file->user_len == -1) {
        perror("While allocating memory for username: ");
        file->user = NULL;
        goto fail;
    }

    return file;

fail:
    free(file);
    return NULL;
}

file_t **load_files(size_t *count)
{
    *count = 0;
    size_t size = SIZE;
    file_t **files = malloc(SIZE * sizeof(file_t *));
    DIR *dir = NULL;
    if ((dir = opendir("."))) {
        struct dirent *entry = NULL;
        while ((entry = readdir(dir)) != NULL) {
            if (*count >= size) {
                size = size + SIZE;
                file_t **tmp =
                    realloc(files, size * sizeof(file_t *));
                if (tmp) {
                    files = tmp;
                    free(tmp);
                } else {
                    return NULL;
                }
            }
            file_t *file = parse_entry(entry);
            if (file) {
                files[(*count)] = file;
            } else {
                fprintf(stderr,
                    "Can't get information about %s skipping",
                    entry->d_name);
                continue;
            }
            // is the structure overwritten yet?
            printf("loaded %lu %s %s\n", *count,
                    files[0]->user,
                    files[0]->filename);
            (*count)++;
        }
    } else {
        return NULL;
    }
    closedir(dir);
    return files;
}

void print_files(file_t **files, size_t count)
{
    for (size_t i = 0; i < count; i++) {
        printf("%s %s\n", files[i]->user, files[i]->filename);
    }
}

int main()
{
    size_t file_count;
    file_t **files = load_files(&file_count);
    if (!files) {
        free_files(files, file_count);
        return 1;
    }

    // do other stuff with files
    print_files(files, file_count);
    free_files(files, file_count);

    return 0;
}

You are deallocating your files array after you reallocate it to be larger. Since files == tmp (because you just assigned tmp to files ), your call to free(tmp) frees the memory that files points to. So, files is then a dangling pointer to freed memory. That memory is presumably later reused for the filename of one of the individual files.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM