From tim@X2296  Tue Apr 29 18:43:00 1997
Received: from X2296 (ppp1552.on.sympatico.ca [206.172.249.16])
          by hub.freebsd.org (8.8.5/8.8.5) with ESMTP id SAA23864
          for <FreeBSD-gnats-submit@freebsd.org>; Tue, 29 Apr 1997 18:42:52 -0700 (PDT)
Received: (from tim@localhost) by X2296 (8.7.6/8.7.3) id VAA03236; Tue, 29 Apr 1997 21:41:51 -0400 (EDT)
Message-Id: <199704300141.VAA03236@X2296>
Date: Tue, 29 Apr 1997 21:41:51 -0400 (EDT)
From: Tim.Vanderhoek@X2296
Reply-To: ac199@hwcn.org
To: FreeBSD-gnats-submit@freebsd.org
Subject: vasprintf() is stupid
X-Send-Pr-Version: 3.2

>Number:         3451
>Category:       bin
>Synopsis:       vasprintf() doesn't work.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    peter
>State:          closed
>Quarter:
>Keywords:
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu May  1 00:54:25 PDT 1997
>Closed-Date:    Thu Aug 14 06:51:35 PDT 1997
>Last-Modified:  Thu Aug 14 06:52:04 PDT 1997
>Originator:     Tim Vanderhoek
>Release:        FreeBSD 2.2-961006-SNAP i386
>Organization:
League of the Missing
>Environment:

vasprintf.c grabbed from -current.

>Description:

It's a simple off-by-one error...  This occurs in vasprintf.c in the
case where the size of the string is such that it occurs.  Specifically,
it must be such that after the vfprintf() call, h.left == 0.

Recall from line #69 of vasprintf.c that h.base is allocatted the
following space:  newbuf = realloc(h->base, h->size);

However, on line #116 of vasprintf.c, where h.left = 0, we
do:  h.base[h.size - 0] = '\0';

This is not good, because it can do bad things such as causing learning
curves in inexperienced hackers such as me.  OTOH, when one doesn't have
a timeschedule, chasing after these things can be pretty fun!  :)

>How-To-Repeat:

Ha!  You think I've got the time to spend trying to reinvent the
specific conditions that show this bug!?  :)  If you really must,
grab 80k gzipped sources from my computer and watch as static strings
local to distant functions suddenly disappear for no reason.

>Fix:

*** old.vasprintf.c	Tue Apr 29 21:32:00 1997
--- vasprintf.c	Tue Apr 29 21:31:45 1997
***************
*** 111,118 ****
  	if (h.base == NULL)	/* failed to realloc in writehook */
  		return (-1);
  
- 	h.base[h.size - h.left] = '\0';
  	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
  	if (*str == NULL)	/* failed to realloc it to actual size */
  		*str = h.base;	/* return oversize buffer */
  	return (ret);
--- 111,118 ----
  	if (h.base == NULL)	/* failed to realloc in writehook */
  		return (-1);
  
  	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
+ 	(*str)[h.size - h.left] = '\0';
  	if (*str == NULL)	/* failed to realloc it to actual size */
  		*str = h.base;	/* return oversize buffer */
  	return (ret);
>Release-Note:
>Audit-Trail:

