From: Tim Starling Date: Thu, 16 Feb 2012 23:27:00 +0000 (+0000) Subject: (bug 34428) Fixed hash mismatch errors in DiffHistoryBlob::patch() by simulating... X-Git-Tag: 1.31.0-rc.0~24659 X-Git-Url: http://git.cyclocoop.org/%24image?a=commitdiff_plain;h=e1ee5c469b685cbdb345f2cfcbef66855e05a01a;p=lhc%2Fweb%2Fwiklou.git (bug 34428) Fixed hash mismatch errors in DiffHistoryBlob::patch() by simulating LibXDiff's broken Adler-32 implementation. --- diff --git a/RELEASE-NOTES-1.20 b/RELEASE-NOTES-1.20 index bb894f86e5..5090672bdf 100644 --- a/RELEASE-NOTES-1.20 +++ b/RELEASE-NOTES-1.20 @@ -29,6 +29,8 @@ production. * (bug 12021) Added user talk link on Special:Listusers * (bug 34445) section edit and TOC hide/show links are excluded from selection and copy/paste on supporting browsers +* (bug 34428) Fixed incorrect hash mismatch errors in the DiffHistoryBlob + history compression method. === API changes in 1.20 === * (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API. diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php index cece3ec1fa..7887ff85d9 100644 --- a/includes/HistoryBlob.php +++ b/includes/HistoryBlob.php @@ -515,14 +515,11 @@ class DiffHistoryBlob implements HistoryBlob { $header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) ); - # Check the checksum if mhash is available - if ( extension_loaded( 'mhash' ) ) { - $ofp = mhash( MHASH_ADLER32, $base ); - if ( $ofp !== substr( $diff, 0, 4 ) ) { - wfDebug( __METHOD__. ": incorrect base checksum\n" ); - // Temp patch for bug 34428: don't return false - //return false; - } + # Check the checksum if hash/mhash is available + $ofp = $this->xdiffAdler32( $base ); + if ( $ofp !== false && $ofp !== substr( $diff, 0, 4 ) ) { + wfDebug( __METHOD__. ": incorrect base checksum\n" ); + return false; } if ( $header['csize'] != strlen( $base ) ) { wfDebug( __METHOD__. ": incorrect base length\n" ); @@ -561,6 +558,29 @@ class DiffHistoryBlob implements HistoryBlob { return $out; } + /** + * Compute a binary "Adler-32" checksum as defined by LibXDiff, i.e. with + * the bytes backwards and initialised with 0 instead of 1. See bug 34428. + * + * Returns false if no hashing library is available + */ + function xdiffAdler32( $s ) { + static $init; + if ( $init === null ) { + // The real Adler-32 checksum of this string is zero, so it + // initialises the state to the LibXDiff initial value. + $init = str_repeat( "\xf0", 205 ) . "\xee" . str_repeat( "\xf0", 67 ) . "\x02"; + } + if ( function_exists( 'hash' ) ) { + $hash = hash( 'adler32', $init . $s, true ); + } elseif ( function_exists( 'mhash' ) ) { + $hash = mhash( MHASH_ADLER32, $init . $s ); + } else { + return false; + } + return strrev( $hash ); + } + function uncompress() { if ( !$this->mDiffs ) { return; diff --git a/tests/phpunit/includes/DiffHistoryBlobTest.php b/tests/phpunit/includes/DiffHistoryBlobTest.php new file mode 100644 index 0000000000..eda55e0654 --- /dev/null +++ b/tests/phpunit/includes/DiffHistoryBlobTest.php @@ -0,0 +1,36 @@ +markTestSkipped( 'The xdiff extension is not available' ); + return; + } + if ( !extension_loaded( 'hash' ) && !extension_loaded( 'mhash' ) ) { + $this->markTestSkipped( 'Neither the hash nor mhash extension is available' ); + return; + } + } + + /** + * Test for DiffHistoryBlob::xdiffAdler32() + * @dataProvider provideXdiffAdler32 + */ + function testXdiffAdler32( $input ) { + $xdiffHash = substr( xdiff_string_rabdiff( $input, '' ), 0, 4 ); + $dhb = new DiffHistoryBlob; + $myHash = $dhb->xdiffAdler32( $input ); + $this->assertSame( bin2hex( $xdiffHash ), bin2hex( $myHash ), + "Hash of " . addcslashes( $input, "\0..\37!@\@\177..\377" ) ); + } + + function provideXdiffAdler32() { + return array( + array( '', 'Empty string' ), + array( "\0", 'Null' ), + array( "\0\0\0", "Several nulls" ), + array( "Hello", "An ASCII string" ), + array( str_repeat( "x", 6000 ), "A string larger than xdiff's NMAX (5552)" ) + ); + } +}