Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include <stdlib.h>
6 :
7 : #include "test/cctest/cctest.h"
8 :
9 : namespace {
10 :
11 : int32_t g_cross_context_int = 0;
12 :
13 : bool g_expect_interceptor_call = false;
14 :
15 72 : void NamedGetter(v8::Local<v8::Name> property,
16 : const v8::PropertyCallbackInfo<v8::Value>& info) {
17 72 : CHECK(g_expect_interceptor_call);
18 : v8::Isolate* isolate = info.GetIsolate();
19 72 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
20 216 : if (property->Equals(context, v8_str("cross_context_int")).FromJust())
21 24 : info.GetReturnValue().Set(g_cross_context_int);
22 72 : }
23 :
24 24 : void NamedSetter(v8::Local<v8::Name> property, v8::Local<v8::Value> value,
25 : const v8::PropertyCallbackInfo<v8::Value>& info) {
26 24 : CHECK(g_expect_interceptor_call);
27 : v8::Isolate* isolate = info.GetIsolate();
28 24 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
29 72 : if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
30 0 : return;
31 24 : if (value->IsInt32()) {
32 48 : g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
33 : }
34 : info.GetReturnValue().Set(value);
35 : }
36 :
37 0 : void NamedQuery(v8::Local<v8::Name> property,
38 : const v8::PropertyCallbackInfo<v8::Integer>& info) {
39 0 : CHECK(g_expect_interceptor_call);
40 : v8::Isolate* isolate = info.GetIsolate();
41 0 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
42 0 : if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
43 0 : return;
44 : info.GetReturnValue().Set(v8::DontDelete);
45 : }
46 :
47 0 : void NamedDeleter(v8::Local<v8::Name> property,
48 : const v8::PropertyCallbackInfo<v8::Boolean>& info) {
49 0 : CHECK(g_expect_interceptor_call);
50 : v8::Isolate* isolate = info.GetIsolate();
51 0 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
52 0 : if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
53 0 : return;
54 : info.GetReturnValue().Set(false);
55 : }
56 :
57 24 : void NamedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
58 24 : CHECK(g_expect_interceptor_call);
59 : v8::Isolate* isolate = info.GetIsolate();
60 24 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
61 24 : v8::Local<v8::Array> names = v8::Array::New(isolate, 1);
62 72 : names->Set(context, 0, v8_str("cross_context_int")).FromJust();
63 : info.GetReturnValue().Set(names);
64 24 : }
65 :
66 48 : void IndexedGetter(uint32_t index,
67 : const v8::PropertyCallbackInfo<v8::Value>& info) {
68 48 : CHECK(g_expect_interceptor_call);
69 48 : if (index == 7) info.GetReturnValue().Set(g_cross_context_int);
70 48 : }
71 :
72 0 : void IndexedSetter(uint32_t index, v8::Local<v8::Value> value,
73 : const v8::PropertyCallbackInfo<v8::Value>& info) {
74 0 : CHECK(g_expect_interceptor_call);
75 : v8::Isolate* isolate = info.GetIsolate();
76 0 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
77 0 : if (index != 7) return;
78 0 : if (value->IsInt32()) {
79 0 : g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
80 : }
81 : info.GetReturnValue().Set(value);
82 : }
83 :
84 0 : void IndexedQuery(uint32_t index,
85 : const v8::PropertyCallbackInfo<v8::Integer>& info) {
86 0 : CHECK(g_expect_interceptor_call);
87 0 : if (index == 7) info.GetReturnValue().Set(v8::DontDelete);
88 0 : }
89 :
90 0 : void IndexedDeleter(uint32_t index,
91 : const v8::PropertyCallbackInfo<v8::Boolean>& info) {
92 0 : CHECK(g_expect_interceptor_call);
93 0 : if (index == 7) info.GetReturnValue().Set(false);
94 0 : }
95 :
96 24 : void IndexedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
97 24 : CHECK(g_expect_interceptor_call);
98 : v8::Isolate* isolate = info.GetIsolate();
99 24 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
100 24 : v8::Local<v8::Array> names = v8::Array::New(isolate, 1);
101 72 : names->Set(context, 0, v8_str("7")).FromJust();
102 : info.GetReturnValue().Set(names);
103 24 : }
104 :
105 6 : void MethodGetter(v8::Local<v8::Name> property,
106 : const v8::PropertyCallbackInfo<v8::Value>& info) {
107 : v8::Isolate* isolate = info.GetIsolate();
108 6 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
109 :
110 : v8::Local<v8::External> data = info.Data().As<v8::External>();
111 : v8::Local<v8::FunctionTemplate>& function_template =
112 6 : *reinterpret_cast<v8::Local<v8::FunctionTemplate>*>(data->Value());
113 :
114 : info.GetReturnValue().Set(
115 6 : function_template->GetFunction(context).ToLocalChecked());
116 6 : }
117 :
118 6 : void MethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
119 : info.GetReturnValue().Set(8);
120 6 : }
121 :
122 6 : void NamedGetterThrowsException(
123 : v8::Local<v8::Name> property,
124 : const v8::PropertyCallbackInfo<v8::Value>& info) {
125 12 : info.GetIsolate()->ThrowException(v8_str("exception"));
126 6 : }
127 :
128 6 : void NamedSetterThrowsException(
129 : v8::Local<v8::Name> property, v8::Local<v8::Value> value,
130 : const v8::PropertyCallbackInfo<v8::Value>& info) {
131 12 : info.GetIsolate()->ThrowException(v8_str("exception"));
132 6 : }
133 :
134 6 : void IndexedGetterThrowsException(
135 : uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
136 12 : info.GetIsolate()->ThrowException(v8_str("exception"));
137 6 : }
138 :
139 6 : void IndexedSetterThrowsException(
140 : uint32_t index, v8::Local<v8::Value> value,
141 : const v8::PropertyCallbackInfo<v8::Value>& info) {
142 12 : info.GetIsolate()->ThrowException(v8_str("exception"));
143 6 : }
144 :
145 132 : bool AccessCheck(v8::Local<v8::Context> accessing_context,
146 : v8::Local<v8::Object> accessed_object,
147 : v8::Local<v8::Value> data) {
148 132 : return false;
149 : }
150 :
151 24 : void GetCrossContextInt(v8::Local<v8::String> property,
152 : const v8::PropertyCallbackInfo<v8::Value>& info) {
153 24 : CHECK(!g_expect_interceptor_call);
154 24 : info.GetReturnValue().Set(g_cross_context_int);
155 24 : }
156 :
157 24 : void SetCrossContextInt(v8::Local<v8::String> property,
158 : v8::Local<v8::Value> value,
159 : const v8::PropertyCallbackInfo<void>& info) {
160 24 : CHECK(!g_expect_interceptor_call);
161 : v8::Isolate* isolate = info.GetIsolate();
162 24 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
163 24 : if (value->IsInt32()) {
164 48 : g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
165 : }
166 24 : }
167 :
168 24 : void Return42(v8::Local<v8::String> property,
169 : const v8::PropertyCallbackInfo<v8::Value>& info) {
170 : info.GetReturnValue().Set(42);
171 24 : }
172 :
173 24 : void CheckCanRunScriptInContext(v8::Isolate* isolate,
174 : v8::Local<v8::Context> context) {
175 24 : v8::HandleScope handle_scope(isolate);
176 : v8::Context::Scope context_scope(context);
177 :
178 24 : g_expect_interceptor_call = false;
179 24 : g_cross_context_int = 0;
180 :
181 : // Running script in this context should work.
182 24 : CompileRunChecked(isolate, "this.foo = 42; this[23] = true;");
183 24 : ExpectInt32("this.all_can_read", 42);
184 24 : CompileRunChecked(isolate, "this.cross_context_int = 23");
185 24 : CHECK_EQ(g_cross_context_int, 23);
186 48 : ExpectInt32("this.cross_context_int", 23);
187 24 : }
188 :
189 24 : void CheckCrossContextAccess(v8::Isolate* isolate,
190 : v8::Local<v8::Context> accessing_context,
191 : v8::Local<v8::Object> accessed_object) {
192 24 : v8::HandleScope handle_scope(isolate);
193 : accessing_context->Global()
194 72 : ->Set(accessing_context, v8_str("other"), accessed_object)
195 48 : .FromJust();
196 : v8::Context::Scope context_scope(accessing_context);
197 :
198 24 : g_expect_interceptor_call = true;
199 24 : g_cross_context_int = 23;
200 :
201 : {
202 24 : v8::TryCatch try_catch(isolate);
203 48 : CHECK(CompileRun(accessing_context, "this.other.foo").IsEmpty());
204 : }
205 : {
206 24 : v8::TryCatch try_catch(isolate);
207 48 : CHECK(CompileRun(accessing_context, "this.other[23]").IsEmpty());
208 : }
209 :
210 : // AllCanRead properties are also inaccessible.
211 : {
212 24 : v8::TryCatch try_catch(isolate);
213 48 : CHECK(CompileRun(accessing_context, "this.other.all_can_read").IsEmpty());
214 : }
215 :
216 : // Intercepted properties are accessible, however.
217 24 : ExpectInt32("this.other.cross_context_int", 23);
218 24 : CompileRunChecked(isolate, "this.other.cross_context_int = 42");
219 24 : ExpectInt32("this.other[7]", 42);
220 : ExpectString("JSON.stringify(Object.getOwnPropertyNames(this.other))",
221 48 : "[\"7\",\"cross_context_int\"]");
222 24 : }
223 :
224 6 : void CheckCrossContextAccessWithException(
225 : v8::Isolate* isolate, v8::Local<v8::Context> accessing_context,
226 : v8::Local<v8::Object> accessed_object) {
227 6 : v8::HandleScope handle_scope(isolate);
228 : accessing_context->Global()
229 18 : ->Set(accessing_context, v8_str("other"), accessed_object)
230 12 : .FromJust();
231 : v8::Context::Scope context_scope(accessing_context);
232 :
233 : {
234 6 : v8::TryCatch try_catch(isolate);
235 : CompileRun("this.other.should_throw");
236 6 : CHECK(try_catch.HasCaught());
237 12 : CHECK(try_catch.Exception()->IsString());
238 18 : CHECK(v8_str("exception")
239 : ->Equals(accessing_context, try_catch.Exception())
240 6 : .FromJust());
241 : }
242 :
243 : {
244 6 : v8::TryCatch try_catch(isolate);
245 : CompileRun("this.other.should_throw = 8");
246 6 : CHECK(try_catch.HasCaught());
247 12 : CHECK(try_catch.Exception()->IsString());
248 18 : CHECK(v8_str("exception")
249 : ->Equals(accessing_context, try_catch.Exception())
250 6 : .FromJust());
251 : }
252 :
253 : {
254 6 : v8::TryCatch try_catch(isolate);
255 : CompileRun("this.other[42]");
256 6 : CHECK(try_catch.HasCaught());
257 12 : CHECK(try_catch.Exception()->IsString());
258 18 : CHECK(v8_str("exception")
259 : ->Equals(accessing_context, try_catch.Exception())
260 6 : .FromJust());
261 : }
262 :
263 : {
264 6 : v8::TryCatch try_catch(isolate);
265 : CompileRun("this.other[42] = 8");
266 6 : CHECK(try_catch.HasCaught());
267 12 : CHECK(try_catch.Exception()->IsString());
268 18 : CHECK(v8_str("exception")
269 : ->Equals(accessing_context, try_catch.Exception())
270 6 : .FromJust());
271 6 : }
272 6 : }
273 :
274 0 : void Ctor(const v8::FunctionCallbackInfo<v8::Value>& info) {
275 0 : CHECK(info.IsConstructCall());
276 0 : }
277 :
278 : } // namespace
279 :
280 23724 : TEST(AccessCheckWithInterceptor) {
281 6 : v8::Isolate* isolate = CcTest::isolate();
282 6 : v8::HandleScope scope(isolate);
283 : v8::Local<v8::ObjectTemplate> global_template =
284 6 : v8::ObjectTemplate::New(isolate);
285 : global_template->SetAccessCheckCallbackAndHandler(
286 : AccessCheck,
287 : v8::NamedPropertyHandlerConfiguration(
288 : NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
289 : v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
290 : IndexedQuery, IndexedDeleter,
291 6 : IndexedEnumerator));
292 : global_template->SetNativeDataProperty(
293 6 : v8_str("cross_context_int"), GetCrossContextInt, SetCrossContextInt);
294 : global_template->SetNativeDataProperty(
295 : v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
296 6 : v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
297 :
298 : v8::Local<v8::Context> context0 =
299 6 : v8::Context::New(isolate, nullptr, global_template);
300 6 : CheckCanRunScriptInContext(isolate, context0);
301 :
302 : // Create another context.
303 : v8::Local<v8::Context> context1 =
304 6 : v8::Context::New(isolate, nullptr, global_template);
305 6 : CheckCrossContextAccess(isolate, context1, context0->Global());
306 6 : }
307 :
308 23724 : TEST(CallFunctionWithRemoteContextReceiver) {
309 6 : v8::Isolate* isolate = CcTest::isolate();
310 6 : v8::HandleScope scope(isolate);
311 : v8::Local<v8::FunctionTemplate> global_template =
312 6 : v8::FunctionTemplate::New(isolate);
313 :
314 : v8::Local<v8::Signature> signature =
315 6 : v8::Signature::New(isolate, global_template);
316 : v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(
317 : isolate, MethodCallback, v8::External::New(isolate, &function_template),
318 12 : signature);
319 :
320 : global_template->InstanceTemplate()->SetAccessCheckCallbackAndHandler(
321 : AccessCheck, v8::NamedPropertyHandlerConfiguration(
322 : MethodGetter, nullptr, nullptr, nullptr, nullptr,
323 : v8::External::New(isolate, &function_template)),
324 18 : v8::IndexedPropertyHandlerConfiguration());
325 :
326 : v8::Local<v8::Object> accessed_object =
327 : v8::Context::NewRemoteContext(isolate,
328 12 : global_template->InstanceTemplate())
329 6 : .ToLocalChecked();
330 : v8::Local<v8::Context> accessing_context =
331 12 : v8::Context::New(isolate, nullptr, global_template->InstanceTemplate());
332 :
333 12 : v8::HandleScope handle_scope(isolate);
334 : accessing_context->Global()
335 18 : ->Set(accessing_context, v8_str("other"), accessed_object)
336 12 : .FromJust();
337 : v8::Context::Scope context_scope(accessing_context);
338 :
339 : {
340 6 : v8::TryCatch try_catch(isolate);
341 6 : ExpectInt32("this.other.method()", 8);
342 6 : CHECK(!try_catch.HasCaught());
343 6 : }
344 6 : }
345 :
346 23724 : TEST(AccessCheckWithExceptionThrowingInterceptor) {
347 6 : v8::Isolate* isolate = CcTest::isolate();
348 : isolate->SetFailedAccessCheckCallbackFunction([](v8::Local<v8::Object> target,
349 : v8::AccessType type,
350 0 : v8::Local<v8::Value> data) {
351 0 : CHECK(false); // This should never be called.
352 6 : });
353 :
354 6 : v8::HandleScope scope(isolate);
355 : v8::Local<v8::ObjectTemplate> global_template =
356 6 : v8::ObjectTemplate::New(isolate);
357 : global_template->SetAccessCheckCallbackAndHandler(
358 : AccessCheck, v8::NamedPropertyHandlerConfiguration(
359 : NamedGetterThrowsException, NamedSetterThrowsException),
360 : v8::IndexedPropertyHandlerConfiguration(IndexedGetterThrowsException,
361 6 : IndexedSetterThrowsException));
362 :
363 : // Create two contexts.
364 : v8::Local<v8::Context> context0 =
365 6 : v8::Context::New(isolate, nullptr, global_template);
366 : v8::Local<v8::Context> context1 =
367 6 : v8::Context::New(isolate, nullptr, global_template);
368 :
369 6 : CheckCrossContextAccessWithException(isolate, context1, context0->Global());
370 6 : }
371 :
372 23724 : TEST(NewRemoteContext) {
373 6 : v8::Isolate* isolate = CcTest::isolate();
374 6 : v8::HandleScope scope(isolate);
375 : v8::Local<v8::ObjectTemplate> global_template =
376 6 : v8::ObjectTemplate::New(isolate);
377 : global_template->SetAccessCheckCallbackAndHandler(
378 : AccessCheck,
379 : v8::NamedPropertyHandlerConfiguration(
380 : NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
381 : v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
382 : IndexedQuery, IndexedDeleter,
383 6 : IndexedEnumerator));
384 : global_template->SetNativeDataProperty(
385 6 : v8_str("cross_context_int"), GetCrossContextInt, SetCrossContextInt);
386 : global_template->SetNativeDataProperty(
387 : v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
388 6 : v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
389 :
390 : v8::Local<v8::Object> global0 =
391 12 : v8::Context::NewRemoteContext(isolate, global_template).ToLocalChecked();
392 :
393 : // Create a real context.
394 : {
395 6 : v8::HandleScope other_scope(isolate);
396 : v8::Local<v8::Context> context1 =
397 6 : v8::Context::New(isolate, nullptr, global_template);
398 :
399 6 : CheckCrossContextAccess(isolate, context1, global0);
400 : }
401 :
402 : // Create a context using the detached global.
403 : {
404 6 : v8::HandleScope other_scope(isolate);
405 : v8::Local<v8::Context> context2 =
406 6 : v8::Context::New(isolate, nullptr, global_template, global0);
407 :
408 6 : CheckCanRunScriptInContext(isolate, context2);
409 : }
410 :
411 : // Turn a regular context into a remote context.
412 : {
413 6 : v8::HandleScope other_scope(isolate);
414 : v8::Local<v8::Context> context3 =
415 6 : v8::Context::New(isolate, nullptr, global_template);
416 :
417 6 : CheckCanRunScriptInContext(isolate, context3);
418 :
419 : // Turn the global object into a remote context, and try to access it.
420 6 : v8::Local<v8::Object> context3_global = context3->Global();
421 6 : context3->DetachGlobal();
422 : v8::Local<v8::Object> global3 =
423 6 : v8::Context::NewRemoteContext(isolate, global_template, context3_global)
424 6 : .ToLocalChecked();
425 : v8::Local<v8::Context> context4 =
426 6 : v8::Context::New(isolate, nullptr, global_template);
427 :
428 6 : CheckCrossContextAccess(isolate, context4, global3);
429 :
430 : // Turn it back into a regular context.
431 : v8::Local<v8::Context> context5 =
432 6 : v8::Context::New(isolate, nullptr, global_template, global3);
433 :
434 6 : CheckCanRunScriptInContext(isolate, context5);
435 6 : }
436 6 : }
437 :
438 23724 : TEST(NewRemoteInstance) {
439 6 : v8::Isolate* isolate = CcTest::isolate();
440 6 : v8::HandleScope scope(isolate);
441 : v8::Local<v8::FunctionTemplate> tmpl =
442 6 : v8::FunctionTemplate::New(isolate, Ctor);
443 6 : v8::Local<v8::ObjectTemplate> instance = tmpl->InstanceTemplate();
444 : instance->SetAccessCheckCallbackAndHandler(
445 : AccessCheck,
446 : v8::NamedPropertyHandlerConfiguration(
447 : NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
448 : v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
449 : IndexedQuery, IndexedDeleter,
450 6 : IndexedEnumerator));
451 : tmpl->SetNativeDataProperty(
452 : v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
453 6 : v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
454 :
455 12 : v8::Local<v8::Object> obj = tmpl->NewRemoteInstance().ToLocalChecked();
456 :
457 6 : v8::Local<v8::Context> context = v8::Context::New(isolate);
458 6 : CheckCrossContextAccess(isolate, context, obj);
459 71160 : }
|