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:
_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)
Note that before is always either NULL or points to some location within
the existing *argz buffer.
while (before != *argz before[-1])
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)))
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.
I'll whip up a patch and post it to the newlib list.