Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/isolate.cc

Issue 2135593002: Enable visibility and security checks for builtin exit frames (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/isolate.h" 5 #include "src/isolate.h"
6 6
7 #include <stdlib.h> 7 #include <stdlib.h>
8 8
9 #include <fstream> // NOLINT(readability/streams) 9 #include <fstream> // NOLINT(readability/streams)
10 #include <sstream> 10 #include <sstream>
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 uint8_t buffer[kMaxStackTraceSize]; 305 uint8_t buffer[kMaxStackTraceSize];
306 int length = Min(kMaxStackTraceSize - 1, trace->length()); 306 int length = Min(kMaxStackTraceSize - 1, trace->length());
307 String::WriteToFlat(*trace, buffer, 0, length); 307 String::WriteToFlat(*trace, buffer, 0, length);
308 buffer[length] = '\0'; 308 buffer[length] = '\0';
309 // TODO(dcarney): convert buffer to utf8? 309 // TODO(dcarney): convert buffer to utf8?
310 base::OS::PrintError("Stacktrace (%x-%x) %p %p: %s\n", magic, magic2, ptr1, 310 base::OS::PrintError("Stacktrace (%x-%x) %p %p: %s\n", magic, magic2, ptr1,
311 ptr2, reinterpret_cast<char*>(buffer)); 311 ptr2, reinterpret_cast<char*>(buffer));
312 base::OS::Abort(); 312 base::OS::Abort();
313 } 313 }
314 314
315
316 // Determines whether the given stack frame should be displayed in
317 // a stack trace. The caller is the error constructor that asked
318 // for the stack trace to be collected. The first time a construct
319 // call to this function is encountered it is skipped. The seen_caller
320 // in/out parameter is used to remember if the caller has been seen
321 // yet.
322 static bool IsVisibleInStackTrace(JSFunction* fun,
323 Object* caller,
324 bool* seen_caller) {
325 if ((fun == caller) && !(*seen_caller)) {
326 *seen_caller = true;
327 return false;
328 }
329 // Skip all frames until we've seen the caller.
330 if (!(*seen_caller)) return false;
331 // Functions defined in native scripts are not visible unless directly
332 // exposed, in which case the native flag is set.
333 // The --builtins-in-stack-traces command line flag allows including
334 // internal call sites in the stack trace for debugging purposes.
335 if (!FLAG_builtins_in_stack_traces && fun->shared()->IsBuiltin()) {
336 return fun->shared()->native();
337 }
338 return true;
339 }
340
341 static Handle<FixedArray> MaybeGrow(Isolate* isolate, 315 static Handle<FixedArray> MaybeGrow(Isolate* isolate,
342 Handle<FixedArray> elements, 316 Handle<FixedArray> elements,
343 int cur_position, int new_size) { 317 int cur_position, int new_size) {
344 if (new_size > elements->length()) { 318 if (new_size > elements->length()) {
345 int new_capacity = JSObject::NewElementsCapacity(elements->length()); 319 int new_capacity = JSObject::NewElementsCapacity(elements->length());
346 Handle<FixedArray> new_elements = 320 Handle<FixedArray> new_elements =
347 isolate->factory()->NewFixedArrayWithHoles(new_capacity); 321 isolate->factory()->NewFixedArrayWithHoles(new_capacity);
348 for (int i = 0; i < cur_position; i++) { 322 for (int i = 0; i < cur_position; i++) {
349 new_elements->set(i, elements->get(i)); 323 new_elements->set(i, elements->get(i));
350 } 324 }
351 elements = new_elements; 325 elements = new_elements;
352 } 326 }
353 DCHECK(new_size <= elements->length()); 327 DCHECK(new_size <= elements->length());
354 return elements; 328 return elements;
355 } 329 }
356 330
331 class StackTraceHelper {
332 public:
333 StackTraceHelper(Isolate* isolate, Handle<Object> caller)
334 : isolate_(isolate), caller_(caller) {
335 // If the caller parameter is a function we skip frames until we're
336 // under it before starting to collect.
337 seen_caller_ = !caller->IsJSFunction();
338 encountered_strict_function_ = false;
339 sloppy_frames_ = 0;
340 }
341
342 // The stack trace API should not expose receivers and function
343 // objects on frames deeper than the top-most one with a strict mode
344 // function. The number of sloppy frames is stored as first element in
345 // the result array.
346 void CountSloppyFrames(JSFunction* fun) {
347 if (!encountered_strict_function_) {
348 if (is_strict(fun->shared()->language_mode())) {
349 encountered_strict_function_ = true;
350 } else {
351 sloppy_frames_++;
352 }
353 }
354 }
355
356 // Determines whether the given stack frame should be displayed in a stack
357 // trace.
358 bool IsVisibleInStackTrace(JSFunction* fun) {
359 return IsAfterCaller(fun) && IsNotInNativeScript(fun) &&
360 IsInSameSecurityContext(fun);
361 }
362
363 int sloppy_frames() const { return sloppy_frames_; }
364
365 private:
366 // The caller is the error constructor that asked
367 // for the stack trace to be collected. The first time a construct
368 // call to this function is encountered it is skipped. The seen_caller
369 // in/out parameter is used to remember if the caller has been seen
370 // yet.
371 bool IsAfterCaller(JSFunction* fun) {
372 if ((fun == *caller_) && !(seen_caller_)) {
373 seen_caller_ = true;
374 return false;
375 }
376 // Skip all frames until we've seen the caller.
377 if (!seen_caller_) return false;
378 return true;
379 }
380
381 bool IsNotInNativeScript(JSFunction* fun) {
382 // Functions defined in native scripts are not visible unless directly
383 // exposed, in which case the native flag is set.
384 // The --builtins-in-stack-traces command line flag allows including
385 // internal call sites in the stack trace for debugging purposes.
386 if (!FLAG_builtins_in_stack_traces && fun->shared()->IsBuiltin()) {
387 return fun->shared()->native();
388 }
389 return true;
390 }
391
392 bool IsInSameSecurityContext(JSFunction* fun) {
393 return isolate_->context()->HasSameSecurityTokenAs(fun->context());
394 }
395
396 Isolate* isolate_;
397 Handle<Object> caller_;
398
399 bool seen_caller_;
400 int sloppy_frames_;
401 bool encountered_strict_function_;
402 };
403
357 Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object, 404 Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
358 Handle<Object> caller) { 405 Handle<Object> caller) {
359 // Get stack trace limit. 406 // Get stack trace limit.
360 Handle<JSObject> error = error_function(); 407 Handle<JSObject> error = error_function();
361 Handle<String> stackTraceLimit = 408 Handle<String> stackTraceLimit =
362 factory()->InternalizeUtf8String("stackTraceLimit"); 409 factory()->InternalizeUtf8String("stackTraceLimit");
363 DCHECK(!stackTraceLimit.is_null()); 410 DCHECK(!stackTraceLimit.is_null());
364 Handle<Object> stack_trace_limit = 411 Handle<Object> stack_trace_limit =
365 JSReceiver::GetDataProperty(error, stackTraceLimit); 412 JSReceiver::GetDataProperty(error, stackTraceLimit);
366 if (!stack_trace_limit->IsNumber()) return factory()->undefined_value(); 413 if (!stack_trace_limit->IsNumber()) return factory()->undefined_value();
367 int limit = FastD2IChecked(stack_trace_limit->Number()); 414 int limit = FastD2IChecked(stack_trace_limit->Number());
368 limit = Max(limit, 0); // Ensure that limit is not negative. 415 limit = Max(limit, 0); // Ensure that limit is not negative.
369 416
370 int initial_size = Min(limit, 10); 417 int initial_size = Min(limit, 10);
371 Handle<FixedArray> elements = 418 Handle<FixedArray> elements =
372 factory()->NewFixedArrayWithHoles(initial_size * 4 + 1); 419 factory()->NewFixedArrayWithHoles(initial_size * 4 + 1);
373 420
374 // If the caller parameter is a function we skip frames until we're 421 StackTraceHelper helper(this, caller);
375 // under it before starting to collect. 422
376 bool seen_caller = !caller->IsJSFunction();
377 // First element is reserved to store the number of sloppy frames. 423 // First element is reserved to store the number of sloppy frames.
378 int cursor = 1; 424 int cursor = 1;
379 int frames_seen = 0; 425 int frames_seen = 0;
380 int sloppy_frames = 0;
381 bool encountered_strict_function = false;
382 for (StackFrameIterator iter(this); !iter.done() && frames_seen < limit; 426 for (StackFrameIterator iter(this); !iter.done() && frames_seen < limit;
383 iter.Advance()) { 427 iter.Advance()) {
384 StackFrame* frame = iter.frame(); 428 StackFrame* frame = iter.frame();
385 429
386 switch (frame->type()) { 430 switch (frame->type()) {
387 case StackFrame::JAVA_SCRIPT: 431 case StackFrame::JAVA_SCRIPT:
388 case StackFrame::OPTIMIZED: 432 case StackFrame::OPTIMIZED:
389 case StackFrame::INTERPRETED: 433 case StackFrame::INTERPRETED:
390 case StackFrame::BUILTIN: { 434 case StackFrame::BUILTIN: {
391 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame); 435 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
392 // Set initial size to the maximum inlining level + 1 for the outermost 436 // Set initial size to the maximum inlining level + 1 for the outermost
393 // function. 437 // function.
394 List<FrameSummary> frames(FLAG_max_inlining_levels + 1); 438 List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
395 js_frame->Summarize(&frames); 439 js_frame->Summarize(&frames);
396 for (int i = frames.length() - 1; i >= 0; i--) { 440 for (int i = frames.length() - 1; i >= 0; i--) {
397 Handle<JSFunction> fun = frames[i].function(); 441 Handle<JSFunction> fun = frames[i].function();
442
443 // Filter out internal frames that we do not want to show.
444 if (!helper.IsVisibleInStackTrace(*fun)) continue;
445 helper.CountSloppyFrames(*fun);
446
398 Handle<Object> recv = frames[i].receiver(); 447 Handle<Object> recv = frames[i].receiver();
399 // Filter out internal frames that we do not want to show. 448 Handle<AbstractCode> abstract_code = frames[i].abstract_code();
400 if (!IsVisibleInStackTrace(*fun, *caller, &seen_caller)) continue; 449 Handle<Smi> offset(Smi::FromInt(frames[i].code_offset()), this);
401 // Filter out frames from other security contexts. 450
402 if (!this->context()->HasSameSecurityTokenAs(fun->context())) {
403 continue;
404 }
405 elements = MaybeGrow(this, elements, cursor, cursor + 4); 451 elements = MaybeGrow(this, elements, cursor, cursor + 4);
406
407 Handle<AbstractCode> abstract_code = frames[i].abstract_code();
408
409 Handle<Smi> offset(Smi::FromInt(frames[i].code_offset()), this);
410 // The stack trace API should not expose receivers and function
411 // objects on frames deeper than the top-most one with a strict mode
412 // function. The number of sloppy frames is stored as first element in
413 // the result array.
414 if (!encountered_strict_function) {
415 if (is_strict(fun->shared()->language_mode())) {
416 encountered_strict_function = true;
417 } else {
418 sloppy_frames++;
419 }
420 }
421 elements->set(cursor++, *recv); 452 elements->set(cursor++, *recv);
422 elements->set(cursor++, *fun); 453 elements->set(cursor++, *fun);
423 elements->set(cursor++, *abstract_code); 454 elements->set(cursor++, *abstract_code);
424 elements->set(cursor++, *offset); 455 elements->set(cursor++, *offset);
425 frames_seen++; 456 frames_seen++;
426 } 457 }
427 } break; 458 } break;
428 459
429 case StackFrame::BUILTIN_EXIT: { 460 case StackFrame::BUILTIN_EXIT: {
430 BuiltinExitFrame* exit_frame = BuiltinExitFrame::cast(frame); 461 BuiltinExitFrame* exit_frame = BuiltinExitFrame::cast(frame);
431 Handle<JSFunction> fun = handle(exit_frame->function(), this); 462 Handle<JSFunction> fun = handle(exit_frame->function(), this);
463
464 // Filter out internal frames that we do not want to show.
465 if (!helper.IsVisibleInStackTrace(*fun)) continue;
466 helper.CountSloppyFrames(*fun);
467
432 Handle<Code> code = handle(exit_frame->LookupCode(), this); 468 Handle<Code> code = handle(exit_frame->LookupCode(), this);
433 int offset = 469 int offset =
434 static_cast<int>(exit_frame->pc() - code->instruction_start()); 470 static_cast<int>(exit_frame->pc() - code->instruction_start());
435 471
436 // In order to help CallSite::IsConstructor detect builtin constructors, 472 // In order to help CallSite::IsConstructor detect builtin constructors,
437 // we reuse the receiver field to pass along a special symbol. 473 // we reuse the receiver field to pass along a special symbol.
438 Handle<Object> recv; 474 Handle<Object> recv;
439 if (exit_frame->IsConstructor()) { 475 if (exit_frame->IsConstructor()) {
440 recv = handle(heap()->call_site_constructor_symbol(), this); 476 recv = handle(heap()->call_site_constructor_symbol(), this);
441 } else { 477 } else {
(...skipping 20 matching lines...) Expand all
462 elements->set(cursor++, Smi::FromInt(wasm_frame->function_index())); 498 elements->set(cursor++, Smi::FromInt(wasm_frame->function_index()));
463 elements->set(cursor++, *abstract_code); 499 elements->set(cursor++, *abstract_code);
464 elements->set(cursor++, Smi::FromInt(offset)); 500 elements->set(cursor++, Smi::FromInt(offset));
465 frames_seen++; 501 frames_seen++;
466 } break; 502 } break;
467 503
468 default: 504 default:
469 break; 505 break;
470 } 506 }
471 } 507 }
472 elements->set(0, Smi::FromInt(sloppy_frames)); 508 elements->set(0, Smi::FromInt(helper.sloppy_frames()));
473 elements->Shrink(cursor); 509 elements->Shrink(cursor);
474 Handle<JSArray> result = factory()->NewJSArrayWithElements(elements); 510 Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
475 result->set_length(Smi::FromInt(cursor)); 511 result->set_length(Smi::FromInt(cursor));
476 // TODO(yangguo): Queue this structured stack trace for preprocessing on GC. 512 // TODO(yangguo): Queue this structured stack trace for preprocessing on GC.
477 return result; 513 return result;
478 } 514 }
479 515
480 MaybeHandle<JSReceiver> Isolate::CaptureAndSetDetailedStackTrace( 516 MaybeHandle<JSReceiver> Isolate::CaptureAndSetDetailedStackTrace(
481 Handle<JSReceiver> error_object) { 517 Handle<JSReceiver> error_object) {
482 if (capture_stack_trace_for_uncaught_exceptions_) { 518 if (capture_stack_trace_for_uncaught_exceptions_) {
(...skipping 2604 matching lines...) Expand 10 before | Expand all | Expand 10 after
3087 // Then check whether this scope intercepts. 3123 // Then check whether this scope intercepts.
3088 if ((flag & intercept_mask_)) { 3124 if ((flag & intercept_mask_)) {
3089 intercepted_flags_ |= flag; 3125 intercepted_flags_ |= flag;
3090 return true; 3126 return true;
3091 } 3127 }
3092 return false; 3128 return false;
3093 } 3129 }
3094 3130
3095 } // namespace internal 3131 } // namespace internal
3096 } // namespace v8 3132 } // namespace v8
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698