The problem described here occurs both in 4.2.3RC2 & 4.3.0-dev. The attached patch is against 4.3.0-dev (HEAD) and as far as my testing shows addresses the problem.
The source of the segfault is a buffer overflow that results when estimated number of times the separator will be inserted is less the then actual number. There is no 'true' way of checking the number of separators to be inserted, since this number will often depend on the data contained in the string itself. So, the solution is to add a check that will reallocate more memory if the initial estimate on the string size proves to be incorrect. Unless, there are better solutions as alternatives to the patch or any strong objections I'll commit this patch to both 4.2.3RC2 & 4.3.0-dev in the morning. Ilia P.S. The segv can be replicated by the attached php script (segfault-4.2.3RC2.txt) submitted by Martin Jansen.
Index: string.c =================================================================== RCS file: /repository/php4/ext/standard/string.c,v retrieving revision 1.286 diff -u -3 -p -r1.286 string.c --- string.c 25 Aug 2002 19:08:07 -0000 1.286 +++ string.c 5 Sep 2002 01:47:59 -0000 @@ -595,7 +595,7 @@ PHP_FUNCTION(wordwrap) const char *text, *breakchar = "\n"; char *newtext; int textlen, breakcharlen = 1, newtextlen; - long current = 0, laststart = 0, lastspace = 0; + long current = 0, laststart = 0, lastspace = 0, mem_alloced = 0; long linelength = 75; zend_bool docut = 0; @@ -646,6 +646,7 @@ PHP_FUNCTION(wordwrap) newtextlen = textlen * (breakcharlen + 1) + 1; } newtext = emalloc(newtextlen); + mem_alloced = newtextlen; /* now keep track of the actual new text length */ newtextlen = 0; @@ -657,6 +658,12 @@ PHP_FUNCTION(wordwrap) if (text[current] == breakchar[0] && current + breakcharlen < textlen && !strncmp(text+current, breakchar, breakcharlen)) { + + if ((newtextlen+current-laststart+breakcharlen) >= mem_alloced) { + mem_alloced += textlen-current+((textlen-current)/linelength + 1) * breakcharlen + 1; + newtext = erealloc(newtext, mem_alloced); + } + memcpy(newtext+newtextlen, text+laststart, current-laststart+breakcharlen); newtextlen += current-laststart+breakcharlen; current += breakcharlen - 1; @@ -666,6 +673,11 @@ PHP_FUNCTION(wordwrap) * copy and insert a break, or just keep track of it */ else if (text[current] == ' ') { if (current - laststart >= linelength) { + if ((newtextlen+current-laststart+breakcharlen) >= mem_alloced) { + mem_alloced += textlen-current+((textlen-current)/linelength + 1) * breakcharlen + 1; + newtext = erealloc(newtext, mem_alloced); + } + memcpy(newtext+newtextlen, text+laststart, current-laststart); newtextlen += current - laststart; memcpy(newtext+newtextlen, breakchar, breakcharlen); @@ -679,6 +691,11 @@ PHP_FUNCTION(wordwrap) * copy and insert a break. */ else if (current - laststart >= linelength && docut && laststart >= lastspace) { + if ((newtextlen+current-laststart+breakcharlen) >= mem_alloced) { + mem_alloced += textlen-current+((textlen-current)/linelength + 1) * breakcharlen + 1; + newtext = erealloc(newtext, mem_alloced); + } + memcpy(newtext+newtextlen, text+laststart, current-laststart); newtextlen += current - laststart; memcpy(newtext+newtextlen, breakchar, breakcharlen); @@ -690,6 +707,10 @@ PHP_FUNCTION(wordwrap) * up the laststart */ else if (current - laststart >= linelength && laststart < lastspace) { + if ((newtextlen+lastspace-laststart+breakcharlen) >= mem_alloced) { + mem_alloced += textlen-current+((textlen-current)/linelength + 1) * breakcharlen + 1; + newtext = erealloc(newtext, mem_alloced); + } memcpy(newtext+newtextlen, text+laststart, lastspace-laststart); newtextlen += lastspace - laststart; memcpy(newtext+newtextlen, breakchar, breakcharlen); @@ -700,8 +721,15 @@ PHP_FUNCTION(wordwrap) /* copy over any stragglers */ if (laststart != current) { + if ((newtextlen+current - laststart) >= mem_alloced) { + mem_alloced += current-laststart+1; + newtext = erealloc(newtext, mem_alloced); + } + memcpy(newtext+newtextlen, text+laststart, current-laststart); newtextlen += current - laststart; + } else { + newtext = erealloc(newtext, newtextlen+1); } newtext[newtextlen] = '\0';
<?php $var = 'Unser Waldhof liegt inmitten der ursprünglichen Landschaft des Dreiländerecks Deutschland – Luxemburg –Belgien. Umgeben von Wäldern, Wiesen und Feldern sind unsere Ferienwohnungen ein idealer Ausgangspunkt für ausgedehnte Streifzüge durch eine intakte Natur. Wanderer und Naturfreunde können bei uns die wohltuende Ruhe einer ländlichen Region genießen. Kinder fühlen sich auf unserem Bauerhof besonders wohl, denn hier können sie den artgerechten Umgang mit unseren Kühen und Kälbern, den Hühnern und Katzen, unseren beiden Ponys Max & Moritz sowie unserem Hofhund Sanny hautnah kennen lernen. Aber auch die angebotenen Traktorfahrten erfreuen sich bei unseren kleinen Gästen größter Beliebtheit. Unsere beiden Wohnungen sind modern und ansprechend eingerichtet und verfügen über eine voll ausgestattete Einbauküche, Telefon, Radio sowie einen Fernseher mit Sat-Anschluß. Der "Kornspeicher" bietet auf 80qm Platz für bis zu 8 Personen. Die Ferienwohnung besteht aus 2 Schlafzimmern, Küche, Wohnraum mit Schlafcouch, Essecke sowie einem Bad mit Dusche und WC. Der "Heuboden" bietet auf 50 qm Platz für 4 Personen. Die Ferienwohnung besteht aus einem Schlafzimmer, Wohnküche mit Schrankbett sowie einem Bad mit Dusche und WC. Eine Terrasse mit bequemen Liegestühlen sowie ein Hobbyraum mit Spielen, Büchern und einer Tischtennisplatte sowie einem Fußballkicker stehen unseren Gästen ebenfalls zur Verfügung. Für unsere kleinen Gäste stehen eine Schaukel, ein Sandkasten und ein Baumhaus bereit. Wir würden uns freuen, Ihnen und Ihren Kindern ein erholsames Naturerlebnis ermöglichen zu können. '; echo strlen($var)."\n"; printf("%f\n",(strlen($var)/43)); //$wrapped = wordwrap($var,43,"\n | "); for( $i=50; $i<200; $i++ ) { echo $i."\n"; flush(); wordwrap($var, 43, str_repeat(" ", $i)); } //wordwrap($var, 43, str_repeat(" ", 80)); //$wrapped = wordwrap($var,43,"\n "); ?>
-- PHP Development Mailing List <http://www.php.net/> To unsubscribe, visit: http://www.php.net/unsub.php