C 如何防止asprintf在堆上的变量上写入?
我正在使用C 如何防止asprintf在堆上的变量上写入?,c,pointers,heap-corruption,asprintf,C,Pointers,Heap Corruption,Asprintf,我正在使用asprintf动态分配内存并加载字符串,以存储有关工作目录中文件的信息 在函数parse_entry的第273次(精确且一致)调用中,执行这一行:file->filename\u len=asprintf(&file->filename,“%s”,entry->d_name)并覆盖文件[0]指向的结构 这是当我逐行运行第273次迭代时,gdb的输出: 在执行上述行之前: p *files[0] {filename = 0x60b8f0 ".", filename_len = 1, u
asprintf
动态分配内存并加载字符串,以存储有关工作目录中文件的信息
在函数parse_entry
的第273次(精确且一致)调用中,执行这一行:file->filename\u len=asprintf(&file->filename,“%s”,entry->d_name)
并覆盖文件[0]
指向的结构
这是当我逐行运行第273次迭代时,gdb
的输出:
在执行上述行之前:
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}
p*文件[0]
{filename=0x746e6175716d7070,
文件名\u len=6340608,用户=0x60c080“\260\300`”,用户\u 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;
}
定义GNU源
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义大小255
类型定义结构{
字符*文件名;
int filename_len;
字符*用户;
int user_len;
}文件;
无效自由文件(文件\u t*f)
{
如果(!f){
返回;
}
如果(f->filename){
免费(f->filename);
}
如果(f->filename){
免费(f->用户);
}
免费(f);
}
无效自由文件(文件**文件,大小计数)
{
如果(!文件){
返回;
}
对于(大小i=0;i文件名\u len=asprintf(&file->filename,“%s”,条目->文件名);
如果(文件->文件名\u len==-1){
perror(“为文件名分配内存时:”);
文件->文件名=NULL;
走向失败;
}
结构统计信息;
if(stat(文件->文件名和信息)){
perror(“无法统计文件:”);
走向失败;
}
结构passwd*passwd=getpwuid(info.st\u uid);
if(passwd){
file->user\u len=asprintf(&file->user,“%s”,passwd->pw\u name);
}否则{
perror(“获取用户名时,使用uid。”);
文件->用户文件=
asprintf(&file->user,“%ju”,(intmax\t)info.st\u uid);
}
如果(文件->用户长度==-1){
perror(“为用户名分配内存时:”);
文件->用户=NULL;
走向失败;
}
返回文件;
失败:
免费(文件);
返回NULL;
}
文件**加载文件(大小*计数)
{
*计数=0;
大小=大小;
文件**files=malloc(大小*大小);
DIR*DIR=NULL;
如果((dir=opendir(“.”)){
struct dirent*entry=NULL;
while((entry=readdir(dir))!=NULL){
如果(*计数>=大小){
尺寸=尺寸+尺寸;
文件**tmp=
realloc(文件,大小*sizeof(文件*);
如果(tmp){
文件=tmp;
免费(tmp);
}否则{
返回NULL;
}
}
file_t*file=parse_条目(条目);
如果(文件){
文件[(*计数)]=文件;
}否则{
fprintf(标准,
“无法获取有关%s跳过的信息”,
输入->d_名称);
继续;
}
//结构被覆盖了吗?
printf(“加载的%lu%s%s\n”,*计数,
文件[0]->用户,
文件[0]->文件名);
(*计数)+;
}
}否则{
返回NULL;
}
closedir(dir);
归还文件;
}
作废打印文件(文件**文件,大小计数)
{
对于(大小i=0;i用户,文件[i]->文件名);
}
}
int main()
{
大小\u t文件\u计数;
文件**文件=加载文件(&文件计数);
如果(!文件){
自由文件(文件、文件计数);
返回1;
}
//用文件做其他事情
打印文件(文件、文件计数);
自由文件(文件、文件计数);
返回0;
}
在将文件重新分配为更大的数组后,您将取消分配该数组。由于files==tmp
(因为您刚刚将tmp
分配给files
),您对free(tmp)
的调用将释放files
指向的内存。因此,文件
是指向已释放内存的悬空指针。该内存可能稍后会被用于某个单独文件的文件名。甚至不需要tmp
变量,只需执行files=realloc(…)
@Barmar实际上需要tmp
,因为realloc
可以返回NULL
,文件
需要释放。(现在我觉得自己很笨……@balast True——这是您的代码缺少的另一件事。)