Przeglądaj źródła

Protect against direct invocation of `new V8Object` and `new V8Function`.

This fixes issue #75.
C. Scott Ananian 11 lat temu
rodzic
commit
2fe87a4cbc
2 zmienionych plików z 104 dodań i 3 usunięć
  1. 60 0
      tests/direct_construct.phpt
  2. 44 3
      v8js.cc

+ 60 - 0
tests/direct_construct.phpt

@@ -0,0 +1,60 @@
+--TEST--
+Test V8::executeString() : direct construction is prohibited
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+# these are not allowed
+echo "-- NOT ALLOWED --\n";
+try {
+	$a = new V8Object;
+} catch (V8JsScriptException $e) {
+	var_dump($e->getMessage());
+}
+try {
+	$a = new V8Function;
+} catch (V8JsScriptException $e) {
+	var_dump($e->getMessage());
+}
+
+# but these are allowed
+echo "-- ALLOWED --\n";
+$v8 = new V8Js();
+$o = $v8->executeString("({foo:1})");
+var_dump($o);
+$f = $v8->executeString("(function() { return 1; })");
+var_dump($f);
+
+# but these are not allowed
+echo "-- NOT ALLOWED --\n";
+try {
+	$oo = new $o();
+} catch (V8JsScriptException $e) {
+	var_dump($e->getMessage());
+}
+try {
+	$ff = new $f;
+} catch (V8JsScriptException $e) {
+	var_dump($e->getMessage());
+}
+
+// free memory
+$o = null; $f = null; $v8 = null;
+?>
+===EOF===
+--EXPECTF--
+-- NOT ALLOWED --
+string(36) "Can't directly construct V8 objects!"
+string(36) "Can't directly construct V8 objects!"
+-- ALLOWED --
+object(V8Object)#%d (1) {
+  ["foo"]=>
+  int(1)
+}
+object(V8Function)#%d (0) {
+}
+-- NOT ALLOWED --
+string(36) "Can't directly construct V8 objects!"
+string(36) "Can't directly construct V8 objects!"
+===EOF===

+ 44 - 3
v8js.cc

@@ -310,6 +310,10 @@ static HashTable *php_v8js_v8_get_properties(zval *object TSRMLS_DC) /* {{{ */
 			/* the garbage collector is running, don't create more zvals */
 			return NULL;
 		}
+		if (obj->isolate == NULL) {
+			/* Half-constructed object.  Shouldn't happen, but be safe. */
+			return NULL;
+		}
 		ALLOC_HASHTABLE(obj->properties);
 		zend_hash_init(obj->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
 	} else {
@@ -490,6 +494,7 @@ static zend_object_value php_v8js_v8_new(zend_class_entry *ce TSRMLS_DC) /* {{{
 	c = (php_v8js_object *) ecalloc(1, sizeof(*c));
 
 	zend_object_std_init(&c->std, ce TSRMLS_CC);
+	new(&c->v8obj) v8::Persistent<v8::Value>();
 
 	retval.handle = zend_objects_store_put(c, NULL, (zend_objects_free_object_storage_t) php_v8js_v8_free_storage, NULL TSRMLS_CC);
 	retval.handlers = &v8_object_handlers;
@@ -498,6 +503,31 @@ static zend_object_value php_v8js_v8_new(zend_class_entry *ce TSRMLS_DC) /* {{{
 }
 /* }}} */
 
+/* NOTE: We could also override v8_object_handlers.get_constructor to throw
+ * an exception when invoked, but doing so causes the half-constructed object
+ * to leak -- this seems to be a PHP bug.  So we'll define magic __construct
+ * methods instead. */
+
+/* {{{ proto V8Object::__construct()
+ */
+PHP_METHOD(V8Object,__construct)
+{
+	zend_throw_exception(php_ce_v8js_script_exception,
+		"Can't directly construct V8 objects!", 0 TSRMLS_CC);
+	RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto V8Function::__construct()
+ */
+PHP_METHOD(V8Function,__construct)
+{
+	zend_throw_exception(php_ce_v8js_script_exception,
+		"Can't directly construct V8 objects!", 0 TSRMLS_CC);
+	RETURN_FALSE;
+}
+/* }}} */
+
 void php_v8js_create_v8(zval *res, v8::Handle<v8::Value> value, int flags, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
 {
 	php_v8js_object *c;
@@ -1379,6 +1409,18 @@ ZEND_BEGIN_ARG_INFO(arginfo_v8jsscriptexception_no_args, 0)
 ZEND_END_ARG_INFO()
 /* }}} */
 
+static const zend_function_entry v8_object_methods[] = { /* {{{ */
+	PHP_ME(V8Object,	__construct,			NULL,				ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+	{NULL, NULL, NULL}
+};
+/* }}} */
+
+static const zend_function_entry v8_function_methods[] = { /* {{{ */
+	PHP_ME(V8Function,	__construct,			NULL,				ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+	{NULL, NULL, NULL}
+};
+/* }}} */
+
 static const zend_function_entry v8js_methods[] = { /* {{{ */
 	PHP_ME(V8Js,	__construct,			arginfo_v8js_construct,				ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
 	PHP_ME(V8Js,	executeString,			arginfo_v8js_executestring,			ZEND_ACC_PUBLIC)
@@ -1569,13 +1611,13 @@ static PHP_MINIT_FUNCTION(v8js)
 	zend_class_entry ce;
 
 	/* V8Object Class */
-	INIT_CLASS_ENTRY(ce, "V8Object", NULL);
+	INIT_CLASS_ENTRY(ce, "V8Object", v8_object_methods);
 	php_ce_v8_object = zend_register_internal_class(&ce TSRMLS_CC);
 	php_ce_v8_object->ce_flags |= ZEND_ACC_FINAL;
 	php_ce_v8_object->create_object = php_v8js_v8_new;
 
 	/* V8Function Class */
-	INIT_CLASS_ENTRY(ce, "V8Function", NULL);
+	INIT_CLASS_ENTRY(ce, "V8Function", v8_function_methods);
 	php_ce_v8_function = zend_register_internal_class(&ce TSRMLS_CC);
 	php_ce_v8_function->ce_flags |= ZEND_ACC_FINAL;
 	php_ce_v8_function->create_object = php_v8js_v8_new;
@@ -1584,7 +1626,6 @@ static PHP_MINIT_FUNCTION(v8js)
 	memcpy(&v8_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 	v8_object_handlers.clone_obj = NULL;
 	v8_object_handlers.cast_object = NULL;
-	v8_object_handlers.get_constructor = NULL;
 	v8_object_handlers.get_property_ptr_ptr = NULL;
 	v8_object_handlers.has_property = php_v8js_v8_has_property;
 	v8_object_handlers.read_property = php_v8js_v8_read_property;