Skip to content

Commit

Permalink
Fix #77177: Serializing or unserializing COM objects crashes
Browse files Browse the repository at this point in the history
Firstly, we avoid returning NULL from the get_property handler, but
instead return an empty HashTable, which already prevents the crashes.
Secondly, since (de-)serialization obviously makes no sense for COM,
DOTNET and VARIANT objects (at least with the current implementation),
we prohibit it right away.
  • Loading branch information
cmb69 committed Nov 23, 2018
1 parent f2de4d1 commit 115ee49
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 1 deletion.
3 changes: 3 additions & 0 deletions NEWS
Expand Up @@ -6,6 +6,9 @@ PHP NEWS
. Fixed bug #71041 (zend_signal_startup() needs ZEND_API).
(Valentin V. Bartenev)

- COM:
. Fixed bug #77177 (Serializing or unserializing COM objects crashes). (cmb)

- Sockets:
. Fixed bug #77136 (Unsupported IPV6_RECVPKTINFO constants on macOS).
(Mizunashi Mana)
Expand Down
9 changes: 9 additions & 0 deletions ext/com_dotnet/com_extension.c
Expand Up @@ -30,6 +30,7 @@
#include "php_com_dotnet.h"
#include "php_com_dotnet_internal.h"
#include "Zend/zend_exceptions.h"
#include "Zend/zend_interfaces.h"

ZEND_DECLARE_MODULE_GLOBALS(com_dotnet)
static PHP_GINIT_FUNCTION(com_dotnet);
Expand Down Expand Up @@ -354,6 +355,8 @@ PHP_MINIT_FUNCTION(com_dotnet)
{
zend_class_entry ce, *tmp;

zend_hash_init(&com_dotnet_object_properties, 0, NULL, NULL, 0);

php_com_wrapper_minit(INIT_FUNC_ARGS_PASSTHRU);
php_com_persist_minit(INIT_FUNC_ARGS_PASSTHRU);

Expand All @@ -372,11 +375,15 @@ PHP_MINIT_FUNCTION(com_dotnet)
ce.create_object = php_com_object_new;
php_com_variant_class_entry = zend_register_internal_class(&ce);
php_com_variant_class_entry->get_iterator = php_com_iter_get;
php_com_variant_class_entry->serialize = zend_class_serialize_deny;
php_com_variant_class_entry->unserialize = zend_class_unserialize_deny;

INIT_CLASS_ENTRY(ce, "com", NULL);
ce.create_object = php_com_object_new;
tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry);
tmp->get_iterator = php_com_iter_get;
tmp->serialize = zend_class_serialize_deny;
tmp->unserialize = zend_class_unserialize_deny;

zend_ts_hash_init(&php_com_typelibraries, 0, NULL, php_com_typelibrary_dtor, 1);

Expand All @@ -385,6 +392,8 @@ PHP_MINIT_FUNCTION(com_dotnet)
ce.create_object = php_com_object_new;
tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry);
tmp->get_iterator = php_com_iter_get;
tmp->serialize = zend_class_serialize_deny;
tmp->unserialize = zend_class_unserialize_deny;
#endif

REGISTER_INI_ENTRIES();
Expand Down
4 changes: 3 additions & 1 deletion ext/com_dotnet/com_handlers.c
Expand Up @@ -29,6 +29,8 @@
#include "php_com_dotnet_internal.h"
#include "Zend/zend_exceptions.h"

const HashTable com_dotnet_object_properties;

static zval *com_property_read(zval *object, zval *member, int type, void **cahce_slot, zval *rv)
{
php_com_dotnet_object *obj;
Expand Down Expand Up @@ -231,7 +233,7 @@ static HashTable *com_properties_get(zval *object)
* infinite recursion when the hash is displayed via var_dump().
* Perhaps it is best to leave it un-implemented.
*/
return NULL;
return &com_dotnet_object_properties;
}

static void function_dtor(zval *zv)
Expand Down
2 changes: 2 additions & 0 deletions ext/com_dotnet/php_com_dotnet_internal.h
Expand Up @@ -31,6 +31,8 @@

#include "zend_ts_hash.h"

extern const HashTable com_dotnet_object_properties;

typedef struct _php_com_dotnet_object {
zend_object zo;

Expand Down
57 changes: 57 additions & 0 deletions ext/com_dotnet/tests/bug77177.phpt
@@ -0,0 +1,57 @@
--TEST--
Bug #77177 (Serializing or unserializing COM objects crashes)
--SKIPIF--
<?php
if (!extension_loaded('com_dotnet')) die('skip com_dotnet extension not available');
?>
--FILE--
<?php
$com = new COM("WScript.Shell");
$dotnet = new DOTNET("mscorlib", "System.Collections.Stack");
$variant = new VARIANT;
foreach ([$com, $dotnet, $variant] as $object) {
try {
serialize($object);
} catch (Exception $ex) {
echo "Exception: {$ex->getMessage()}\n";
}
}

$strings = ['C:3:"com":0:{}', 'C:6:"dotnet":0:{}', 'C:7:"variant":0:{}'];
foreach ($strings as $string) {
try {
unserialize($string);
} catch (Exception $ex) {
echo "Exception: {$ex->getMessage()}\n";
}
}

$strings = ['O:3:"com":0:{}', 'O:6:"dotnet":0:{}', 'O:7:"variant":0:{}'];
foreach ($strings as $string) {
var_dump(unserialize($string));
}
?>
===DONE===
--EXPECTF--
Exception: Serialization of 'com' is not allowed
Exception: Serialization of 'dotnet' is not allowed
Exception: Serialization of 'variant' is not allowed
Exception: Unserialization of 'com' is not allowed
Exception: Unserialization of 'dotnet' is not allowed
Exception: Unserialization of 'variant' is not allowed

Warning: Erroneous data format for unserializing 'com' in %s on line %d

Notice: unserialize(): Error at offset 13 of 14 bytes in %s on line %d
bool(false)

Warning: Erroneous data format for unserializing 'dotnet' in %s on line %d

Notice: unserialize(): Error at offset 16 of 17 bytes in %s on line %d
bool(false)

Warning: Erroneous data format for unserializing 'variant' in %s on line %d

Notice: unserialize(): Error at offset 17 of 18 bytes in %s on line %d
bool(false)
===DONE===

0 comments on commit 115ee49

Please sign in to comment.