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