From 754a663e81785494f4979eaa79dc5b724e6db894 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Wed, 8 Mar 2017 18:14:40 +0100 Subject: [PATCH 01/16] move #undef directives to the top of the file --- v8js_convert.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/v8js_convert.cc b/v8js_convert.cc index 5f6aebd..8d273f8 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -32,6 +32,11 @@ extern "C" { #include "zend_closures.h" } +/* On Windows there are max and min macros, which would clobber the + * method names of std::numeric_limits< > otherwise. */ +#undef max +#undef min + static int v8js_is_assoc_array(HashTable *myht TSRMLS_DC) /* {{{ */ { zend_string *key; @@ -138,10 +143,6 @@ v8::Handle zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) case IS_LONG: v = Z_LVAL_P(value); - /* On Windows there are max and min macros, which would clobber the - * method names of std::numeric_limits< > otherwise. */ -#undef max -#undef min if (v < - std::numeric_limits::min() || v > std::numeric_limits::max()) { jsValue = V8JS_FLOAT(static_cast(v)); } else { From 7af272a7099236ef2b31b2a547fa470850f20556 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Wed, 8 Mar 2017 18:16:12 +0100 Subject: [PATCH 02/16] throw on string export beyond int-max length --- v8js_convert.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/v8js_convert.cc b/v8js_convert.cc index 8d273f8..6ef9424 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -20,6 +20,7 @@ #include #include "php_v8js_macros.h" +#include "v8js_exceptions.h" #include "v8js_object_export.h" #include "v8js_v8object_class.h" #include "v8js_v8.h" @@ -30,6 +31,7 @@ extern "C" { #include "ext/standard/php_string.h" #include "zend_interfaces.h" #include "zend_closures.h" +#include "zend_exceptions.h" } /* On Windows there are max and min macros, which would clobber the @@ -107,6 +109,7 @@ v8::Handle zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) { v8::Handle jsValue; zend_long v; + zend_string *value_str; zend_class_entry *ce; switch (Z_TYPE_P(value)) @@ -138,7 +141,14 @@ v8::Handle zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) break; case IS_STRING: - jsValue = V8JS_ZSTR(Z_STR_P(value)); + value_str = Z_STR_P(value); + if (ZSTR_LEN(value_str) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "String exceeds maximum string length", 0); + break; + } + + jsValue = v8::String::NewFromUtf8(isolate, ZSTR_VAL(value_str), v8::String::kNormalString, static_cast(ZSTR_LEN(value_str))); break; case IS_LONG: From fe2e289a3274c363a39c34881e5f13783d2dc751 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Wed, 8 Mar 2017 18:16:39 +0100 Subject: [PATCH 03/16] ulong -> zend_ulong --- v8js_convert.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v8js_convert.cc b/v8js_convert.cc index 6ef9424..a4a47f2 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -42,7 +42,7 @@ extern "C" { static int v8js_is_assoc_array(HashTable *myht TSRMLS_DC) /* {{{ */ { zend_string *key; - ulong index, idx = 0; + zend_ulong index, idx = 0; ZEND_HASH_FOREACH_KEY(myht, index, key) { if(key) { From cd28b5c3203c2b151e759bb46fe76a007578e485 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 07:39:31 +0100 Subject: [PATCH 04/16] v8js_class: add size checks + precission down casts --- v8js_class.cc | 102 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 13 deletions(-) diff --git a/v8js_class.cc b/v8js_class.cc index ccbe7a7..5dc2171 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -36,6 +36,11 @@ extern "C" { #include "zend_exceptions.h" } +/* On Windows there are max and min macros, which would clobber the + * method names of std::numeric_limits< > otherwise. */ +#undef max +#undef min + #define PHP_V8JS_SCRIPT_RES_NAME "V8Js script" /* {{{ Class Entries */ @@ -362,8 +367,14 @@ static PHP_METHOD(V8Js, __construct) if (Z_TYPE_P(snapshot_blob) == IS_STRING) { ZVAL_COPY(&c->zval_snapshot_blob, snapshot_blob); + if (Z_STRLEN_P(snapshot_blob) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Snapshot size exceeds maximum supported length", 0); + return; + } + c->snapshot_blob.data = Z_STRVAL_P(snapshot_blob); - c->snapshot_blob.raw_size = Z_STRLEN_P(snapshot_blob); + c->snapshot_blob.raw_size = static_cast(Z_STRLEN_P(snapshot_blob)); c->create_params.snapshot_blob = &c->snapshot_blob; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument snapshot_blob expected to be of string type"); @@ -445,7 +456,14 @@ static PHP_METHOD(V8Js, __construct) /* Set class name for PHP object */ zend_class_entry *ce = Z_OBJCE_P(getThis()); - php_obj_t->SetClassName(V8JS_SYML(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name))); + + if (ZSTR_LEN(ce->name) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "PHP object class name exceeds maximum supported length", 0); + return; + } + + php_obj_t->SetClassName(V8JS_SYML(ZSTR_VAL(ce->name), static_cast(ZSTR_LEN(ce->name)))); /* Register Get accessor for passed variables */ if (vars_arr && zend_hash_num_elements(Z_ARRVAL_P(vars_arr)) > 0) { @@ -453,9 +471,22 @@ static PHP_METHOD(V8Js, __construct) } /* Set name for the PHP JS object */ - v8::Local object_name_js = (object_name && ZSTR_LEN(object_name)) - ? V8JS_ZSYM(object_name) - : V8JS_SYM("PHP"); + v8::Local object_name_js; + + if (object_name && ZSTR_LEN(object_name)) { + if (ZSTR_LEN(object_name) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "PHP JS object class name exceeds maximum supported length", 0); + return; + } + + object_name_js = v8::String::NewFromUtf8(isolate, ZSTR_VAL(object_name), + v8::String::kInternalizedString, static_cast(ZSTR_LEN(object_name))); + } + else { + object_name_js = V8JS_SYM("PHP"); + } + c->object_name.Reset(isolate, object_name_js); /* Add the PHP object into global object */ @@ -473,9 +504,18 @@ static PHP_METHOD(V8Js, __construct) if(property_info && property_info != ZEND_WRONG_PROPERTY_INFO && (property_info->flags & ZEND_ACC_PUBLIC)) { + if (ZSTR_LEN(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Property name exceeds maximum supported length", 0); + return; + } + + v8::Local key = v8::String::NewFromUtf8(isolate, ZSTR_VAL(member), + v8::String::kInternalizedString, static_cast(ZSTR_LEN(member))); + /* Write value to PHP JS object */ value = OBJ_PROP(Z_OBJ_P(getThis()), property_info->offset); - php_obj->ForceSet(V8JS_ZSYM(member), zval_to_v8js(value, isolate TSRMLS_CC), v8::ReadOnly); + php_obj->ForceSet(key, zval_to_v8js(value, isolate TSRMLS_CC), v8::ReadOnly); } } ZEND_HASH_FOREACH_END(); @@ -527,7 +567,15 @@ static PHP_METHOD(V8Js, __construct) continue; } - v8::Local method_name = V8JS_ZSTR(method_ptr->common.function_name); + if (ZSTR_LEN(method_ptr->common.function_name) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Method name exceeds maximum supported length", 0); + return; + } + + v8::Local method_name = v8::String::NewFromUtf8(isolate, + ZSTR_VAL(method_ptr->common.function_name), v8::String::kInternalizedString, + static_cast(ZSTR_LEN(method_ptr->common.function_name))); v8::Local ft; /*try { @@ -567,7 +615,7 @@ PHP_METHOD(V8Js, __wakeup) } /* }}} */ -static void v8js_compile_script(zval *this_ptr, zend_string *str, zend_string *identifier, v8js_script **ret TSRMLS_DC) +static void v8js_compile_script(zval *this_ptr, const zend_string *str, const zend_string *identifier, v8js_script **ret TSRMLS_DC) { v8js_script *res = NULL; @@ -577,10 +625,24 @@ static void v8js_compile_script(zval *this_ptr, zend_string *str, zend_string *i v8::TryCatch try_catch; /* Set script identifier */ - v8::Local sname = identifier ? V8JS_ZSTR(identifier) : V8JS_SYM("V8Js::compileString()"); + if (identifier && ZSTR_LEN(identifier) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Script identifier exceeds maximum supported length", 0); + return; + } + + v8::Local sname = identifier + ? v8::String::NewFromUtf8(isolate, ZSTR_VAL(identifier), v8::String::kNormalString, static_cast(ZSTR_LEN(identifier))) + : V8JS_SYM("V8Js::compileString()"); /* Compiles a string context independently. TODO: Add a php function which calls this and returns the result as resource which can be executed later. */ - v8::Local source = V8JS_ZSTR(str); + if (ZSTR_LEN(str) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Script source exceeds maximum supported length", 0); + return; + } + + v8::Local source = v8::String::NewFromUtf8(isolate, ZSTR_VAL(str), v8::String::kNormalString, static_cast(ZSTR_LEN(str))); v8::Local script = v8::Script::Compile(source, sname); /* Compile errors? */ @@ -1020,7 +1082,7 @@ static PHP_METHOD(V8Js, registerExtension) static PHP_METHOD(V8Js, getExtensions) { v8js_jsext *jsext; - ulong index; + zend_ulong index; zend_string *key; zval *val, ext; @@ -1209,8 +1271,15 @@ static void v8js_write_property(zval *object, zval *member, zval *value, void ** v8::Local object_name_js = v8::Local::New(isolate, c->object_name); v8::Local jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject(); + if (Z_STRLEN_P(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Property name exceeds maximum supported length", 0); + return; + } + /* Write value to PHP JS object */ - jsobj->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, isolate TSRMLS_CC), v8::ReadOnly); + v8::Local key = V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))); + jsobj->ForceSet(key, zval_to_v8js(value, isolate TSRMLS_CC), v8::ReadOnly); } /* Write value to PHP object */ @@ -1226,8 +1295,15 @@ static void v8js_unset_property(zval *object, zval *member, void **cache_slot TS v8::Local object_name_js = v8::Local::New(isolate, c->object_name); v8::Local jsobj = V8JS_GLOBAL(isolate)->Get(object_name_js)->ToObject(); + if (Z_STRLEN_P(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Property name exceeds maximum supported length", 0); + return; + } + /* Delete value from PHP JS object */ - jsobj->Delete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); + v8::Local key = V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))); + jsobj->Delete(key); /* Unset from PHP object */ std_object_handlers.unset_property(object, member, NULL); From b6d47ac7a8bac6a9a000cf179d5c155a0ec02eb5 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 22:47:24 +0100 Subject: [PATCH 05/16] declare v8_initialized flag as bool --- php_v8js_macros.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/php_v8js_macros.h b/php_v8js_macros.h index c91a8ad..d2f32d9 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -102,7 +102,7 @@ struct v8js_timer_ctx; /* Module globals */ ZEND_BEGIN_MODULE_GLOBALS(v8js) // Thread-local cache whether V8 has been initialized so far - int v8_initialized; + bool v8_initialized; /* Ini globals */ bool use_date; /* Generate JS Date objects instead of PHP DateTime */ @@ -141,7 +141,7 @@ ZEND_EXTERN_MODULE_GLOBALS(v8js) */ struct _v8js_process_globals { #ifdef ZTS - int v8_initialized; + bool v8_initialized; std::mutex lock; #endif From e5f57b766121519a04cde0ffb9b13b5cd7203478 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 22:48:23 +0100 Subject: [PATCH 06/16] move #undef min/max to v8js_v8.h --- v8js_class.cc | 5 ----- v8js_convert.cc | 5 ----- v8js_v8.h | 6 ++++++ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/v8js_class.cc b/v8js_class.cc index 5dc2171..7f963ed 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -36,11 +36,6 @@ extern "C" { #include "zend_exceptions.h" } -/* On Windows there are max and min macros, which would clobber the - * method names of std::numeric_limits< > otherwise. */ -#undef max -#undef min - #define PHP_V8JS_SCRIPT_RES_NAME "V8Js script" /* {{{ Class Entries */ diff --git a/v8js_convert.cc b/v8js_convert.cc index a4a47f2..02c862a 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -34,11 +34,6 @@ extern "C" { #include "zend_exceptions.h" } -/* On Windows there are max and min macros, which would clobber the - * method names of std::numeric_limits< > otherwise. */ -#undef max -#undef min - static int v8js_is_assoc_array(HashTable *myht TSRMLS_DC) /* {{{ */ { zend_string *key; diff --git a/v8js_v8.h b/v8js_v8.h index 555dae3..646eac5 100644 --- a/v8js_v8.h +++ b/v8js_v8.h @@ -36,6 +36,12 @@ #define V8JS_GLOBAL(isolate) ((isolate)->GetCurrentContext()->Global()) +/* On Windows there are max and min macros, which would clobber the + * method names of std::numeric_limits< > otherwise. */ +#undef max +#undef min + + /* Extracts a C string from a V8 Utf8Value. */ static inline const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */ { From 78807b672053474ede7087aa63b4b7d899b3af64 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 22:49:23 +0100 Subject: [PATCH 07/16] do comparison instead of (bool) cast --- v8js.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v8js.cc b/v8js.cc index 4adb397..0cba337 100644 --- a/v8js.cc +++ b/v8js.cc @@ -77,7 +77,7 @@ static bool v8js_ini_to_bool(const zend_string *new_value) /* {{{ */ } else if (ZSTR_LEN(new_value) == 4 && strcasecmp("true", ZSTR_VAL(new_value)) == 0) { return true; } else { - return (bool) atoi(ZSTR_VAL(new_value)); + return 0 != atoi(ZSTR_VAL(new_value)); } } /* }}} */ From d69d8e967160268bd45827d855f28308b47ceb84 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 22:50:28 +0100 Subject: [PATCH 08/16] check ArrayAccess offset/count against int max limits --- v8js_array_access.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/v8js_array_access.cc b/v8js_array_access.cc index 3419172..44bc024 100644 --- a/v8js_array_access.cc +++ b/v8js_array_access.cc @@ -16,6 +16,7 @@ #include "php_v8js_macros.h" #include "v8js_array_access.h" +#include "v8js_exceptions.h" #include "v8js_object_export.h" extern "C" { @@ -24,6 +25,7 @@ extern "C" { #include "ext/standard/php_string.h" #include "zend_interfaces.h" #include "zend_closures.h" +#include "zend_exceptions.h" } static zval v8js_array_access_dispatch(zend_object *object, const char *method_name, int param_count, @@ -123,8 +125,15 @@ static int v8js_array_access_get_count_result(zend_object *object TSRMLS_DC) /* return 0; } - int result = Z_LVAL(php_value); - return result; + zend_long result = Z_LVAL(php_value); + + if (result > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Array size/offset exceeds maximum supported length", 0); + return 0; + } + + return static_cast(result); } /* }}} */ From a493919a17633f82c6121ccc2890b65d854341ac Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 22:58:53 +0100 Subject: [PATCH 09/16] extract function zend_long_to_v8js --- php_v8js_macros.h | 7 +++++-- v8js_convert.cc | 22 +++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/php_v8js_macros.h b/php_v8js_macros.h index d2f32d9..f781fb5 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2016 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ @@ -79,7 +79,10 @@ extern "C" { /* Convert zval into V8 value */ -v8::Handle zval_to_v8js(zval *, v8::Isolate * TSRMLS_DC); +v8::Handle zval_to_v8js(zval *, v8::Isolate *); + +/* Convert zend_long into V8 value */ +v8::Handle zend_long_to_v8js(zend_long, v8::Isolate *); /* Convert V8 value into zval */ int v8js_to_zval(v8::Handle, zval *, int, v8::Isolate * TSRMLS_DC); diff --git a/v8js_convert.cc b/v8js_convert.cc index 02c862a..ca04478 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2016 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ @@ -100,10 +100,19 @@ static v8::Handle v8js_hash_to_jsarr(zval *value, v8::Isolate *isolat } /* }}} */ -v8::Handle zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) /* {{{ */ +v8::Handle zend_long_to_v8js(zend_long v, v8::Isolate *isolate) /* {{{ */ +{ + if (v < - std::numeric_limits::min() || v > std::numeric_limits::max()) { + return V8JS_FLOAT(static_cast(v)); + } else { + return V8JS_INT(static_cast(v)); + } +} +/* }}} */ + +v8::Handle zval_to_v8js(zval *value, v8::Isolate *isolate) /* {{{ */ { v8::Handle jsValue; - zend_long v; zend_string *value_str; zend_class_entry *ce; @@ -147,12 +156,7 @@ v8::Handle zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) break; case IS_LONG: - v = Z_LVAL_P(value); - if (v < - std::numeric_limits::min() || v > std::numeric_limits::max()) { - jsValue = V8JS_FLOAT(static_cast(v)); - } else { - jsValue = V8JS_INT(static_cast(v)); - } + jsValue = zend_long_to_v8js(Z_LVAL_P(value), isolate); break; case IS_DOUBLE: From 7625d9b94fe177ea3767b95ce180f606c3969474 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 23:08:42 +0100 Subject: [PATCH 10/16] v8js_methods: add size checks + precission down casts --- v8js_methods.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/v8js_methods.cc b/v8js_methods.cc index 41994b7..4a905fd 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2013 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ @@ -18,6 +18,7 @@ #include "php_v8js_macros.h" #include "v8js_commonjs.h" +#include "v8js_exceptions.h" extern "C" { #include "zend_exceptions.h" @@ -42,15 +43,15 @@ V8JS_METHOD(sleep) /* {{{ */ V8JS_METHOD(print) /* {{{ */ { v8::Isolate *isolate = info.GetIsolate(); - int ret = 0; - V8JS_TSRMLS_FETCH(); + zend_long ret = 0; for (int i = 0; i < info.Length(); i++) { v8::String::Utf8Value str(info[i]); const char *cstr = ToCString(str); ret = PHPWRITE(cstr, strlen(cstr)); } - info.GetReturnValue().Set(V8JS_INT(ret)); + + info.GetReturnValue().Set(zend_long_to_v8js(ret, isolate)); } /* }}} */ @@ -433,7 +434,13 @@ V8JS_METHOD(require) // Set script identifier v8::Local sname = V8JS_STR(normalised_module_id); - v8::Local source = V8JS_ZSTR(Z_STR(module_code)); + if (Z_STRLEN(module_code) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Module code size exceeds maximum supported length", 0); + return; + } + + v8::Local source = V8JS_STRL(Z_STRVAL(module_code), static_cast(Z_STRLEN(module_code))); zval_ptr_dtor(&module_code); // Create and compile script From db7c81d4fa3af6fb36569d1b07a1cb6ed8f7a533 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 23:49:18 +0100 Subject: [PATCH 11/16] v8js_object_export: add size checks + precission down casts --- v8js_object_export.cc | 122 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 17 deletions(-) diff --git a/v8js_object_export.cc b/v8js_object_export.cc index 0b40295..63d6fed 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2016 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ @@ -18,6 +18,7 @@ #include "php_v8js_macros.h" #include "v8js_array_access.h" +#include "v8js_exceptions.h" #include "v8js_generator_export.h" #include "v8js_object_export.h" #include "v8js_v8object_class.h" @@ -42,7 +43,7 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v zval fname, retval; unsigned int argc = info.Length(), min_num_args = 0, max_num_args = 0; char *error; - int error_len; + zend_ulong error_len; unsigned int i; v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0); @@ -77,7 +78,14 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v (argc < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s", argc); - return_value = V8JS_THROW(isolate, TypeError, error, error_len); + if (error_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Generated error message length exceeds maximum supported length", 0); + } + else { + return_value = V8JS_THROW(isolate, TypeError, error, static_cast(error_len)); + } + if (object->ce == zend_ce_closure) { zend_string_release(method_ptr->internal_function.function_name); efree(method_ptr); @@ -100,7 +108,15 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v } else { if (v8js_to_zval(info[i], &fci.params[i], ctx->flags, isolate TSRMLS_CC) == FAILURE) { error_len = spprintf(&error, 0, "converting parameter #%d passed to %s() failed", i + 1, method_ptr->common.function_name); - return_value = V8JS_THROW(isolate, Error, error, error_len); + + if (error_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Generated error message length exceeds maximum supported length", 0); + } + else { + return_value = V8JS_THROW(isolate, Error, error, static_cast(error_len)); + } + efree(error); goto failure; } @@ -311,7 +327,7 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo(self->GetAlignedPointerFromInternalField(1)); ce = object->ce; @@ -340,11 +356,23 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo method_name = V8JS_ZSTR(method_ptr->common.function_name); + + v8::Local method_name; + // rename PHP special method names to JS equivalents. if (IS_MAGIC_FUNC(ZEND_TOSTRING_FUNC_NAME)) { method_name = V8JS_SYM("toString"); } + else { + if (ZSTR_LEN(method_ptr->common.function_name) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Method name length exceeds maximum supported length", 0); + return; + } + + method_name = V8JS_STRL(ZSTR_VAL(method_ptr->common.function_name), static_cast(ZSTR_LEN(method_ptr->common.function_name))); + } + result->Set(result_len++, method_name); } ZEND_HASH_FOREACH_END(); @@ -362,13 +390,20 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Object key length exceeds maximum supported length", 0); + continue; + } + // prefix enumerated property names with '$' so they can be // dereferenced unambiguously (ie, don't conflict with method // names) char *prefixed = static_cast(emalloc(ZSTR_LEN(key) + 2)); prefixed[0] = '$'; strncpy(prefixed + 1, ZSTR_VAL(key), ZSTR_LEN(key) + 1); - result->Set(result_len++, V8JS_STRL(prefixed, ZSTR_LEN(key) + 1)); + result->Set(result_len++, V8JS_STRL(prefixed, static_cast(ZSTR_LEN(key) + 1))); efree(prefixed); } else { // even numeric indices are enumerated as strings in JavaScript @@ -425,10 +460,10 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo& info) { v8::Isolate *isolate = info.GetIsolate(); v8::Local self = info.Holder(); - v8::Handle return_value; + v8::Handle return_value = V8JS_NULL; char *error; - int error_len; + size_t error_len; V8JS_TSRMLS_FETCH(); zend_class_entry *ce; @@ -440,16 +475,33 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo& info) error_len = spprintf(&error, 0, "%s::__call expects 2 parameters, %d given", ce->name, (int) info.Length()); - return_value = V8JS_THROW(isolate, TypeError, error, error_len); + + if (error_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Generated error message length exceeds maximum supported length", 0); + } + else { + return_value = V8JS_THROW(isolate, TypeError, error, static_cast(error_len)); + } + efree(error); info.GetReturnValue().Set(return_value); return; } + if (!info[1]->IsArray()) { error_len = spprintf(&error, 0, "%s::__call expects 2nd parameter to be an array", ce->name); - return_value = V8JS_THROW(isolate, TypeError, error, error_len); + + if (error_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Generated error message length exceeds maximum supported length", 0); + } + else { + return_value = V8JS_THROW(isolate, TypeError, error, static_cast(error_len)); + } + efree(error); info.GetReturnValue().Set(return_value); return; @@ -462,7 +514,15 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo& info) error_len = spprintf(&error, 0, "%s::__call expects fewer than a million arguments", ce->name); - return_value = V8JS_THROW(isolate, TypeError, error, error_len); + + if (error_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Generated error message length exceeds maximum supported length", 0); + } + else { + return_value = V8JS_THROW(isolate, TypeError, error, static_cast(error_len)); + } + efree(error); info.GetReturnValue().Set(return_value); return; @@ -484,7 +544,15 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo& info) error_len = spprintf(&error, 0, "%s::__call to %s method %s", ce->name, (method_ptr == NULL) ? "undefined" : "non-public", method_name); - return_value = V8JS_THROW(isolate, TypeError, error, error_len); + + if (error_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Generated error message length exceeds maximum supported length", 0); + } + else { + return_value = V8JS_THROW(isolate, TypeError, error, static_cast(error_len)); + } + efree(error); info.GetReturnValue().Set(return_value); return; @@ -759,7 +827,13 @@ static v8::Handle v8js_wrap_object(v8::Isolate *isolate, zend_class_ /* No cached v8::FunctionTemplate available as of yet, create one. */ new_tpl = v8::FunctionTemplate::New(isolate, 0); - new_tpl->SetClassName(V8JS_ZSTR(ce->name)); + if (ZSTR_LEN(ce->name) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Class name length exceeds maximum supported length", 0); + return v8::Local(); + } + + new_tpl->SetClassName(V8JS_STRL(ZSTR_VAL(ce->name), static_cast(ZSTR_LEN(ce->name)))); new_tpl->InstanceTemplate()->SetInternalFieldCount(2); if (ce == zend_ce_closure) { @@ -855,7 +929,7 @@ static v8::Handle v8js_wrap_array_to_object(v8::Isolate *isolate, zv { int i; zend_string *key; - ulong index; + zend_ulong index; v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0); v8::Local new_tpl; @@ -899,9 +973,23 @@ static v8::Handle v8js_wrap_array_to_object(v8::Isolate *isolate, zv } continue; } - newobj->Set(V8JS_ZSTR(key), zval_to_v8js(data, isolate TSRMLS_CC)); + + if (ZSTR_LEN(key) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Object key length exceeds maximum supported length", 0); + continue; + } + + newobj->Set(V8JS_STRL(ZSTR_VAL(key), static_cast(ZSTR_LEN(key))), + zval_to_v8js(data, isolate)); } else { - newobj->Set(index, zval_to_v8js(data, isolate TSRMLS_CC)); + if (index > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Array index exceeds maximum supported bound", 0); + continue; + } + + newobj->Set(static_cast(index), zval_to_v8js(data, isolate)); } if (tmp_ht) { From ab8613f41bc4566ac7079b4493e565880b5453cc Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Fri, 10 Mar 2017 23:53:11 +0100 Subject: [PATCH 12/16] v8js_v8: add size check + precission down cast --- v8js_v8.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/v8js_v8.cc b/v8js_v8.cc index 286c432..a4579b1 100644 --- a/v8js_v8.cc +++ b/v8js_v8.cc @@ -65,8 +65,15 @@ void v8js_v8_init(TSRMLS_D) /* {{{ */ /* Set V8 command line flags (must be done before V8::Initialize()!) */ if (v8js_process_globals.v8_flags) { - v8::V8::SetFlagsFromString(v8js_process_globals.v8_flags, - strlen(v8js_process_globals.v8_flags)); + size_t flags_len = strlen(v8js_process_globals.v8_flags); + + if (flags_len > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Length of V8 flags exceeds maximum supported length", 0); + } + else { + v8::V8::SetFlagsFromString(v8js_process_globals.v8_flags, static_cast(flags_len)); + } } /* Initialize V8 */ From 4a8c49bfed0f7116b4b17aae6149ae83bb9137e8 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 11 Mar 2017 00:09:51 +0100 Subject: [PATCH 13/16] v8js_v8object_class: add size check + precission down cast --- v8js_v8object_class.cc | 48 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/v8js_v8object_class.cc b/v8js_v8object_class.cc index 7518ff9..269cf99 100644 --- a/v8js_v8object_class.cc +++ b/v8js_v8object_class.cc @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2016 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ @@ -67,9 +67,14 @@ static int v8js_v8object_has_property(zval *object, zval *member, int has_set_ex if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject()) { + if (Z_STRLEN_P(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Member name length exceeds maximum supported length", 0); + return retval; + } v8::Local jsObj = v8obj->ToObject(); - v8::Local jsKey = V8JS_ZSTR(Z_STR_P(member)); + v8::Local jsKey = V8JS_STRL(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))); v8::Local jsVal; /* Skip any prototype properties */ @@ -126,9 +131,14 @@ static zval *v8js_v8object_read_property(zval *object, zval *member, int type, v if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject()) { + if (Z_STRLEN_P(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Member name length exceeds maximum supported length", 0); + return retval; + } v8::Local jsObj = v8obj->ToObject(); - v8::Local jsKey = V8JS_ZSTR(Z_STR_P(member)); + v8::Local jsKey = V8JS_STRL(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))); v8::Local jsVal; /* Skip any prototype properties */ @@ -158,8 +168,14 @@ static void v8js_v8object_write_property(zval *object, zval *member, zval *value V8JS_CTX_PROLOGUE(obj->ctx); v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); + if (Z_STRLEN_P(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Member name length exceeds maximum supported length", 0); + return; + } + if (v8obj->IsObject()) { - v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member)), zval_to_v8js(value, isolate TSRMLS_CC)); + v8obj->ToObject()->ForceSet(V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member))), zval_to_v8js(value, isolate)); } } /* }}} */ @@ -177,8 +193,14 @@ static void v8js_v8object_unset_property(zval *object, zval *member, void **cach V8JS_CTX_PROLOGUE(obj->ctx); v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); + if (Z_STRLEN_P(member) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Member name length exceeds maximum supported length", 0); + return; + } + if (v8obj->IsObject()) { - v8obj->ToObject()->Delete(V8JS_SYML(Z_STRVAL_P(member), Z_STRLEN_P(member))); + v8obj->ToObject()->Delete(V8JS_SYML(Z_STRVAL_P(member), static_cast(Z_STRLEN_P(member)))); } } /* }}} */ @@ -241,8 +263,14 @@ static zend_function *v8js_v8object_get_method(zend_object **object_ptr, zend_st return NULL; } + if (ZSTR_LEN(method) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Method name length exceeds maximum supported length", 0); + return NULL; + } + V8JS_CTX_PROLOGUE_EX(obj->ctx, NULL); - v8::Local jsKey = V8JS_ZSTR(method); + v8::Local jsKey = V8JS_STRL(ZSTR_VAL(method), static_cast(ZSTR_LEN(method))); v8::Local v8obj = v8::Local::New(isolate, obj->v8obj); if (!obj->v8obj.IsEmpty() && v8obj->IsObject() && !v8obj->IsFunction()) { @@ -277,6 +305,12 @@ static int v8js_v8object_call_method(zend_string *method, zend_object *object, I return FAILURE; } + if (ZSTR_LEN(method) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Method name length exceeds maximum supported length", 0); + return FAILURE; + } + if (argc > 0) { argv = (zval*)safe_emalloc(sizeof(zval), argc, 0); zend_get_parameters_array_ex(argc, argv); @@ -288,7 +322,7 @@ static int v8js_v8object_call_method(zend_string *method, zend_object *object, I std::function< v8::Local(v8::Isolate *) > v8_call = [obj, method, argc, argv, object, &return_value TSRMLS_CC](v8::Isolate *isolate) { int i = 0; - v8::Local method_name = V8JS_ZSYM(method); + v8::Local method_name = V8JS_SYML(ZSTR_VAL(method), static_cast(ZSTR_LEN(method))); v8::Local v8obj = v8::Local::New(isolate, obj->v8obj)->ToObject(); v8::Local thisObj; v8::Local cb; From cc888029c5afe7cb7de657c6286bfb464381bab0 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 11 Mar 2017 00:14:53 +0100 Subject: [PATCH 14/16] v8js_variable: add size check + precission down cast --- v8js_variables.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/v8js_variables.cc b/v8js_variables.cc index a0fa57c..f867494 100644 --- a/v8js_variables.cc +++ b/v8js_variables.cc @@ -2,17 +2,16 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2013 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ | Author: Jani Taskinen | | Author: Patrick Reilly | + | Author: Stefan Siegl | +----------------------------------------------------------------------+ */ -/* $Id:$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -20,6 +19,11 @@ #include #include "php_v8js_macros.h" +#include "v8js_exceptions.h" + +extern "C" { +#include "zend_exceptions.h" +} static void v8js_fetch_php_variable(v8::Local name, const v8::PropertyCallbackInfo& info) /* {{{ */ { @@ -66,13 +70,19 @@ void v8js_register_accessors(std::vector *accessor_list, v8: continue; /* Ignore invalid values */ } + if (ZSTR_LEN(property_name) > std::numeric_limits::max()) { + zend_throw_exception(php_ce_v8js_exception, + "Property name length exceeds maximum supported length", 0); + continue; + } + // Create context to store accessor data v8js_accessor_ctx *ctx = (v8js_accessor_ctx *)emalloc(sizeof(v8js_accessor_ctx)); ctx->variable_name = zend_string_copy(Z_STR_P(item)); ctx->isolate = isolate; /* Set the variable fetch callback for given symbol on named property */ - php_obj->SetAccessor(V8JS_ZSTR(property_name), v8js_fetch_php_variable, NULL, v8::External::New(isolate, ctx), v8::PROHIBITS_OVERWRITING, v8::ReadOnly, v8::AccessorSignature::New(isolate, php_obj_t)); + php_obj->SetAccessor(V8JS_STRL(ZSTR_VAL(property_name), static_cast(ZSTR_LEN(property_name))), v8js_fetch_php_variable, NULL, v8::External::New(isolate, ctx), v8::PROHIBITS_OVERWRITING, v8::ReadOnly, v8::AccessorSignature::New(isolate, php_obj_t)); /* record the context so we can free it later */ accessor_list->push_back(ctx); From d2c171a83f5ebab6c1d0ee62565435dc02948b91 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 11 Mar 2017 10:14:30 +0100 Subject: [PATCH 15/16] fix bound check: uint32_t -> int --- v8js_v8.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v8js_v8.cc b/v8js_v8.cc index a4579b1..2dd6387 100644 --- a/v8js_v8.cc +++ b/v8js_v8.cc @@ -67,7 +67,7 @@ void v8js_v8_init(TSRMLS_D) /* {{{ */ if (v8js_process_globals.v8_flags) { size_t flags_len = strlen(v8js_process_globals.v8_flags); - if (flags_len > std::numeric_limits::max()) { + if (flags_len > std::numeric_limits::max()) { zend_throw_exception(php_ce_v8js_exception, "Length of V8 flags exceeds maximum supported length", 0); } From 376165733548f3f7fdb842621e73cf741bc6c893 Mon Sep 17 00:00:00 2001 From: Stefan Siegl Date: Sat, 11 Mar 2017 10:17:10 +0100 Subject: [PATCH 16/16] update copyright years of changed files --- v8js.cc | 3 ++- v8js_array_access.cc | 4 ++-- v8js_class.cc | 2 +- v8js_methods.cc | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/v8js.cc b/v8js.cc index 0cba337..a61ddca 100644 --- a/v8js.cc +++ b/v8js.cc @@ -2,12 +2,13 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2013 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ | Author: Jani Taskinen | | Author: Patrick Reilly | + | Author: Stefan Siegl | +----------------------------------------------------------------------+ */ diff --git a/v8js_array_access.cc b/v8js_array_access.cc index 44bc024..6802db0 100644 --- a/v8js_array_access.cc +++ b/v8js_array_access.cc @@ -2,11 +2,11 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2016 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ - | Author: Stefan Siegl | + | Author: Stefan Siegl | +----------------------------------------------------------------------+ */ diff --git a/v8js_class.cc b/v8js_class.cc index 7f963ed..5761e78 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2016 The PHP Group | + | Copyright (c) 1997-2017 The PHP Group | +----------------------------------------------------------------------+ | http://www.opensource.org/licenses/mit-license.php MIT License | +----------------------------------------------------------------------+ diff --git a/v8js_methods.cc b/v8js_methods.cc index 4a905fd..8dbf375 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -8,7 +8,7 @@ +----------------------------------------------------------------------+ | Author: Jani Taskinen | | Author: Patrick Reilly | - | Author: Stefan Siegl | + | Author: Stefan Siegl | +----------------------------------------------------------------------+ */