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