fmemopen 给出 Valgrind 错误

fmemopen gives a Valgrind error

我有以下代码:

char *filedata;
FILE *f;
filedata = malloc(3);
if (filedata == NULL)
{
    fprintf(stderr, "out of memory\n");
    exit(1);
}
memcpy(filedata, "foo", 3);
f = fmemopen(filedata, 3, "r");
if (f == NULL)
{
    fprintf(stderr, "out of memory\n");
    exit(1);
}
fclose(f);
free(filedata);

现在,当我使用 Valgrind 执行此操作时,出现以下错误:

==32454== Invalid read of size 1
==32454==    at 0x4006D33: __GI_strlen (mc_replace_strmem.c:284)
==32454==    by 0x855B7E: fmemopen (fmemopen.c:246)
==32454==    by 0x80485BF: main (in /home/tilli/memopen/a.out)
==32454==  Address 0x402502b is 0 bytes after a block of size 3 alloc'd
==32454==    at 0x4005BDC: malloc (vg_replace_malloc.c:195)
==32454==    by 0x8048548: main (in /home/tilli/memopen/a.out)

似乎 fmemopen 正在为我传递给它的参数做 strlen()。但是,手册页说 fmemopen 的缓冲区参数可以是字符串或内存缓冲区(因此它不必以 '[=15=]' 结尾)。此外,它说参数必须至少 size 字节长,确实如此。

这里有什么问题?我刚刚在库函数 fmemopen 中发现了一个错误吗?如果 strlen 没有找到 '[=15=]' 终止符而是继续读取未映射的内存,我是否纠正了在极端情况下此错误可能会使使用 fmemopen 的程序崩溃?

我是 运行 Fedora 12 版。

从手册来看,[=10=] 似乎用作流缓冲区的 EOF 标记,除非模式包含 b。这解释了为什么使用 strlen() 来定位 EOF。

来自 man fmemopen:

The argument mode is the same as for fopen(3). If mode specifies an append mode, then the initial file position is set to the location of the first null byte ('[=12=]') in the buffer; otherwise the initial file position is set to the start of the buffer. Since glibc 2.9, the letter 'b' may be specified as the second character in mode. This provides "binary" mode: writes don't implicitly add a terminating null byte, and fseek(3) SEEK_END is rela‐ tive to the end of the buffer (i.e., the value specified by the size argument), rather than the current string length.

然而,在同一个人身上我们可以读到:

In a stream opened for reading, null bytes ('[=13=]') in the buffer do not cause read operations to return an end- of-file indication. A read from the buffer will only indicate end-of-file when the file pointer advances size bytes past the start of the buffer.

所以您可能发现了一个错误。

在Ubuntu16.04上使用gcc 5.3.1和valgrind 3.11.0,valgrind不报错。如此看来,该错误已同时修复。