Add handling for PCRE errors in ustringGsub

Bug: T130823
Change-Id: I6fab71c82ddab92daf6b369cb9857d9892f2d246
This commit is contained in:
Brad Jorsch 2016-03-24 10:08:29 -04:00
parent 35b3cdcb72
commit ba19a82c06
3 changed files with 97 additions and 0 deletions

View file

@ -695,6 +695,52 @@ class Scribunto_LuaUstringLibrary extends Scribunto_LuaLibraryBase {
$count = 0; $count = 0;
$s2 = preg_replace_callback( $re, $cb, $s, $n, $count ); $s2 = preg_replace_callback( $re, $cb, $s, $n, $count );
if ( $s2 === null ) {
self::handlePCREError( preg_last_error(), $pattern );
}
return array( $s2, $count ); return array( $s2, $count );
} }
/**
* Handle a PCRE error
* @param int $error From preg_last_error()
* @param string $pattern Pattern being matched
* @throws Scribunto_LuaError
*/
private function handlePCREError( $error, $pattern ) {
$PREG_JIT_STACKLIMIT_ERROR = defined( 'PREG_JIT_STACKLIMIT_ERROR' )
? PREG_JIT_STACKLIMIT_ERROR
: 'PREG_JIT_STACKLIMIT_ERROR';
$error = preg_last_error();
switch ( $error ) {
case PREG_NO_ERROR:
// Huh?
break;
case PREG_INTERNAL_ERROR:
throw new Scribunto_LuaError( "PCRE internal error" );
case PREG_BACKTRACK_LIMIT_ERROR:
throw new Scribunto_LuaError(
"PCRE backtrack limit reached while matching pattern '$pattern'"
);
case PREG_RECURSION_LIMIT_ERROR:
throw new Scribunto_LuaError(
"PCRE recursion limit reached while matching pattern '$pattern'"
);
case PREG_BAD_UTF8_ERROR:
// Should have alreay been caught, but just in case
throw new Scribunto_LuaError( "PCRE bad UTF-8 error" );
case PREG_BAD_UTF8_OFFSET_ERROR:
// Shouldn't happen, but just in case
throw new Scribunto_LuaError( "PCRE bad UTF-8 offset error" );
case $PREG_JIT_STACKLIMIT_ERROR:
throw new Scribunto_LuaError(
"PCRE JIT stack limit reached while matching pattern '$pattern'"
);
default:
throw new Scribunto_LuaError(
"PCRE error code $error while matching pattern '$pattern'"
);
}
}
} }

View file

@ -18,4 +18,18 @@ class Scribunto_LuaUstringLibraryPureLuaTests extends Scribunto_LuaUstringLibrar
', 'fortest' ) ', 'fortest' )
); );
} }
/**
* @dataProvider providePCREErrors
*/
public function testPCREErrors( $ini, $args, $error ) {
// Not applicable
$this->assertTrue( true );
}
public static function providePCREErrors() {
return array(
array( array(), array(), null ),
);
}
} }

View file

@ -60,6 +60,43 @@ class Scribunto_LuaUstringLibraryTests extends Scribunto_LuaEngineTestBase {
$this->assertSame( $expected, $actual ); $this->assertSame( $expected, $actual );
$this->luaTestName = null; $this->luaTestName = null;
} }
/**
* @dataProvider providePCREErrors
*/
public function testPCREErrors( $ini, $args, $error ) {
$reset = array();
foreach ( $ini as $key => $value ) {
$old = ini_set( $key, $value );
if ( $old === false ) {
$this->markTestSkipped( "Failed to set ini setting $key = $value" );
}
$reset[] = new ScopedCallback( 'ini_set', array( $key, $old ) );
}
$interpreter = $this->getEngine()->getInterpreter();
$func = $interpreter->loadString( 'return mw.ustring.gsub( ... )', 'fortest' );
try {
call_user_func_array(
array( $interpreter, 'callFunction' ),
array_merge( array( $func ), $args )
);
$this->fail( 'Expected exception not thrown' );
} catch ( Scribunto_LuaError $e ) {
$this->assertSame( $error, $e->getMessage() );
}
}
public static function providePCREErrors() {
return array(
array(
array( 'pcre.backtrack_limit' => 10 ),
array( 'zzzzzzzzzzzzzzzzzzzz', '^(.-)[abc]*$', '%1' ),
'Lua error: PCRE backtrack limit reached while matching pattern \'^(.-)[abc]*$\'.'
),
// @TODO: Figure out patterns that hit other PCRE limits
);
}
} }
class UstringLibraryNormalizationTestProvider extends Scribunto_LuaDataProvider { class UstringLibraryNormalizationTestProvider extends Scribunto_LuaDataProvider {