diff --git a/NEWS b/NEWS index e947b61b9d4c..2bbe43032ece 100644 --- a/NEWS +++ b/NEWS @@ -77,6 +77,8 @@ PHP NEWS - Intl: . Fixed malformed ResourceBundle::get() error message when fallback is disabled. (Weilin Du) + . Fix incorrect argument positions for invalid start/end arguments in + transliterator_transliterate(). (Weilin Du) . Fixed IntlTimeZone::getDisplayName() to synchronize object error state for invalid display types. (Weilin Du) . Added IntlNumberRangeFormatter class to format an interval of two numbers diff --git a/ext/intl/tests/transliterator_transliterate_error.phpt b/ext/intl/tests/transliterator_transliterate_error.phpt index e11de9333d4c..61190abc6d33 100644 --- a/ext/intl/tests/transliterator_transliterate_error.phpt +++ b/ext/intl/tests/transliterator_transliterate_error.phpt @@ -9,17 +9,38 @@ intl.use_exceptions=true $tr = Transliterator::create("latin"); -try { - var_dump(transliterator_transliterate($tr, "str", 7)); -} catch (Throwable $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; +function dump_throwable(callable $callback): void { + try { + $callback(); + } catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } } -try { +//Arguments +dump_throwable(function() use ($tr) { + var_dump(transliterator_transliterate($tr, "str", 7)); +}); + +dump_throwable(function() use ($tr) { transliterator_transliterate($tr, "str", 7, 6); -} catch (Throwable $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; -} +}); + +dump_throwable(function() use ($tr) { + transliterator_transliterate($tr, "str", 0, -2); +}); + +dump_throwable(function() use ($tr) { + transliterator_transliterate($tr, "str", -1); +}); + +dump_throwable(function() { + transliterator_transliterate("latin", "str", -1); +}); + +dump_throwable(function() use ($tr) { + $tr->transliterate("str", 7, 6); +}); //bad UTF-8 try { @@ -31,5 +52,9 @@ try { ?> --EXPECT-- IntlException: transliterator_transliterate(): Neither "start" nor the "end" arguments can exceed the number of UTF-16 code units (in this case, 3) -ValueError: transliterator_transliterate(): Argument #2 ($string) must be less than or equal to argument #3 ($end) +ValueError: transliterator_transliterate(): Argument #3 ($start) must be less than or equal to argument #4 ($end) +ValueError: transliterator_transliterate(): Argument #4 ($end) must be greater than or equal to -1 +ValueError: transliterator_transliterate(): Argument #3 ($start) must be greater than or equal to 0 +ValueError: transliterator_transliterate(): Argument #3 ($start) must be greater than or equal to 0 +ValueError: Transliterator::transliterate(): Argument #2 ($start) must be less than or equal to argument #3 ($end) IntlException: transliterator_transliterate(): String conversion of string to UTF-16 failed diff --git a/ext/intl/transliterator/transliterator_methods.cpp b/ext/intl/transliterator/transliterator_methods.cpp index 2dce4612e7a2..16e5cf8b95d3 100644 --- a/ext/intl/transliterator/transliterator_methods.cpp +++ b/ext/intl/transliterator/transliterator_methods.cpp @@ -276,14 +276,16 @@ U_CFUNC PHP_FUNCTION( transliterator_transliterate ) zend_long start = 0, limit = -1; int success = 0; + bool is_method; zval tmp_object; TRANSLITERATOR_METHOD_INIT_VARS; object = getThis(); + is_method = object != NULL; ZVAL_UNDEF(&tmp_object); - if (object == nullptr) { + if (!is_method) { /* in non-OOP version, accept both a transliterator and a string */ zend_string *arg1_str; zend_object *arg1_obj; @@ -320,17 +322,17 @@ U_CFUNC PHP_FUNCTION( transliterator_transliterate ) } if (limit < -1) { - zend_argument_value_error(object ? 3 : 4, "must be greater than or equal to -1"); + zend_argument_value_error(is_method ? 3 : 4, "must be greater than or equal to -1"); goto cleanup_object; } if (start < 0) { - zend_argument_value_error(object ? 2 : 3, "must be greater than or equal to 0"); + zend_argument_value_error(is_method ? 2 : 3, "must be greater than or equal to 0"); goto cleanup_object; } if (limit != -1 && start > limit) { - zend_argument_value_error(object ? 2 : 3, "must be less than or equal to argument #%d ($end)", object ? 3 : 4); + zend_argument_value_error(is_method ? 2 : 3, "must be less than or equal to argument #%d ($end)", is_method ? 3 : 4); goto cleanup_object; } diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index e7f29c6a354b..2db966c1661e 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -504,6 +504,12 @@ PHP_METHOD(Openssl_Session, __unserialize) Z_PARAM_ARRAY_HT(data) ZEND_PARSE_PARAMETERS_END(); + php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(ZEND_THIS); + if (obj->session != NULL) { + zend_throw_error(NULL, "Cannot call Openssl\\Session::__unserialize() on an already-initialized session"); + RETURN_THROWS(); + } + zval *pem_zv = zend_hash_str_find(data, ZEND_STRL("pem")); if (!pem_zv || Z_TYPE_P(pem_zv) != IS_STRING) { zend_throw_exception(php_openssl_exception_ce, "Invalid serialization data", 0); @@ -524,7 +530,6 @@ PHP_METHOD(Openssl_Session, __unserialize) RETURN_THROWS(); } - php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(ZEND_THIS); obj->session = session; /* Populate id property */ diff --git a/ext/openssl/tests/session_unserialize_repeat.phpt b/ext/openssl/tests/session_unserialize_repeat.phpt new file mode 100644 index 000000000000..b1e33c2d03bd --- /dev/null +++ b/ext/openssl/tests/session_unserialize_repeat.phpt @@ -0,0 +1,84 @@ +--TEST-- +Openssl\Session::__unserialize throws on a repeat call +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 10); + if ($client) { + fwrite($client, "ok\n"); + fclose($client); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $captured = null; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function ($s, $session) use (&$captured) { + $captured = $session; + return true; + }, + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + ]]); + + $c = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 10, + STREAM_CLIENT_CONNECT, $ctx); + if (!$c) { + echo "connect failed: $errstr\n"; + return; + } + fread($c, 8); + fclose($c); + + if (!$captured instanceof Openssl\Session) { + echo "no session captured\n"; + return; + } + + $payload = $captured->__serialize(); + $sess = unserialize(serialize($captured)); + echo "first: " . (is_object($sess) ? get_class($sess) : "fail") . "\n"; + + try { + $sess->__unserialize($payload); + echo "second: no throw\n"; + } catch (Error $e) { + echo "second: " . $e->getMessage() . "\n"; + } + + echo "alive\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session-unserialize-repeat', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +first: Openssl\Session +second: Cannot call Openssl\Session::__unserialize() on an already-initialized session +alive