mdemo ltdl failure

Charles Wilson cygwin@cwilson.fastmail.fm
Fri Mar 16 19:52:00 GMT 2007


Well, once I got the cygwin1.dbg stuff worked out, it was pretty easy to 
track down: it is a bug in newlib's argz_insert:

Charles Wilson wrote:
> Here's the code from newlib's argz_insert:
> 
> error_t
> _DEFUN (argz_insert, (argz, argz_len, before, entry),
>        char **argz _AND
>        size_t *argz_len _AND
>        char *before _AND
>        const char *entry)
> {
>   int len = 0;
> 
>   if (before == NULL)
>     return argz_add(argz, argz_len, entry);
> 
>   if (before < *argz || before >= *argz + *argz_len)
>     return EINVAL;
> 

Note that before is always either NULL or points to some location within 
the existing *argz buffer.

>   while (before != *argz && before[-1])
>     before--;

Because *argz contains NULL-delimited strings one after the other, if 
the user calls this function with a before that points into the middle 
of one of those strings, the preceeding two lines just back up to the 
beginning of that string (or to the beginning of the current argz, 
whichever comes first).

>   len = strlen(entry) + 1;

In the failing call, we actually do a realloc...

>   if(!(*argz = (char *)realloc(*argz, *argz_len + len)))
>     return ENOMEM;

But if we realloc the *argz buffer, then a non-NULL 'before' pointer 
will be pointing into the old, freed, *argz buffer.  So the following is 
clearly wrong, because we are copying stuff _from_ the new *argz to a 
modified location in the old (shorter) *argz -- which will overrun the 
end of the old buffer by exactly strlen(entry), and clobber stuff. 
Depending on the actual allocated locations of malloced data, this could 
include (1) like some of the the memory held by entry, or (2) some of 
the memory held by the new *argz buffer.  In this case, it is (1).

>   memmove(before + len, before, *argz + *argz_len - before);

Then, we copy this clobbered entry data into the front of (the freed, 
old *argz buffer)

>   memcpy(before, entry, len);
> 
>   *argz_len += len;

meanwhile, the actual (newly realloc'ed) *argz buffer just contains 
whatever was in *argz prior to the call to this function.  Worse, with 
upward malloc movement, the too-large memove above might also have 
clobbered the first several bytes of the new *argz buffer, as well.

And that's what's happening in this case.

The eventual FREE(buf) error is because the first few bytes in the 
malloc-managed memory for buf (e.g. just below *buf) which contain 
malloc bookkeeping info, are also clobbered.

>   return 0;
> }

I'll whip up a patch and post it to the newlib list.

--
Chuck

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/



More information about the Cygwin mailing list