svn commit: r197752 - head/lib/libc/stdio

David Schultz das at freebsd.org
Tue Jan 12 00:40:09 UTC 2010


On Mon, Jan 11, 2010, Max Laier wrote:
> On Sunday 04 October 2009 21:43:36 David Schultz wrote:
> > Author: das
> > Date: Sun Oct  4 19:43:36 2009
> > New Revision: 197752
> > URL: http://svn.freebsd.org/changeset/base/197752
> > 
> > Log:
> >   Better glibc compatibility for getline/getdelim:
> > 
> >   - Tolerate applications that pass a NULL pointer for the buffer and
> >     claim that the capacity of the buffer is nonzero.
> > 
> >   - If an application passes in a non-NULL buffer pointer and claims the
> >     buffer has zero capacity, we should free (well, realloc) it
> >     anyway. It could have been obtained from malloc(0), so failing to
> >     free it would be a small memory leak.
> > 
> >   MFC After:	2 weeks
> >   Reported by:	naddy
> >   PR:		ports/138320
> > 
> > Modified:
> >   head/lib/libc/stdio/getdelim.c
> > 
> > Modified: head/lib/libc/stdio/getdelim.c
> > ===========================================================================
> > === --- head/lib/libc/stdio/getdelim.c	Sun Oct  4 19:03:32 2009	(r197751)
> >  +++ head/lib/libc/stdio/getdelim.c	Sun Oct  4 19:43:36 2009	(r197752) @@
> >  -120,8 +120,8 @@ getdelim(char ** __restrict linep, size_
> >  		goto error;
> >  	}
> > 
> > -	if (*linecapp == 0)
> > -		*linep = NULL;
> > +	if (*linep == NULL)
> > +		*linecapp = 0;
> > 
> >  	if (fp->_r <= 0 && __srefill(fp)) {
> >  		/* If fp is at EOF already, we just need space for the NUL. */
> 
> I think we should have kept the original if case here, as well.  Otherwise 
> something like this might fail:
> 
> 	char *line;				/* note uninitialized */
> 	size_t len = 0;
> 	getline(&line, &len, fd);
> 
> and I think it is a reasonable thing to pass in an uninitialized pointer if 
> you tell that there is no space associated with it, yet.
> 
> I don't know if there are many (ab)uses of getline like this, but I was just 
> bitten by it.

The trouble is that in many malloc() implementations, including
ours, malloc(0) actually makes a small allocation instead of
returning NULL.  Hence, with your proposed change, code such as
the following would result in a memory leak:

       size_t len = 0;
       char *line = malloc(len);
       getline(&line, &len, fd);

POSIX says that *linep has to be a valid argument to the free(),
but it's allowed to point to a buffer that's larger than
advertised (e.g., a 128-byte buffer when *linecapp is 0.)  It's a
bad API; what's missing is a "line buffer" ADT that hides the
details of its implementation.  But it's still a bit better than
fgetln() and way better than something hand-rolled.


More information about the svn-src-all mailing list