Как предотвратить запись asprintf над переменными в куче? - PullRequest
0 голосов
/ 19 мая 2018

Я использую asprintf для динамического выделения памяти и загрузки строк для хранения информации о файлах в рабочем каталоге.

В 273-м (точно и последовательно) вызове функции parse_entry эта строка выполняется: file->filename_len = asprintf(&file->filename, "%s", entry->d_name); и перезаписывает структуру, на которую указывает files[0].

Это вывод gdb, поскольку я запускал 273-ю итерацию построчно:

до выполнения вышеупомянутогострока:

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

после исполнения:

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

Код прилагается ниже.Обратите внимание, что это своего рода минимальный пример для демонстрации проблем, с которыми я сталкиваюсь.

Как мне предотвратить это?

#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;
}

1 Ответ

0 голосов
/ 19 мая 2018

Вы освобождаете свой массив files после того, как перераспределяете его для увеличения.Поскольку files == tmp (поскольку вы только что присвоили tmp files), ваш вызов free(tmp) освобождает память, на которую указывает files.Таким образом, files - это висячий указатель на освобожденную память.Эта память, вероятно, позже используется повторно для filename одного из отдельных файлов.

...