+59
-2
lines changedFilter options
+59
-2
lines changed Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@ static zend_object* Message_create(zend_class_entry* class_type) {
65
65
Message_SuppressDefaultProperties(class_type);
66
66
zend_object_std_init(&intern->std, class_type);
67
67
intern->std.handlers = &message_object_handlers;
68
+
intern->desc = NULL;
68
69
Arena_Init(&intern->arena);
69
70
return &intern->std;
70
71
}
@@ -89,6 +90,15 @@ static void Message_dtor(zend_object* obj) {
89
90
* Helper function to look up a field given a member name (as a string).
90
91
*/
91
92
static const upb_FieldDef* get_field(Message* msg, zend_string* member) {
93
+
if (!msg || !msg->desc || !msg->desc->msgdef) {
94
+
zend_throw_exception_ex(NULL, 0,
95
+
"Couldn't find descriptor. "
96
+
"The message constructor was likely bypassed, "
97
+
"resulting in an uninitialized descriptor.");
98
+
99
+
return NULL;
100
+
}
101
+
92
102
const upb_MessageDef* m = msg->desc->msgdef;
93
103
const upb_FieldDef* f = upb_MessageDef_FindFieldByNameWithSize(
94
104
m, ZSTR_VAL(member), ZSTR_LEN(member));
Original file line number Diff line number Diff line change
@@ -21,12 +21,27 @@
21
21
use Php\Test\TestNamespace;
22
22
23
23
# This is not allowed, but we at least shouldn't crash.
24
-
class C extends \Google\Protobuf\Internal\Message {
25
-
public function __construct($data = null) {
24
+
class C extends \Google\Protobuf\Internal\Message
25
+
{
26
+
public function __construct($data = null)
27
+
{
26
28
parent::__construct($data);
27
29
}
28
30
}
29
31
32
+
# This is not allowed, but we at least shouldn't crash.
33
+
class TestMessageMockProxy extends TestMessage
34
+
{
35
+
public $_proxy_data = null;
36
+
37
+
public function __construct($data = null)
38
+
{
39
+
$this->_proxy_data = $data;
40
+
// bypass parent constructor
41
+
// This is common behavior by phpunit ond other mock/proxy libraries
42
+
}
43
+
}
44
+
30
45
class GeneratedClassTest extends TestBase
31
46
{
32
47
@@ -1902,6 +1917,38 @@ public function testNoSegfaultWithError()
1902
1917
new TestMessage(['optional_int32' => $this->throwIntendedException()]);
1903
1918
}
1904
1919
1920
+
public function testNoSegfaultWithContructorBypass()
1921
+
{
1922
+
if (!extension_loaded('protobuf')) {
1923
+
$this->markTestSkipped('PHP Protobuf extension is not loaded');
1924
+
}
1925
+
1926
+
$this->expectException(Exception::class);
1927
+
$this->expectExceptionMessage(
1928
+
"Couldn't find descriptor. " .
1929
+
"The message constructor was likely bypassed, resulting in an uninitialized descriptor."
1930
+
);
1931
+
1932
+
$m = new TestMessageMockProxy(['optional_int32' => 123]);
1933
+
1934
+
/**
1935
+
* At this point the message constructor was bypassed and the descriptor is not initialized.
1936
+
* This is a common PHP pattern where a proxy/mock class extends a concrete class,
1937
+
* frequently used in frameworks like PHPUnit, phpspec, and Mockery.
1938
+
*
1939
+
* When this happens, the message's internal descriptor is never initialized.
1940
+
*
1941
+
* Without proper handling, accessing properties via getters (like $this->getOptionalInt32())
1942
+
* would cause the C extension to segfault when trying to access the uninitialized descriptor.
1943
+
*
1944
+
* Instead of segfaulting, we now detect this uninitialized state and throw an exception.
1945
+
*
1946
+
* See: https://github.com/protocolbuffers/protobuf/issues/19978
1947
+
*/
1948
+
1949
+
$m->getOptionalInt32();
1950
+
}
1951
+
1905
1952
public function testNoExceptionWithVarDump()
1906
1953
{
1907
1954
$m = new Sub(['a' => 1]);
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4