Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protect against stack overflow if the user derives from Message. #8248

Merged
merged 2 commits into from Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 23 additions & 1 deletion php/ext/google/protobuf/message.c
Expand Up @@ -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) {
Expand Down
5 changes: 3 additions & 2 deletions php/src/Google/Protobuf/Internal/Message.php
Expand Up @@ -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();
Expand Down
17 changes: 17 additions & 0 deletions php/tests/GeneratedClassTest.php
Expand Up @@ -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
{

Expand Down Expand Up @@ -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
#########################################################
Expand Down