read()因地址错误而失败,valgrind显示Syscall param read(buf)指向不可寻址字节
我有一个函数,可以使用read()因地址错误而失败,valgrind显示Syscall param read(buf)指向不可寻址字节,c,linux,realloc,C,Linux,Realloc,我有一个函数,可以使用read()系统调用读取一个文件,并返回一个char指针和从文件读取的数据。如果需要,该函数将重新分配空间。在特定点之后,读取失败,错误为“坏地址”。出现故障的最低代码如下所示: #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> const unsigned BUFSIZ
read()
系统调用读取一个文件,并返回一个char
指针和从文件读取的数据。如果需要,该函数将重新分配空间。在特定点之后,读取失败,错误为“坏地址”。出现故障的最低代码如下所示:
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
const unsigned BUFSIZE = 8192;
typedef struct
{
char* buffer;
long size;
} string_t;
string_t read_file(const char* path)
{
string_t error = { .buffer = NULL, .size = -1 };
int fd = open(path, O_RDONLY);
if (fd == -1) {
perror("open() failed in read_file");
return error;
}
string_t s;
s.buffer = malloc(BUFSIZE * sizeof(char));
s.size = 0;
int nread = 0;
long total_read = 0;
while ((nread = read(fd, s.buffer + total_read, BUFSIZE)) != 0) {
if (nread == -1) {
if (errno == EINTR) {
perror("error EINTR");
continue;
} else {
perror("read() failed in read_file");
close(fd);
return error;
}
} else {
printf("%ld %ld %d\n", total_read, s.size, nread);
total_read += nread;
s.size = total_read;
if (nread == BUFSIZE) {
if (realloc(s.buffer, s.size + BUFSIZE) == NULL) {
perror("out of memory...");
close(fd);
return error;
}
}
}
}
close(fd);
s.buffer[s.size] = 0;
return s;
}
int main()
{
const char* path = "/usr/share/dict/cracklib-small";
string_t s = read_file(path);
if (s.size == -1) {
printf("error\n");
return 1;
}
printf("%s\n", s.buffer);
free(s.buffer);
return 0;
}
Valgrind显示:
==4299== Syscall param read(buf) points to unaddressable byte(s)
==4299== at 0x5184F00: __read_nocancel (in /usr/lib/libc-2.21.so)
==4299== by 0x400A58: read_file (file_helpers.c:31)
==4299== by 0x400AA3: main (file_helpers.c:64)
==4299== Address 0x7568040 is 0 bytes after a block of size 8,192 free'd
==4299== at 0x4C2C29E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4299== by 0x400A12: read_file (file_helpers.c:46)
==4299== by 0x400AA3: main (file_helpers.c:64)
==4299==
我可以看到它抱怨realloc,但我不明白是什么导致了地址错误。上次执行realloc()
后,read()
写入的缓冲区是否已损坏
wc-c
报告运行函数的文件有477238个字节。您对realloc()
的使用乍一看是错误的
if (realloc(s.buffer, s.size + BUFSIZE) == NULL)
此语句检查realloc()
是否成功。如果失败,则处理该情况。好的
如果realloc()
成功了怎么办
根据,
realloc()
函数返回一个指向新分配内存的指针,该指针适合任何类型的变量,并且可能不同于ptr
,如果请求失败,则返回NULL。如果size
等于0
,则返回NULL
或适合传递给free()
的指针。如果realloc()
失败,则原始块保持不变;它不会被释放或移动
这意味着,您将丢失新分配的内存,然后使用free()
d内存
我想,您可能已经预料到realloc()
将调整s.buffer
本身的大小,但是,我恐怕这里不是这样
解决方案:
您应该在临时变量中收集realloc()
的返回值,对照NULL
进行检查,如果不是NULL
,则将该返回值分配回指针s.buffer
FWIW,不要使用原始指针本身来收集
realloc()
的返回值,如果失败,您也将丢失实际内存。问题可能在于如何使用realloc
realloc(s.buffer,s.size+BUFSIZE)=NULL
不是使用realloc
的正确方法。取自的realloc
的返回值为:
在尺寸不等于0的情况下成功完成后,realloc()应
返回一个指向(可能移动的)分配空间的指针。如果大小是
0,可以是一个空指针,也可以是一个可以成功删除的唯一指针
将返回传递给free()的。如果没有足够的可用资源
内存,realloc()将返回一个空指针
将errno设置为ENOMEM
关键部分在于realloc
可以移动分配的空间及其数据。因此,不能只检查返回值是否为NULL。您想做更多的事情,包括:
void *tmp = realloc(s.buffer, s.size + BUFSIZE);
if (tmp == NULL) {
perror("out of memory...");
free(s.buffer); // Release the memory to avoid a leak
close(fd);
return error;
}
s.buffer = tmp;
换句话说,使用返回值realloc
更新指针。如果数据未移动,realloc
返回传递给它的内存地址;如果它被移动,realloc
将返回一个新地址
更新:
您可能没有遇到的另一个问题是如何处理read
的返回值。如果read
返回的内存少于请求的内存,则不会realloc
更多内存,进一步读取可能会读取超过缓冲区的内存。您现在可能没有遇到这种情况,因为只有当读取
没有失败并且读取的数据量不小于请求的数据量时,才会显示这种情况。包括realloc
fix的解决方案如下:
int nread = 0;
long total_read = 0;
int space_remaining = BUFSIZE;
while ((nread = read(fd, s.buffer + total_read, space_remaining)) != 0) {
if (nread == -1) {
if (errno == EINTR) {
perror("error EINTR");
continue;
} else {
perror("read() failed in read_file");
close(fd);
return error;
}
} else {
printf("%ld %ld %d\n", total_read, s.size, nread);
total_read += nread;
s.size = total_read;
space_remaining -= nread;
if (space_remaining == 0) {
void *tmp = realloc(s.buffer, s.size + BUFSIZE);
if (tmp == NULL) {
perror("out of memory...");
free(s.buffer); // Release the memory to avoid a leak
close(fd);
return error;
}
s.buffer = tmp;
space_remaining = BUFSIZE;
}
}
}
变量space\u remaining
用于跟踪缓冲区中剩余的空间。这是读取的量,当缓冲区大小增加时会重置。由于您正在realloc
-占用更多空间,因此您不希望执行先前建议的典型(BUFSIZE-total_read)
模式,尽管这是您看到的典型模式
如果
read
始终返回请求的数据量,您也不会看到此问题。谢谢。这个问题可以作为“如何写一个好问题”的例子。是否有充分的理由不使用它来确定文件大小、分配空间、,然后一次全部读取?我正在编写一个爬虫程序作为一个辅助项目,想看看在不知道响应大小的情况下从套接字读取是如何工作的,所以我想先尝试从文件读取。注意:1)表达式“sizeof(char)”总是返回1,并且对malloc()的调用没有影响。它只是把代码弄乱了。建议从对malloc()的任何调用中删除该表达式2)对read()的调用总是请求BUF_大小的字节,但在读取任何字节后,输入缓冲区中没有可用的BUF_大小的字节。建议使用:'while((nread=read(fd,s.buffer+total_read,BUFSIZE-total_read))!=0){一些注释,继续3)传递完整的结构不是一个好主意,原因有几个。建议在成功调用malloc()后传递/返回指向结构的指针。4)然后在出现错误时,需要将指向已分配内存的指针传递给free()。5)对realloc()的调用没有保存从realloc()返回的指针。结果是s.buffer仍指向旧的已分配内存,该内存在已分配内存复制到新的已分配内存后被释放。无法解决read()的问题,因为它能够输入超出已分配内存输入缓冲区末尾的字符。无法解决read()的问题能够在分配的内存输入缓冲区结束后输入字符。@user3629249作者总是重新分配以添加额外的空间,所以不需要这样做(BUFSIZE-total_read),我认为
int nread = 0;
long total_read = 0;
int space_remaining = BUFSIZE;
while ((nread = read(fd, s.buffer + total_read, space_remaining)) != 0) {
if (nread == -1) {
if (errno == EINTR) {
perror("error EINTR");
continue;
} else {
perror("read() failed in read_file");
close(fd);
return error;
}
} else {
printf("%ld %ld %d\n", total_read, s.size, nread);
total_read += nread;
s.size = total_read;
space_remaining -= nread;
if (space_remaining == 0) {
void *tmp = realloc(s.buffer, s.size + BUFSIZE);
if (tmp == NULL) {
perror("out of memory...");
free(s.buffer); // Release the memory to avoid a leak
close(fd);
return error;
}
s.buffer = tmp;
space_remaining = BUFSIZE;
}
}
}