diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index cf43d3ad840f..224f6f7237eb 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -4708,7 +4708,7 @@ static struct jit_observer_fcall_is_unobserved_data jit_observer_fcall_is_unobse
ir_ref observer_handler_user = ir_ADD_OFFSET(run_time_cache, zend_observer_fcall_op_array_extension * sizeof(void *));
ir_MERGE_WITH(if_internal_func_end);
- *observer_handler = ir_PHI_2(IR_ADDR, observer_handler_internal, observer_handler_user);
+ *observer_handler = ir_PHI_2(IR_ADDR, observer_handler_user, observer_handler_internal);
}
// JIT: if (*observer_handler == ZEND_OBSERVER_NONE_OBSERVED) {
diff --git a/ext/opcache/tests/jit/gh22158.phpt b/ext/opcache/tests/jit/gh22158.phpt
new file mode 100644
index 000000000000..ce7f9bc55938
--- /dev/null
+++ b/ext/opcache/tests/jit/gh22158.phpt
@@ -0,0 +1,33 @@
+--TEST--
+GH-22158 (Tracing JIT dispatches observer begin handler through the wrong run_time_cache slot on megamorphic calls)
+--EXTENSIONS--
+opcache
+zend_test
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.jit=tracing
+opcache.jit_buffer_size=32M
+opcache.jit_max_polymorphic_calls=0
+zend_test.observer.enabled=1
+zend_test.observer.observe_all=1
+zend_test.observer.show_output=0
+zend_test.observer.reserve_op_array_handle=1
+--FILE--
+f();
+}
+echo $t, "\n";
+?>
+--EXPECT--
+600000
diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c
index 0e7e02df46a4..2da9329e2a04 100644
--- a/ext/pdo_sqlite/pdo_sqlite.c
+++ b/ext/pdo_sqlite/pdo_sqlite.c
@@ -203,7 +203,7 @@ static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int
switch(whence) {
case SEEK_CUR:
if (offset < 0) {
- if (sqlite3_stream->position < (size_t)(-offset)) {
+ if (sqlite3_stream->position < -(size_t)offset) {
sqlite3_stream->position = 0;
*newoffs = -1;
return -1;
@@ -241,7 +241,7 @@ static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int
sqlite3_stream->position = sqlite3_stream->size;
*newoffs = -1;
return -1;
- } else if (sqlite3_stream->size < (size_t)(-offset)) {
+ } else if (sqlite3_stream->size < -(size_t)offset) {
sqlite3_stream->position = 0;
*newoffs = -1;
return -1;
diff --git a/ext/pdo_sqlite/tests/gh20964.phpt b/ext/pdo_sqlite/tests/gh20964.phpt
new file mode 100644
index 000000000000..65070cb1838a
--- /dev/null
+++ b/ext/pdo_sqlite/tests/gh20964.phpt
@@ -0,0 +1,23 @@
+--TEST--
+GH-20964 (fseek() on PDO SQLite blob stream with PHP_INT_MIN causes undefined behavior)
+--EXTENSIONS--
+pdo_sqlite
+--FILE--
+exec('CREATE TABLE test (id INTEGER PRIMARY KEY, data BLOB)');
+$db->exec("INSERT INTO test (id, data) VALUES (1, 'hello')");
+
+$stream = $db->openBlob('test', 'data', 1);
+
+var_dump(fseek($stream, PHP_INT_MIN, SEEK_END));
+
+rewind($stream);
+var_dump(fseek($stream, PHP_INT_MIN, SEEK_CUR));
+
+fclose($stream);
+?>
+--EXPECT--
+int(-1)
+int(-1)
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 1d030caf9d45..e8433e7c4f67 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -1158,12 +1158,12 @@ int make_http_soap_request(
zend_string_release_ex(http_headers, 0);
zend_string_release_ex(http_body, 0);
if (new_uri->scheme == NULL && new_uri->path != NULL) {
- new_uri->scheme = new_uri->scheme ? zend_string_copy(new_uri->scheme) : NULL;
- new_uri->host = new_uri->host ? zend_string_copy(new_uri->host) : NULL;
- new_uri->port = new_uri->port;
+ new_uri->scheme = uri->scheme ? zend_string_copy(uri->scheme) : NULL;
+ new_uri->host = uri->host ? zend_string_copy(uri->host) : NULL;
+ new_uri->port = uri->port;
if (new_uri->path && ZSTR_VAL(new_uri->path)[0] != '/') {
- if (new_uri->path) {
- char *t = ZSTR_VAL(new_uri->path);
+ if (uri->path) {
+ char *t = ZSTR_VAL(uri->path);
char *p = strrchr(t, '/');
if (p) {
zend_string *s = zend_string_concat2(t, (p - t) + 1, ZSTR_VAL(new_uri->path), ZSTR_LEN(new_uri->path));
diff --git a/ext/soap/tests/bugs/relative_redirect.phpt b/ext/soap/tests/bugs/relative_redirect.phpt
new file mode 100644
index 000000000000..774e7cbd98d7
--- /dev/null
+++ b/ext/soap/tests/bugs/relative_redirect.phpt
@@ -0,0 +1,49 @@
+--TEST--
+SOAP client follows a redirect with a scheme-less (relative) Location
+--EXTENSIONS--
+soap
+--SKIPIF--
+
+--FILE--
+',
+ '',
+ 'ok',
+ '';
+} else {
+ http_response_code(302);
+ header("Location: /redirected");
+}
+PHP;
+
+php_cli_server_start($code, null, $args);
+
+$client = new SoapClient(null, [
+ 'location' => 'http://' . PHP_CLI_SERVER_ADDRESS . '/start',
+ 'uri' => 'test-uri',
+]);
+
+try {
+ $client->__soapCall("foo", []);
+ echo "redirect followed\n";
+} catch (SoapFault $e) {
+ echo "SoapFault: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+redirect followed
diff --git a/ext/soap/tests/bugs/relative_redirect_path.phpt b/ext/soap/tests/bugs/relative_redirect_path.phpt
new file mode 100644
index 000000000000..09d4c857cc92
--- /dev/null
+++ b/ext/soap/tests/bugs/relative_redirect_path.phpt
@@ -0,0 +1,49 @@
+--TEST--
+SOAP client follows a redirect with a relative Location resolved against the request path
+--EXTENSIONS--
+soap
+--SKIPIF--
+
+--FILE--
+',
+ '',
+ 'ok',
+ '';
+} else {
+ http_response_code(302);
+ header("Location: redirected");
+}
+PHP;
+
+php_cli_server_start($code, null, $args);
+
+$client = new SoapClient(null, [
+ 'location' => 'http://' . PHP_CLI_SERVER_ADDRESS . '/svc/start',
+ 'uri' => 'test-uri',
+]);
+
+try {
+ $client->__soapCall("foo", []);
+ echo "redirect followed\n";
+} catch (SoapFault $e) {
+ echo "SoapFault: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+redirect followed
diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c
index bef568a62dfd..67ff850cadd0 100644
--- a/ext/sqlite3/sqlite3.c
+++ b/ext/sqlite3/sqlite3.c
@@ -1158,7 +1158,7 @@ static int php_sqlite3_stream_seek(php_stream *stream, zend_off_t offset, int wh
switch(whence) {
case SEEK_CUR:
if (offset < 0) {
- if (sqlite3_stream->position < (size_t)(-offset)) {
+ if (sqlite3_stream->position < -(size_t)offset) {
sqlite3_stream->position = 0;
*newoffs = -1;
return -1;
@@ -1199,7 +1199,7 @@ static int php_sqlite3_stream_seek(php_stream *stream, zend_off_t offset, int wh
sqlite3_stream->position = sqlite3_stream->size;
*newoffs = -1;
return -1;
- } else if (sqlite3_stream->size < (size_t)(-offset)) {
+ } else if (sqlite3_stream->size < -(size_t)offset) {
sqlite3_stream->position = 0;
*newoffs = -1;
return -1;
diff --git a/ext/sqlite3/tests/gh20964.phpt b/ext/sqlite3/tests/gh20964.phpt
new file mode 100644
index 000000000000..852940ba983e
--- /dev/null
+++ b/ext/sqlite3/tests/gh20964.phpt
@@ -0,0 +1,24 @@
+--TEST--
+GH-20964 (fseek() on SQLite3 blob stream with PHP_INT_MIN causes undefined behavior)
+--EXTENSIONS--
+sqlite3
+--FILE--
+exec('CREATE TABLE test (id INTEGER PRIMARY KEY, data BLOB)');
+$db->exec("INSERT INTO test (id, data) VALUES (1, 'hello')");
+
+$stream = $db->openBlob('test', 'data', 1);
+
+var_dump(fseek($stream, PHP_INT_MIN, SEEK_END));
+
+rewind($stream);
+var_dump(fseek($stream, PHP_INT_MIN, SEEK_CUR));
+
+fclose($stream);
+$db->close();
+?>
+--EXPECT--
+int(-1)
+int(-1)
diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c
index 8883a9d1dfb6..2fd4073b4af0 100644
--- a/ext/zend_test/observer.c
+++ b/ext/zend_test/observer.c
@@ -14,6 +14,7 @@
#include "php_test.h"
#include "observer.h"
#include "zend_observer.h"
+#include "zend_extensions.h"
#include "zend_smart_str.h"
#include "ext/standard/php_var.h"
#include "zend_generators.h"
@@ -398,6 +399,7 @@ PHP_INI_BEGIN()
STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_switch", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_switch, zend_zend_test_globals, zend_test_globals)
STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_destroy", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_destroy, zend_zend_test_globals, zend_test_globals)
STD_PHP_INI_BOOLEAN("zend_test.observer.execute_internal", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_execute_internal, zend_zend_test_globals, zend_test_globals)
+ STD_PHP_INI_BOOLEAN("zend_test.observer.reserve_op_array_handle", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_reserve_op_array_handle, zend_zend_test_globals, zend_test_globals)
PHP_INI_END()
void zend_test_observer_init(INIT_FUNC_ARGS)
@@ -406,6 +408,9 @@ void zend_test_observer_init(INIT_FUNC_ARGS)
if (type != MODULE_TEMPORARY) {
REGISTER_INI_ENTRIES();
if (ZT_G(observer_enabled)) {
+ if (ZT_G(observer_reserve_op_array_handle)) {
+ zend_get_op_array_extension_handle("zend_test");
+ }
zend_observer_fcall_register(observer_fcall_init);
}
} else {
diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h
index 65b04856eb31..eb6ef6181c48 100644
--- a/ext/zend_test/php_test.h
+++ b/ext/zend_test/php_test.h
@@ -47,6 +47,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
int observer_fiber_switch;
int observer_fiber_destroy;
int observer_execute_internal;
+ int observer_reserve_op_array_handle;
HashTable *global_weakmap;
int replace_zend_execute_ex;
int register_passes;
diff --git a/main/streams/memory.c b/main/streams/memory.c
index af54c46dd9ad..1cc1886e609d 100644
--- a/main/streams/memory.c
+++ b/main/streams/memory.c
@@ -126,7 +126,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
switch(whence) {
case SEEK_CUR:
if (offset < 0) {
- if (ms->fpos < (size_t)(-offset)) {
+ if (ms->fpos < -(size_t)offset) {
ms->fpos = 0;
*newoffs = -1;
return -1;
@@ -163,7 +163,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
stream->eof = 0;
stream->fatal_error = 0;
return 0;
- } else if (ZSTR_LEN(ms->data) < (size_t)(-offset)) {
+ } else if (ZSTR_LEN(ms->data) < -(size_t)offset) {
ms->fpos = 0;
*newoffs = -1;
return -1;
diff --git a/tests/basic/gh20964.phpt b/tests/basic/gh20964.phpt
new file mode 100644
index 000000000000..2a97164c7924
--- /dev/null
+++ b/tests/basic/gh20964.phpt
@@ -0,0 +1,22 @@
+--TEST--
+GH-20964 (fseek() on php://memory with PHP_INT_MIN causes undefined behavior)
+--FILE--
+
+--EXPECT--
+int(-1)
+bool(false)
+int(-1)
+bool(false)