Skip to content
This repository was archived by the owner on Mar 29, 2024. It is now read-only.

Commit 2090716

Browse files
committed
Avoid segfault from v8 on snapshot version mismatch, closes #73
1 parent 5d97af2 commit 2090716

15 files changed

+224
-45
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ tests/*
33
!tests/.testsuite.php
44
!tests/.v8-helpers.php
55
!tests/.tracking_dtors.php
6+
!tests/stubs
67

78
.deps
89
*.lo

Diff for: src/php_v8_isolate.cc

+12-3
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,18 @@ static PHP_METHOD(Isolate, __construct) {
200200

201201
if (snapshot_zv != NULL) {
202202
PHP_V8_STARTUP_DATA_FETCH_INTO(snapshot_zv, php_v8_startup_data);
203-
if (php_v8_startup_data->blob && php_v8_startup_data->blob->hasData()) {
204-
php_v8_isolate->blob = php_v8_startup_data->blob;
205-
php_v8_isolate->create_params->snapshot_blob = php_v8_isolate->blob->acquire();
203+
204+
if (php_v8_startup_data->blob && php_v8_startup_data->blob->hasData() && !php_v8_startup_data->blob->rejected()) {
205+
206+
script_compiler_tag runtime = php_v8_startup_data_get_current_tag();
207+
script_compiler_tag version = php_v8_startup_data->blob->version();
208+
209+
if (runtime.magic == version.magic && runtime.tag == version.tag) {
210+
php_v8_isolate->blob = php_v8_startup_data->blob;
211+
php_v8_isolate->create_params->snapshot_blob = php_v8_isolate->blob->acquire();
212+
} else {
213+
php_v8_startup_data->blob->reject();
214+
}
206215
}
207216
}
208217

Diff for: src/php_v8_script_compiler.cc

+16-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
#include "php_v8_string.h"
2525
#include "php_v8_value.h"
2626
#include "php_v8_isolate.h"
27+
#include "php_v8_a.h"
2728
#include "php_v8.h"
29+
#include "zend_smart_str.h"
2830

2931
zend_class_entry* php_v8_script_compiler_class_entry;
3032
#define this_ce php_v8_script_compiler_class_entry
@@ -59,13 +61,17 @@ static v8::ScriptCompiler::Source * php_v8_build_source(zval *source_string_zv,
5961
return source;
6062
}
6163

62-
static PHP_METHOD(ScriptCompiler, cachedDataVersionTag)
64+
static PHP_METHOD(ScriptCompiler, getCachedDataVersionTag)
6365
{
6466
if (zend_parse_parameters_none() == FAILURE) {
6567
return;
6668
}
6769

68-
RETURN_LONG(static_cast<zend_long>(v8::ScriptCompiler::CachedDataVersionTag()));
70+
php_v8_init();
71+
script_compiler_tag version = php_v8_startup_data_get_current_tag();
72+
uint64_t tag = (uint64_t) version.magic << (sizeof(version.tag) * 8) | version.tag;
73+
74+
RETURN_DOUBLE(tag);
6975
}
7076

7177
static PHP_METHOD(ScriptCompiler, compileUnboundScript)
@@ -80,6 +86,8 @@ static PHP_METHOD(ScriptCompiler, compileUnboundScript)
8086
return;
8187
}
8288

89+
php_v8_init();
90+
8391
PHP_V8_CHECK_COMPILER_OPTIONS_RANGE(options, "Invalid Script compiler options given. See V8\\ScriptCompiler OPTION_* class constants for available options.")
8492

8593
PHP_V8_CONTEXT_FETCH_WITH_CHECK(php_v8_context_zv, php_v8_context);
@@ -136,6 +144,8 @@ static PHP_METHOD(ScriptCompiler, compile)
136144
return;
137145
}
138146

147+
php_v8_init();
148+
139149
PHP_V8_CHECK_COMPILER_OPTIONS_RANGE(options, "Invalid Script compiler options given. See V8\\ScriptCompiler OPTION_* class constants for available options.")
140150

141151
PHP_V8_CONTEXT_FETCH_WITH_CHECK(php_v8_context_zv, php_v8_context);
@@ -198,6 +208,8 @@ static PHP_METHOD(ScriptCompiler, compileFunctionInContext)
198208
return;
199209
}
200210

211+
php_v8_init();
212+
201213
PHP_V8_CONTEXT_FETCH_WITH_CHECK(php_v8_context_zv, php_v8_context);
202214

203215
zval *source_string_zv = PHP_V8_SOURCE_READ_SOURCE_STRING(php_v8_source_zv);
@@ -252,7 +264,7 @@ static PHP_METHOD(ScriptCompiler, compileFunctionInContext)
252264
}
253265

254266

255-
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_cachedDataVersionTag, ZEND_RETURN_VALUE, 0, IS_LONG, 0)
267+
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_getCachedDataVersionTag, ZEND_RETURN_VALUE, 0, IS_DOUBLE, 0)
256268
ZEND_END_ARG_INFO()
257269

258270
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_compileUnboundScript, ZEND_RETURN_VALUE, 2, V8\\UnboundScript, 0)
@@ -276,7 +288,7 @@ ZEND_END_ARG_INFO()
276288

277289

278290
static const zend_function_entry php_v8_script_compiler_methods[] = {
279-
PHP_V8_ME(ScriptCompiler, cachedDataVersionTag, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
291+
PHP_V8_ME(ScriptCompiler, getCachedDataVersionTag, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
280292
PHP_V8_ME(ScriptCompiler, compileUnboundScript, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
281293
PHP_V8_ME(ScriptCompiler, compile, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
282294
PHP_V8_ME(ScriptCompiler, compileFunctionInContext, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)

Diff for: src/php_v8_script_compiler.h

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef PHP_V8_SCRIPT_COMPILER_H
1414
#define PHP_V8_SCRIPT_COMPILER_H
1515

16+
1617
extern "C" {
1718
#include "php.h"
1819

@@ -21,6 +22,8 @@ extern "C" {
2122
#endif
2223
}
2324

25+
#include "v8.h"
26+
2427
extern zend_class_entry *php_v8_script_compiler_class_entry;
2528

2629
#define PHP_V8_CHECK_COMPILER_OPTIONS_RANGE(options, message) \

Diff for: src/php_v8_startup_data.cc

+43-16
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,36 @@
1616

1717
#include "php_v8_startup_data.h"
1818
#include "php_v8_exceptions.h"
19+
#include "php_v8_script_compiler.h"
1920
#include "php_v8_a.h"
2021
#include "php_v8.h"
22+
#include "zend_smart_str.h"
2123

2224

2325
zend_class_entry *php_v8_startup_data_class_entry;
2426
#define this_ce php_v8_startup_data_class_entry
2527

2628
static zend_object_handlers php_v8_startup_data_object_handlers;
2729

30+
extern script_compiler_tag php_v8_startup_data_get_current_tag() {
31+
script_compiler_tag tag = {PHP_V8_SCRIPT_COMPILER_TAG_MAGIC, v8::ScriptCompiler::CachedDataVersionTag()};
32+
return tag;
33+
}
2834

2935
void php_v8_startup_data_create(zval *return_value, v8::StartupData *blob) {
3036
object_init_ex(return_value, this_ce);
3137

3238
PHP_V8_STARTUP_DATA_FETCH_INTO(return_value, php_v8_startup_data);
3339

34-
php_v8_startup_data->blob = new phpv8::StartupData(blob);
40+
script_compiler_tag version = php_v8_startup_data_get_current_tag();
41+
42+
const char *blob_data = blob->data;
43+
44+
blob->data = (const char *) estrndup(blob->data, blob->raw_size);
45+
46+
delete []blob_data;
47+
48+
php_v8_startup_data->blob = new phpv8::StartupData(blob, version);
3549
}
3650

3751
static void php_v8_startup_data_free(zend_object *object) {
@@ -68,22 +82,27 @@ static PHP_METHOD(StartupData, __construct) {
6882
return;
6983
}
7084

71-
if (ZSTR_LEN(blob_string) > INT_MAX) {
72-
PHP_V8_THROW_EXCEPTION("Failed to create startup blob due to blob size integer overflow");
85+
if (ZSTR_LEN(blob_string) > INT_MAX + sizeof(script_compiler_tag)) {
86+
PHP_V8_THROW_EXCEPTION("Invalid startup blob (too large)");
7387
return;
7488
}
7589

76-
if (!ZSTR_LEN(blob_string)) {
90+
if (ZSTR_LEN(blob_string) < sizeof(script_compiler_tag)) {
91+
PHP_V8_THROW_EXCEPTION("Invalid startup blob (too small)");
7792
return;
7893
}
7994

95+
script_compiler_tag version = {};
96+
memcpy(&version, &ZSTR_VAL(blob_string)[0], sizeof(script_compiler_tag));
97+
8098
PHP_V8_STARTUP_DATA_FETCH_INTO(getThis(), php_v8_startup_data);
8199

82100
v8::StartupData *blob = new v8::StartupData();
83-
blob->data = (const char *) estrndup(ZSTR_VAL(blob_string), ZSTR_LEN(blob_string));
84-
blob->raw_size = static_cast<int>(ZSTR_LEN(blob_string));
85101

86-
php_v8_startup_data->blob = new phpv8::StartupData(blob);
102+
blob->data = (const char *) estrndup(&ZSTR_VAL(blob_string)[sizeof(script_compiler_tag)], ZSTR_LEN(blob_string) - sizeof(script_compiler_tag));
103+
blob->raw_size = static_cast<int>(ZSTR_LEN(blob_string) - sizeof(script_compiler_tag));
104+
105+
php_v8_startup_data->blob = new phpv8::StartupData(blob, version);
87106
}
88107

89108
static PHP_METHOD(StartupData, getData) {
@@ -94,26 +113,34 @@ static PHP_METHOD(StartupData, getData) {
94113
PHP_V8_STARTUP_DATA_FETCH_INTO(getThis(), php_v8_startup_data);
95114

96115
if (php_v8_startup_data->blob && php_v8_startup_data->blob->hasData()) {
97-
zend_string *out = zend_string_init(php_v8_startup_data->blob->data()->data, static_cast<size_t>(php_v8_startup_data->blob->data()->raw_size), 0);
116+
script_compiler_tag loaded = php_v8_startup_data->blob->version();
117+
smart_str my_str = {0};
118+
119+
smart_str_appendl(&my_str, (char *)&loaded, sizeof(script_compiler_tag));
120+
smart_str_appendl(&my_str, php_v8_startup_data->blob->data()->data, static_cast<size_t>(php_v8_startup_data->blob->data()->raw_size));
121+
smart_str_0(&my_str);
122+
123+
zend_string *out = zend_string_copy(my_str.s);
124+
smart_str_free(&my_str);
125+
98126
RETURN_STR(out);
99127
}
100128

101129
RETURN_EMPTY_STRING();
102130
}
103131

104-
static PHP_METHOD(StartupData, getRawSize) {
132+
static PHP_METHOD(StartupData, isRejected) {
105133
if (zend_parse_parameters_none() == FAILURE) {
106134
return;
107135
}
108136

109137
PHP_V8_STARTUP_DATA_FETCH_INTO(getThis(), php_v8_startup_data);
110138

111-
if (php_v8_startup_data->blob && php_v8_startup_data->blob->hasData()) {
112-
RETVAL_LONG(static_cast<zend_long>(php_v8_startup_data->blob->data()->raw_size));
113-
return;
139+
if (php_v8_startup_data->blob) {
140+
RETURN_BOOL(php_v8_startup_data->blob->rejected());
114141
}
115142

116-
RETURN_LONG(0);
143+
RETURN_BOOL(false);
117144
}
118145

119146
static PHP_METHOD(StartupData, createFromSource) {
@@ -128,7 +155,7 @@ static PHP_METHOD(StartupData, createFromSource) {
128155
const char *source = ZSTR_VAL(blob);
129156
php_v8_init();
130157

131-
v8::StartupData *startup_blob = new v8::StartupData;
158+
v8::StartupData *startup_blob = new v8::StartupData();
132159

133160
*startup_blob = v8::V8::CreateSnapshotDataBlob(source);
134161

@@ -175,7 +202,7 @@ ZEND_END_ARG_INFO()
175202
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_getData, ZEND_RETURN_VALUE, 0, IS_STRING, 0)
176203
ZEND_END_ARG_INFO()
177204

178-
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_getRawSize, ZEND_RETURN_VALUE, 0, IS_LONG, 0)
205+
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_isRejected, ZEND_RETURN_VALUE, 0, _IS_BOOL, 0)
179206
ZEND_END_ARG_INFO()
180207

181208
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_createFromSource, ZEND_RETURN_VALUE, 1, V8\\StartupData, 0)
@@ -191,7 +218,7 @@ ZEND_END_ARG_INFO()
191218
static const zend_function_entry php_v8_startup_data_methods[] = {
192219
PHP_V8_ME(StartupData, __construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
193220
PHP_V8_ME(StartupData, getData, ZEND_ACC_PUBLIC)
194-
PHP_V8_ME(StartupData, getRawSize, ZEND_ACC_PUBLIC)
221+
PHP_V8_ME(StartupData, isRejected, ZEND_ACC_PUBLIC)
195222
PHP_V8_ME(StartupData, createFromSource, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
196223
PHP_V8_ME(StartupData, warmUpSnapshotDataBlob, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
197224

Diff for: src/php_v8_startup_data.h

+26-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define PHP_V8_STARTUP_DATA_H
1515

1616
typedef struct _php_v8_startup_data_t php_v8_startup_data_t;
17+
typedef struct _script_compiler_tag script_compiler_tag;
1718

1819
namespace phpv8 {
1920
class StartupData;
@@ -33,15 +34,22 @@ extern "C" {
3334
extern zend_class_entry* php_v8_startup_data_class_entry;
3435

3536
inline php_v8_startup_data_t * php_v8_startup_data_fetch_object(zend_object *obj);
37+
extern script_compiler_tag php_v8_startup_data_get_current_tag();
3638

3739
#define PHP_V8_STARTUP_DATA_FETCH(zv) php_v8_startup_data_fetch_object(Z_OBJ_P(zv))
3840
#define PHP_V8_STARTUP_DATA_FETCH_INTO(pzval, into) php_v8_startup_data_t *(into) = PHP_V8_STARTUP_DATA_FETCH((pzval))
3941

42+
#define PHP_V8_SCRIPT_COMPILER_TAG_MAGIC 1 // increment it whenever you change sth that breaks blobs
43+
44+
struct _script_compiler_tag {
45+
uint32_t magic;
46+
uint32_t tag;
47+
};
4048

4149
namespace phpv8 {
4250
class StartupData {
4351
public:
44-
StartupData(v8::StartupData *data = nullptr) : _data(data), in_use(1) {}
52+
StartupData(v8::StartupData *data, script_compiler_tag version) : _data(data), in_use(1), _version(version), _rejected(false) {}
4553

4654
inline v8::StartupData *acquire() {
4755
assert(in_use < UINT32_MAX);
@@ -62,8 +70,23 @@ namespace phpv8 {
6270
return --in_use == 0;
6371
}
6472

73+
void reject() {
74+
_rejected = true;
75+
}
76+
77+
bool rejected() {
78+
return _rejected;
79+
}
80+
81+
script_compiler_tag version() {
82+
return _version;
83+
}
84+
6585
~StartupData() {
6686
if (_data) {
87+
efree((void*)_data->data);
88+
_data->data = nullptr;
89+
_data->raw_size = 0;
6790
delete _data;
6891
}
6992
}
@@ -72,6 +95,8 @@ namespace phpv8 {
7295
private:
7396
v8::StartupData *_data;
7497
uint32_t in_use;
98+
script_compiler_tag _version;
99+
bool _rejected;
75100
};
76101
}
77102

Diff for: stubs/src/ScriptCompiler.php

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
11
<?php declare(strict_types=1);
22

3+
/**
4+
* This file is part of the pinepain/php-v8 PHP extension.
5+
*
6+
* Copyright (c) 2015-2017 Bogdan Padalko <[email protected]>
7+
*
8+
* Licensed under the MIT license: http://opensource.org/licenses/MIT
9+
*
10+
* For the full copyright and license information, please view the
11+
* LICENSE file that was distributed with this source or visit
12+
* http://opensource.org/licenses/MIT
13+
*/
14+
315