From: ac199@hwcn.org
To: freebsd-gnats-submit@freebsd.org, Tim.Vanderhoek@X2296
Cc: peter@freebsd.org
Subject: Re: bin/3451: vasprintf() doesn't work.
Date: Fri, 13 Jun 1997 18:05:29 -0400 (EDT)

 [Cc'd to peter as the last to touch vasprintf.c]
 
 
 >                            Problem Report bin/3451
 >                                       
 >   vasprintf() doesn't work.
 >   
 >   Fix
 >          
 >
 >*** old.vasprintf.c     Tue Apr 29 21:32:00 1997
 >--- vasprintf.c Tue Apr 29 21:31:45 1997
 
 Of course, this still isn't right...
 
 [post-patch vasprintf.c]
 >--- 111,118 ----
 >        if (h.base == NULL)     /* failed to realloc in writehook */
 >                return (-1);
 >
 >        *str = realloc(h.base, (size_t)(h.size - h.left + 1));
 >+       (*str)[h.size - h.left] = '\0';
 >        if (*str == NULL)       /* failed to realloc it to actual size */
 >                *str = h.base;  /* return oversize buffer */
 >        return (ret);
 
 realloc() can fail, resulting in a null pointer dereference.  That's
 undesirable.
 
 Try, instead,
 
 *** orig.vasprintf.c	Tue Apr 29 21:32:00 1997
 --- vasprintf.c	Fri Jun 13 17:54:17 1997
 ***************
 *** 111,119 ****
   	if (h.base == NULL)	/* failed to realloc in writehook */
   		return (-1);
   
 - 	h.base[h.size - h.left] = '\0';
   	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
   	if (*str == NULL)	/* failed to realloc it to actual size */
 ! 		*str = h.base;	/* return oversize buffer */
   	return (ret);
   }
 --- 111,119 ----
   	if (h.base == NULL)	/* failed to realloc in writehook */
   		return (-1);
   
   	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
   	if (*str == NULL)	/* failed to realloc it to actual size */
 ! 		return (-1);
 ! 	(*str)[h.size - h.left] = '\0';
   	return (ret);
   }
 

From: Tim Vanderhoek <tim@X2296>
To: Bruce Evans <bde@zeta.org.au>
Cc: ac199@hwcn.org, freebsd-bugs@hub.freebsd.org,
        freebsd-gnats-submit@freebsd.org
Subject: Re: bin/3451: vasprintf() doesn't work.
Date: Sat, 14 Jun 1997 09:07:46 -0400 (EDT)

 On Sat, 14 Jun 1997, Bruce Evans wrote:
 
 > >   	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
 > >   	if (*str == NULL)	/* failed to realloc it to actual size */
 > > ! 		return (-1);
 > > ! 	(*str)[h.size - h.left] = '\0';
 > >   	return (ret);
 > >   }
 > 
 > One more problem: realloc() can fail, resulting in a leaking the memory
 > pointed to by h.base.
 
 Which, of course, points us to yet another error in the original
 vasprintf.c...  :)  The test to see if str is NULL is
 *str == NULL....
 
 Final patch frees h.base (as is done everywhere else) and fixes 
 that... :)
 
 
 *** old.vasprintf.c	Tue Apr 29 21:32:00 1997
 --- vasprintf.c	Sat Jun 14 08:56:41 1997
 ***************
 *** 111,119 ****
   	if (h.base == NULL)	/* failed to realloc in writehook */
   		return (-1);
   
 - 	h.base[h.size - h.left] = '\0';
   	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
 ! 	if (*str == NULL)	/* failed to realloc it to actual size */
 ! 		*str = h.base;	/* return oversize buffer */
   	return (ret);
   }
 --- 111,121 ----
   	if (h.base == NULL)	/* failed to realloc in writehook */
   		return (-1);
   
   	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
 ! 	if (str == NULL) {	/* failed to realloc it to actual size */
 ! 		free(h.base);
 ! 		return (-1);
 ! 	}
 ! 	(*str)[h.size - h.left] = '\0';
   	return (ret);
   }
 
 
 --
 tIM...HOEk
 optimization: The theory that making your code incomprehensible by using
               only one-letter variable names will make it run faster.
 

From: Tim Vanderhoek <tim@X2296>
To: hoek@hwcn.org
Cc: Bruce Evans <bde@zeta.org.au>, ac199@hwcn.org,
        freebsd-bugs@hub.freebsd.org, freebsd-gnats-submit@freebsd.org
