From 243060e04f97396afa0a9f05f41d5582818221fb Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Mon, 1 Feb 2021 16:45:09 -0800 Subject: [PATCH 1/2] Protect against stack overflow if the user derives from Message. --- php/ext/google/protobuf/message.c | 24 +++++++++++++++++++++++- php/tests/GeneratedClassTest.php | 17 +++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 0e61770eefa1..c8ea96435ec4 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -554,10 +554,32 @@ static void Message_Initialize(Message *intern, const Descriptor *desc) { */ PHP_METHOD(Message, __construct) { Message* intern = (Message*)Z_OBJ_P(getThis()); - const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis())); + const Descriptor* desc; + zend_class_entry *ce = Z_OBJCE_P(getThis()); upb_arena *arena = Arena_Get(&intern->arena); zval *init_arr = NULL; + // This descriptor should always be available, as the generated __construct + // method calls initOnce() to load the descriptor prior to calling us. + // + // However, if the user created their own class derived from Message, this + // will trigger an infinite construction loop and blow the stack. We + // temporarily clear create_object to break this loop (see check in + // NameMap_GetMessage()). + PBPHP_ASSERT(ce->create_object == Message_create); + ce->create_object = NULL; + desc = Descriptor_GetFromClassEntry(ce); + ce->create_object = Message_create; + + if (!desc) { + zend_throw_exception_ex( + NULL, 0, + "Couldn't find descriptor. Note only generated code may derive from " + "\\Google\\Protobuf\\Internal\\Message"); + return; + } + + Message_Initialize(intern, desc); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) { diff --git a/php/tests/GeneratedClassTest.php b/php/tests/GeneratedClassTest.php index c0de0ba9b26b..d1836a9c1851 100644 --- a/php/tests/GeneratedClassTest.php +++ b/php/tests/GeneratedClassTest.php @@ -24,6 +24,13 @@ use PBEmpty\PBEcho\TestEmptyPackage; use Php\Test\TestNamespace; +# This is not allowed, but we at least shouldn't crash. +class C extends \Google\Protobuf\Internal\Message { + public function __construct($data = null) { + parent::__construct($data); + } +} + class GeneratedClassTest extends TestBase { @@ -1723,6 +1730,16 @@ public function testHasOneof() { $this->assertFalse($m->hasOneofString()); } + ######################################################### + # Test that we don't crash if users create their own messages. + ######################################################### + + public function testUserDefinedClass() { + # This is not allowed, but at least we shouldn't crash. + $this->expectException(Exception::class); + $p = new C(); + } + ######################################################### # Test no segfault when error happens ######################################################### From a6f1797de4f70fc2bea252cac9ce055bcdaa4e8e Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Mon, 1 Feb 2021 18:24:50 -0800 Subject: [PATCH 2/2] For pure-PHP, change error into an exception. --- php/src/Google/Protobuf/Internal/Message.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php index c02d2b451729..64aadf9d260c 100644 --- a/php/src/Google/Protobuf/Internal/Message.php +++ b/php/src/Google/Protobuf/Internal/Message.php @@ -93,8 +93,9 @@ private function initWithGeneratedPool() $pool = DescriptorPool::getGeneratedPool(); $this->desc = $pool->getDescriptorByClassName(get_class($this)); if (is_null($this->desc)) { - user_error(get_class($this) . " is not found in descriptor pool."); - return; + throw new \InvalidArgumentException( + get_class($this) ." is not found in descriptor pool. " . + 'Only generated classes may derive from Message.'); } foreach ($this->desc->getField() as $field) { $setter = $field->getSetter();