Я использую 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;
}