Как предотвратить запись asprintf над переменными в куче?
Я использую 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 ответ
Вы освобождаете свой files
массив после перераспределения его будет больше. поскольку files == tmp
(потому что вы только что назначены tmp
в files
), ваш звонок free(tmp)
освобождает память, которая files
указывает на. Так, files
затем висячий указатель на освобожденную память. Эта память, вероятно, позже повторно используется для filename
одного из отдельных файлов.