X-Spam-Check-By: sourceware.org Message-ID: <45FAF541.4050308@cwilson.fastmail.fm> Date: Fri, 16 Mar 2007 14:51:29 -0500 From: Charles Wilson User-Agent: Thunderbird 1.5.0.10 (Windows/20070221) MIME-Version: 1.0 To: cygwin AT cygwin DOT com, Libtool Patches Subject: Re: mdemo ltdl failure References: <20070225191816 DOT GB2990 AT iam DOT uni-bonn DOT de> <45E3B6CA DOT 50600 AT cwilson DOT fastmail DOT fm> <20070227220200 DOT GC2982 AT iam DOT uni-bonn DOT de> <20070307222815 DOT 5072 DOT qmail AT iam DOT uni-bonn DOT de> <45FAB925 DOT 9090200 AT cwilson DOT fastmail DOT fm> <45FABF35 DOT 9080002 AT cwilson DOT no DOT fastmail DOT spam DOT com> In-Reply-To: <45FABF35.9080002@cwilson.no.fastmail.spam.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com 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/