Subject: Re: bin/3451: vasprintf() doesn't work.
Date: Sat, 14 Jun 1997 09:14:29 -0400 (EDT)

 On Sat, 14 Jun 1997, Tim Vanderhoek wrote:
 
 > On Sat, 14 Jun 1997, Bruce Evans wrote:
 > 
 > > >   	*str = realloc(h.base, (size_t)(h.size - h.left + 1));
 > > >   	if (*str == NULL)	/* failed to realloc it to actual size */
 > > > ! 		return (-1);
 > > > ! 	(*str)[h.size - h.left] = '\0';
 > > >   	return (ret);
 > 
 > Which, of course, points us to yet another error in the original
 > vasprintf.c...  :)  The test to see if str is NULL is
 > *str == NULL....
 
 No, wait...  That should be *str == NULL...  Just ignore that
 patch, and just put the free(h.base) in there...
 
 Sheesh...  I should really look this stuff over before I send
 it...  :-(
 
 > ! 	if (str == NULL) {	/* failed to realloc it to actual size */
   !	if (*str == NULL) {	/* failed to realloc it to actual size */
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 corrected.  again.
 
 --
 tIM...HOEk
 optimization: The theory that making your code incomprehensible by using
               only one-letter variable names will make it run faster.
 

From: ac199@hwcn.org
To: freebsd-gnats-submit@freebsd.org, tim@X2296
Cc:  Subject: Re: bin/3451 : vasprintf() doesn't work.
Date: Sun, 6 Jul 1997 02:26:47 -0400 (EDT)

 >
 >   How-To-Repeat
 
 As a demonstration of my dedication and intelligence :), this pr now
 includes, in addition to the ~5 line good fix and detailed explanation of
 it, demonstration code!
 
 Read the Caveat, though...  Tested on 2.2.2 (the bug exists in -current,
 as of July 5, but I've not tested this code there).
 
 
 /*
  * CAVEAT:  This code depends on a specific behaviour of malloc(3).
  * If, at some point in the future, it stops demonstrating the
  * vasprintf(3) bug, it could be because the behaviour of malloc()
  * has been changed subtly, instead of being because vasprintf()
  * has been fixed.
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
 char d[] = "In an effort to keep this demonstration as simple and
 to the point as possible, I use this string instead of mallocing and
 memsetting.  This string will be cut to exactly 128 characters.";
 
 main() {
  	static char * a, * b, * c;  /* static makes debugging w/ 
  	                             * watches easier */
 
  	d[128] = '\0';  /* cut d to proper size */
 
  	a = malloc (128);  /* 128 == CHUNK_SPARE in vasprintf.c */
  	b = malloc (70);  /* This is very tricky.  If we alloc too little
  	                   * (or too much) space here, malloc() will send
  	                   * the next malloc(128) off into the boonies,
  	                   * even though we free(a) 128. */
  	free (a);  /* asprintf will malloc(128) and get what I just
  	            * free()'d.  The memory at b should be contigious. */
 
  	strcpy (b, "string");
 
  	asprintf (&c, d);
 
  	printf ("b %s equals \"string\".  It is \"%s\".\n",
  	    strcmp(b,"string") ? "no longer" : "still", b);
 
  	/* At no point have we touched b since strcpy().  It should still
  	 * say "string", but it won't... */
 }
State-Changed-From-To: open->feedback 
State-Changed-By: peter 
State-Changed-When: Sun Jul 6 01:43:03 PDT 1997 
State-Changed-Why:  
Hopefully fixed in revs 1.6 and 1.7 of vasprintf.c 


Responsible-Changed-From-To: freebsd-bugs->peter-bugs 
Responsible-Changed-By: peter 
Responsible-Changed-When: Sun Jul 6 01:43:03 PDT 1997 
Responsible-Changed-Why:  
my bug.. 
Responsible-Changed-From-To: peter-bugs->peter 
Responsible-Changed-By: peter 
Responsible-Changed-When: Sun Jul 6 01:47:32 PDT 1997 
Responsible-Changed-Why:  
oops, spell my name right.. :-) 

From: hoek@hwcn.org
To: freebsd-gnats-submit@freebsd.org, ac199@hwcn.org
Cc:  Subject: Re: bin/3451 : vasprintf() doesn't work.
Date: Tue, 15 Jul 1997 00:18:58 -0400 (EDT)

 >State-Changed-From-To: open->feedback
 >State-Changed-By: peter
 >State-Changed-When: Sun Jul 6 01:43:03 PDT 1997
 >State-Changed-Why:
 >Hopefully fixed in revs 1.6 and 1.7 of vasprintf.c
 
 Yes, it sure looks fixed.  I don't believe there's any reason to leave
 this in `feedback', now.  It can be closed.  :)
 
 Out of curiousity, though.  Under what conditions can realloc() fail to
 shorten a buffer?  (Perhaps with a user-defined *alloc()?)
 
State-Changed-From-To: feedback->closed 
State-Changed-By: peter 
State-Changed-When: Thu Aug 14 06:51:35 PDT 1997 
State-Changed-Why:  
Fixed. 
>Unformatted:
