Subject: Re: Possible bug relating to malloc()/realloc(), popen(), and read()
To: Vincent Stemen <netbsd@crel.us>
From: Sami Kantoluoto <sami.kantoluoto@embedtronics.fi>
List: port-i386
Date: 12/02/2004 16:08:26
Hi,
On Wed, Dec 01, 2004 at 07:05:40PM -0600, Vincent Stemen wrote:
> I have encountered what appears to be a bug when reading the output of
> a command via a pipe from popen() into memory allocated by malloc()
> and realloc(). Perhaps I am overlooking something, but the same piece
> of code behaves correctly when I test it on FreeBSD. I am wondering
> if I should send a problem report. I searched the NetBSD mailing
> lists, google, and the NetBSD GNATS Database and found no reference to
> this problem.
[snipped]
> I am allocating memory as needed to store the output of a command. If
> I allocate and try to read any more than 1024 bytes at a time, it will
> only read 1024 bytes on the first read, then when I call realloc() and
> do a second read, read() returns a count that indicates it read the
> full amount specified, but the pointer I get back from realloc() does
> not point to the beginning of the data allocated by the first malloc()
> as it should. Instead, it appears to point to the beginning plus
> 1024. All subsequent calls to realloc() appear to return a pointer to
> the same place.
>
> It works correctly, even with larger chunks, if I read directly from
> the file rather than from the output of the cat command using popen().
> It also works correctly using popen() as long as I only read 1024
> bytes or less.
I don't know why first read() returns only 1024 bytes but I think the code
works just like it should if read() returns less than 1028 bytes. See below.
[snipped]
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <fcntl.h>
> #include <err.h>
>
> int
> main(int argc, char *argv[])
> {
> FILE *cmd;
> int cmd_fd;
> char *output;
> char *bufp;
> ssize_t count;
> size_t bytes_read = 0;
> size_t block_size = 1028;
>
> output = NULL;
> if ((cmd = popen(argv[1], "r")) == NULL) return(-1);
> cmd_fd = fileno(cmd);
>
> if ((output = malloc(block_size)) == NULL)
> {
> warn("Could not allocate memory");
> return(-1);
> }
>
> bufp = output;
> bufp[0] = 0;
>
> while ((count = read(cmd_fd, bufp, block_size)) > 0)
> {
> bytes_read += count;
> printf("*** Read %i bytes ***\n", count); // debug xxxx
Following 'if' is false when less than 1028 bytes are read so next read()
overwrites previously read data, right?
> if (count == block_size)
> {
> if ((bufp = realloc(output, bytes_read + block_size)) == NULL)
> {
> warn("Could not allocate memory");
> output[bytes_read] = 0;
> return(-1);
> }
> output = bufp;
> bufp += bytes_read;
> }
>
> printf("---------------------------------------------\n"); // debug xxxx
> printf("%s\n\n", output); // debug xxxx
> }
>
> if (count == -1)
> {
> warn("Error reading file");
> output[bytes_read] = 0;
> return( -1);
> }
>
> printf("===================\n"); // debug xxxx
> printf("Total bytes read = %d\n", bytes_read); // debug xxxx
>
> output[bytes_read] = 0;
> pclose(cmd);
> return(0);
> }
-sk