asprintf bugs

Jeff Johnston jjohnstn@redhat.com
Thu Nov 30 09:36:00 GMT 2006


Thanks for the analysis Eric.  Patch checked in.

Regarding minimizing the asprintf default buffer size, yes, it would be 
worth pursuing.  I made up a trial patch.  It simply checks for __SMBF 
inside __smakebuf.  If already set, this implies that the asprintf 
family is using the routine for the first time.  Try it out and let me 
know what you think.

-- Jeff J.

Eric Blake wrote:
> asprintf has two bugs.  The first is a performance bug - when collecting a
> string more than BUFSIZ bytes, realloc is potentially called for every byte
> added to the string.  If every call to realloc results in a relocation of the
> existing memory contents, you have O(n^2) behavior.  On the other hand, if
> realloc were called with a 1.5 growth factor, rather than to the currently
> needed size, you have reduced the complexity to O(n log n).
> 
> The second is a fatal off-by-one bug.  For a demonstration of the bug, in
> cygwin, this command causes bash to coredump, due to its use of asprintf to
> implement the bash printf builtin.  Basically, asprintf is writing beyond the
> array bounds of any printed string greater than BUFSIZ bytes, and when that
> write happens to be beyond a malloc buffer boundary, it corrupts data such that
> free'ing the allocated string goes haywire:
> $ bash
> $ printf '%s\n' `perl -e 'printf "a"x1028'` > /dev/null
> Aborted (core dumped)
> $ 
> 
> One other thing to consider, but that this patch does not address: currently,
> asprintf ALWAYS allocates a buffer of at least BUFSIZ bytes (1024 on cygwin),
> even for something like asprintf(&s,"") that only uses 1 byte, as part of the
> cantwrite() check at the start of vfprintf.  Is it worth trying to tweak things
> so that it initially allocates a smaller size, such as 64 bytes, to decrease
> wasted memory overhead on small strings, not to mention the fact that some
> malloc implementations have better strategies for small strings than for large
> strings, in an effort to improve performance?
> 
> 2006-11-29  Eric Blake  <ebb9@byu.net>
> 
> 	* libc/stdio/fvwrite.c (__sfvwrite_r): Avoid off-by-one error in
> 	asprintf, as well as quadratic realloc behavior.
> 
> Index: libc/stdio/fvwrite.c
> ===================================================================
> RCS file: /cvs/src/src/newlib/libc/stdio/fvwrite.c,v
> retrieving revision 1.8
> diff -u -u -p -r1.8 fvwrite.c
> --- libc/stdio/fvwrite.c	14 Jun 2006 20:49:11 -0000	1.8
> +++ libc/stdio/fvwrite.c	29 Nov 2006 17:54:15 -0000
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 1990 The Regents of the University of California.
> + * Copyright (c) 1990, 2006 The Regents of the University of California.
>   * All rights reserved.
>   *
>   * Redistribution and use in source and binary forms are permitted
> @@ -127,13 +127,23 @@ _DEFUN(__sfvwrite_r, (ptr, fp, uio),
>  	  w = fp->_w;
>  	  if (fp->_flags & __SSTR)
>  	    {
> -	      if (len > w && fp->_flags & __SMBF)
> +	      if (len >= w && fp->_flags & __SMBF)
>  		{ /* must be asprintf family */
>  		  unsigned char *ptr;
>  		  int curpos = (fp->_p - fp->_bf._base);
> +		  /* Choose a geometric growth factor to avoid
> +		     quadratic realloc behavior, but use a rate less
> +		     than (1+sqrt(5))/2 to accomodate malloc
> +		     overhead. asprintf EXPECTS us to overallocate, so
> +		     that it can add a trailing \0 without
> +		     reallocating.  The new allocation should thus be
> +		     max(prev_size*1.5, curpos+len+1). */
> +		  int newsize = fp->_bf._size * 3 / 2;
> +		  if (newsize < curpos + len + 1)
> +		    newsize = curpos + len + 1;
>  		  ptr = (unsigned char *)_realloc_r (_REENT, 
>                                                       fp->_bf._base, 
> -                                                     curpos + len);
> +                                                     newsize);
>  		  if (!ptr)
>  		    {
>  		      /* Free buffer which is no longer used.  */
> @@ -142,8 +152,9 @@ _DEFUN(__sfvwrite_r, (ptr, fp, uio),
>  		    }
>  		  fp->_bf._base = ptr;
>  		  fp->_p = ptr + curpos;
> -		  fp->_bf._size = curpos + len;
> -		  w = fp->_w = len;
> +		  fp->_bf._size = newsize;
> +		  w = len;
> +		  fp->_w = newsize - curpos;
>  		}
>  	      if (len < w)
>  		w = len;
> 
> 
> 

-------------- next part --------------
A non-text attachment was scrubbed...
Name: makebuf.patch
Type: text/x-patch
Size: 886 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/newlib/attachments/20061130/bb6bca6b/attachment.bin>


More information about the Newlib mailing list