juhist
juhist

Reputation: 4314

fmemopen gives a Valgrind error

I have the following code:

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);

Now, when I execute this with Valgrind, I get the following error:

==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)

It seems like fmemopen is doing an strlen() for the argument I passed to it. However, the manual page says that the buffer argument to fmemopen can be a string or a memory buffer (so it doesn't have to be '\0'-terminated). Furthermore, it says that the argument must be at least size bytes long, which it is.

What is wrong here? Have I just a found a bug in the library function fmemopen? Am I correct that in extreme cases this bug could crash a program using fmemopen if the strlen doesn't find a '\0' terminator but instead continues to read unmapped memory?

I'm running Fedora release 12.

Upvotes: 1

Views: 440

Answers (2)

ralfg
ralfg

Reputation: 552

Using gcc 5.3.1 and valgrind 3.11.0 on Ubuntu 16.04, valgrind does not report any error. So it seems, that the bug has been fixed in the meantime.

Upvotes: 2

chmike
chmike

Reputation: 22174

From the manual it looks like the \0 is used as EOF marker for stream buffers unless the mode includes a b. This explains why a strlen() would be used to locate the EOF.

From 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 ('\0') 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.

However, in the same man we can read :

In a stream opened for reading, null bytes ('\0') 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.

So you might have found a bug.

Upvotes: 2

Related Questions