416
namespace V8;
517

6-
use V8\ScriptCompiler\CompileOptions;
18+
719
use V8\ScriptCompiler\Source;
820

921

1022
/**
1123
* For compiling scripts.
1224
*/
13-
class ScriptCompiler
25+
class ScriptCompiler
1426
{
15-
const OPTION_NO_COMPILE_OPTIONS = 0;
16-
const OPTION_PRODUCE_PARSER_CACHE = 1;
17-
const OPTION_CONSUME_PARSER_CACHE = 2;
18-
const OPTION_PRODUCE_CODE_CACHE = 3;
27+
const OPTION_NO_COMPILE_OPTIONS = 0;
28+
const OPTION_PRODUCE_PARSER_CACHE = 1;
29+
const OPTION_CONSUME_PARSER_CACHE = 2;
30+
const OPTION_PRODUCE_CODE_CACHE = 3;
1931
const OPTION_PRODUCE_FULL_CODE_CACHE = 4;
20-
const OPTION_CONSUME_CODE_CACHE = 5;
32+
const OPTION_CONSUME_CODE_CACHE = 5;
2133

2234
private function __construct()
2335
{
@@ -41,9 +53,9 @@ private function __construct()
4153
* Alternatively, this tag can be stored alongside the cached data and
4254
* compared when it is being used.
4355
*
44-
* @return int
56+
* @return float
4557
*/
46-
public static function cachedDataVersionTag(): int
58+
public static function getCachedDataVersionTag(): float
4759
{
4860
}
4961

Diff for: stubs/src/StartupData.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function getData(): string
2929
{
3030
}
3131

32-
public function getRawSize(): int
32+
public function isRejected(): bool
3333
{
3434
}
3535

0 commit comments

Comments
 (0)