Commit message: v8: Interrupts must not mask stack overflow.

Backport of https://codereview.chromium.org/339883002
CVE: CVE-2014-5256
CWE: CWE-20
Vulnerability description: Node.js 0.8 before 0.8.28 and 0.10 before 0.10.30 does not consider the possibility of recursive processing that triggers V8 garbage collection in conjunction with a V8 interrupt, which allows remote attackers to cause a denial of service (memory corruption and application crash) via deep JSON objects whose parsing lets this interrupt mask an overflow of the program stack.

Project: node
File: isolate.h

  INLINE(static Isolate* UncheckedCurrent()) {
    return reinterpret_cast<Isolate*>(Thread::GetThreadLocal(isolate_key_));
  }

Project: node
File: isolate.h

  void set_external_caught_exception(bool value) {
    thread_local_top_.external_caught_exception_ = value;
  }

Project: node
File: isolate.h

  static Address js_entry_sp(ThreadLocalTop* thread) {
    return thread->js_entry_sp_;
  }

Project: node
File: isolate.h

  void Free() {
    ASSERT(!has_pending_message_);
    ASSERT(!external_caught_exception_);
    ASSERT(try_catch_handler_address_ == NULL);
  }

Project: node
File: isolate.h

  void set_ignore_out_of_memory(bool value) {
    thread_local_top_.ignore_out_of_memory_ = value;
  }

Project: node
File: isolate.h

  Handle<Context> context() { return context_; }

Project: node
File: isolate.h

  void SetCurrentVMState(StateTag state) {
    if (RuntimeProfiler::IsEnabled()) {
      // Make sure thread local top is initialized.
      ASSERT(thread_local_top_.isolate_ == this);
      StateTag current_state = thread_local_top_.current_vm_state_;
      if (current_state != JS && state == JS) {
        // Non-JS -> JS transition.
        RuntimeProfiler::IsolateEnteredJS(this);
      } else if (current_state == JS && state != JS) {
        // JS -> non-JS transition.
        ASSERT(RuntimeProfiler::IsSomeIsolateInJS());
        RuntimeProfiler::IsolateExitedJS(this);
      } else {
        // Other types of state transitions are not interesting to the
        // runtime profiler, because they don't affect whether we're
        // in JS or not.
        ASSERT((current_state == JS) == (state == JS));
      }
    }
    thread_local_top_.current_vm_state_ = state;
  }

Project: node
File: isolate.h

  void clear_pending_exception() {
    thread_local_top_.pending_exception_ = heap_.the_hole_value();
  }

Project: node
File: isolate.h

  explicit ThreadId(int id) : id_(id) {}

Project: node
File: isolate.h

  StackGuard* stack_guard() { return &stack_guard_; }

Project: node
File: isolate.h

  inline Address* handler_address() { return &thread_local_top_.handler_; }

Project: node
File: isolate.h

  unibrow::Mapping<unibrow::CanonicalizationRange>* jsregexp_canonrange() {
    return &jsregexp_canonrange_;
  }

Project: node
File: isolate.h

  ~ExecutionAccess() { Unlock(isolate_); }

Project: node
File: isolate.h

    void set_stack_limit(uintptr_t value) { stack_limit_ = value; }

Project: node
File: isolate.h

  void set_simulator_redirection(Redirection* redirection) {
    simulator_redirection_ = redirection;
  }

Project: node
File: isolate.h

  inline bool HasOverflowed() const {
    StackGuard* stack_guard = isolate_->stack_guard();
    return reinterpret_cast<uintptr_t>(this) < stack_guard->real_climit();
  }

Project: node
File: isolate.h

  void set_context(Context* context) {
    ASSERT(context == NULL || context->IsContext());
    thread_local_top_.context_ = context;
  }

Project: node
File: isolate.h

  static int ArchiveSpacePerThread() { return sizeof(ThreadLocalTop); }

Project: node
File: isolate.h

  StaticResource<SafeStringInputBuffer>* compiler_safe_string_input_buffer() {
    return &compiler_safe_string_input_buffer_;
  }

Project: node
File: isolate.h

  void set_external_callback(Address callback) {
    thread_local_top_.external_callback_ = callback;
  }

Project: node
File: isolate.h

  Mutex* debugger_access() { return debugger_access_; }

Project: node
File: isolate.h

  OptimizingCompilerThread* optimizing_compiler_thread() {
    return &optimizing_compiler_thread_;
  }

Project: node
File: isolate.h

  ThreadId thread_id() { return thread_local_top_.thread_id_; }

Project: node
File: isolate.h

  MemoryAllocator* memory_allocator() {
    return memory_allocator_;
  }

Project: node
File: isolate.h

  ~PostponeInterruptsScope() {
    if (--stack_guard_->thread_local_.postpone_interrupts_nesting_ == 0) {
      stack_guard_->EnableInterrupts();
    }
  }

Project: node
File: isolate.h

  Context* context() { return thread_local_top_.context_; }

Project: node
File: isolate.h

  v8::TryCatch* catcher() {
    return thread_local_top_.catcher_;
  }

Project: node
File: isolate.h

  Mutex* break_access() { return break_access_; }

Project: node
File: isolate.h

  Address has_pending_message_address() {
    return reinterpret_cast<Address>(&thread_local_top_.has_pending_message_);
  }

Project: node
File: isolate.h

  INLINE(bool Equals(const ThreadId& other) const) {
    return id_ == other.id_;
  }

Project: node
File: isolate.h

  Handle<GlobalObject> global_object() {
    return Handle<GlobalObject>(context()->global_object());
  }

Project: node
File: isolate.h

  void set_date_cache(DateCache* date_cache) {
    if (date_cache != date_cache_) {
      delete date_cache_;
    }
    date_cache_ = date_cache;
  }

Project: node
File: isolate.h

  bool IsBelowFrame(JavaScriptFrame* frame) {
    return (c_entry_fp_ == 0) || (c_entry_fp_ > frame->sp());
  }

Project: node
File: isolate.h

  double time_millis_since_init() {
    return OS::TimeCurrentMillis() - time_millis_at_init_;
  }

Project: node
File: isolate.h

  Redirection* simulator_redirection() {
    return simulator_redirection_;
  }

Project: node
File: isolate.h

  HistogramInfo* heap_histograms() { return heap_histograms_; }

Project: node
File: isolate.h

  explicit PostponeInterruptsScope(Isolate* isolate)
      : stack_guard_(isolate->stack_guard()) {
    stack_guard_->thread_local_.postpone_interrupts_nesting_++;
    stack_guard_->DisableInterrupts();
  }

Project: node
File: isolate.h

  HandleScopeImplementer* handle_scope_implementer() {
    ASSERT(handle_scope_implementer_);
    return handle_scope_implementer_;
  }

Project: node
File: isolate.h

  Debug* debug() {
    if (!NoBarrier_Load(&debugger_initialized_)) InitializeDebugger();
    return debug_;
  }

Project: node
File: isolate.h

  bool has_pending_exception() {
    return !thread_local_top_.pending_exception_->IsTheHole();
  }

Project: node
File: isolate.h

  void clear_scheduled_exception() {
    thread_local_top_.scheduled_exception_ = heap_.the_hole_value();
  }

Project: node
File: isolate.h

    uintptr_t stack_limit() const { return stack_limit_; }

Project: node
File: isolate.h

  bool has_installed_extensions() { return has_installed_extensions_; }

Project: node
File: isolate.h

inline void Context::mark_out_of_memory() {
  native_context()->set_out_of_memory(HEAP->true_value());
}

Project: node
File: isolate.h

  void FreeThreadResources() { thread_local_top_.Free(); }

Project: node
File: isolate.h

  StringInputBuffer* objects_string_compare_buffer_a() {
    return &objects_string_compare_buffer_a_;
  }

Project: node
File: isolate.h

  SaveContext* save_context() {return thread_local_top_.save_context_; }

Project: node
File: isolate.h

  bool has_scheduled_exception() {
    return thread_local_top_.scheduled_exception_ != heap_.the_hole_value();
  }

Project: node
File: isolate.h

  void set_context_exit_happened(bool context_exit_happened) {
    context_exit_happened_ = context_exit_happened;
  }

Project: node
File: isolate.h

  bool ignore_out_of_memory() {
    return thread_local_top_.ignore_out_of_memory_;
  }

Project: node
File: isolate.h

  Builtins* builtins() { return &builtins_; }

Project: node
File: isolate.h

  bool is_catchable_by_javascript(MaybeObject* exception) {
    return (exception != Failure::OutOfMemoryException()) &&
        (exception != heap()->termination_exception());
  }

Project: node
File: isolate.h

  JSObject::SpillInformation* js_spill_information() {
    return &js_spill_information_;
  }

Project: node
File: isolate.h

    void set_simulator(Simulator* simulator) {
      simulator_ = simulator;
    }

Project: node
File: isolate.h

  static Thread::LocalStorageKey thread_id_key() {
    return thread_id_key_;
  }

Project: node
File: isolate.h

  inline Address try_catch_handler_address() {
    return try_catch_handler_address_;
  }

Project: node
File: isolate.h

  Object* global_proxy() {
    return context()->global_proxy();
  }

Project: node
File: isolate.h

    ThreadState* thread_state() const { return thread_state_; }

Project: node
File: isolate.h

  CodeRange* code_range() { return code_range_; }

Project: node
File: isolate.h

  TranscendentalCache* transcendental_cache() const {
    return transcendental_cache_;
  }

Project: node
File: isolate.h

  bool external_caught_exception() {
    return thread_local_top_.external_caught_exception_;
  }

Project: node
File: isolate.h

      regexp_macro_assembler_canonicalize() {
    return &regexp_macro_assembler_canonicalize_;
  }

Project: node
File: isolate.h

  DateCache* date_cache() {
    return date_cache_;
  }

Project: node
File: isolate.h

  UnicodeCache* unicode_cache() {
    return unicode_cache_;
  }

Project: node
File: isolate.h

  unibrow::Mapping<unibrow::Ecma262UnCanonicalize>* jsregexp_uncanonicalize() {
    return &jsregexp_uncanonicalize_;
  }

Project: node
File: isolate.h

  Debugger* debugger() {
    if (!NoBarrier_Load(&debugger_initialized_)) InitializeDebugger();
    return debugger_;
  }

Project: node
File: isolate.h

  bool IsDefaultIsolate() const { return this == default_isolate_; }

Project: node
File: isolate.h

  Bootstrapper* bootstrapper() { return bootstrapper_; }

Project: node
File: isolate.h

  StateTag current_vm_state() {
    return thread_local_top_.current_vm_state_;
  }

Project: node
File: isolate.h

inline bool Context::has_out_of_memory() {
  return native_context()->out_of_memory()->IsTrue();
}

Project: node
File: isolate.h

  StringTracker* string_tracker() { return string_tracker_; }

Project: node
File: isolate.h

  bool IsInUse() { return entry_stack_ != NULL; }

Project: node
File: isolate.h

  Address external_callback() {
    return thread_local_top_.external_callback_;
  }

Project: node
File: isolate.h

  void* GetData() { return embedder_data_; }

Project: node
File: isolate.h

  static void Lock(Isolate* isolate) { isolate->break_access_->Lock(); }

Project: node
File: isolate.h

  Handle<JSBuiltinsObject> js_builtins_object() {
    return Handle<JSBuiltinsObject>(thread_local_top_.context_->builtins());
  }

Project: node
File: isolate.h

    explicit ExceptionScope(Isolate* isolate) :
      // Scope currently can only be used for regular exceptions, not
      // failures like OOM or termination exception.
      isolate_(isolate),
      pending_exception_(isolate_->pending_exception()->ToObjectUnchecked()),
      catcher_(isolate_->catcher())
    { }

Project: node
File: isolate.h

  KeyedLookupCache* keyed_lookup_cache() {
    return keyed_lookup_cache_;
  }

Project: node
File: isolate.h

  DescriptorLookupCache* descriptor_lookup_cache() {
    return descriptor_lookup_cache_;
  }

Project: node
File: isolate.h

  INLINE(static Isolate* Current()) {
    Isolate* isolate = reinterpret_cast<Isolate*>(
        Thread::GetExistingThreadLocal(isolate_key_));
    ASSERT(isolate != NULL);
    return isolate;
  }

Project: node
File: isolate.h

  GlobalHandles* global_handles() { return global_handles_; }

Project: node
File: isolate.h

  MaybeObject* pending_exception() {
    ASSERT(has_pending_exception());
    return thread_local_top_.pending_exception_;
  }

Project: node
File: isolate.h

  void set_catcher(v8::TryCatch* catcher) {
    thread_local_top_.catcher_ = catcher;
  }

Project: node
File: isolate.h

  explicit StackLimitCheck(Isolate* isolate) : isolate_(isolate) { }

Project: node
File: isolate.h

  inline Address* c_entry_fp_address() {
    return &thread_local_top_.c_entry_fp_;
  }

Project: node
File: isolate.h

    ThreadId thread_id() const { return thread_id_; }

Project: node
File: isolate.h

  static void Unlock(Isolate* isolate) { isolate->break_access_->Unlock(); }

Project: node
File: isolate.h

  StringInputBuffer* objects_string_compare_buffer_b() {
    return &objects_string_compare_buffer_b_;
  }

Project: node
File: isolate.h

  RuntimeProfiler* runtime_profiler() { return runtime_profiler_; }

Project: node
File: isolate.h

  explicit ExecutionAccess(Isolate* isolate) : isolate_(isolate) {
    Lock(isolate);
  }

Project: node
File: isolate.h

  ThreadLocalTop* thread_local_top() { return &thread_local_top_; }

Project: node
File: isolate.h

  ContextSwitcher* context_switcher() { return context_switcher_; }

Project: node
File: isolate.h

  v8::TryCatch* try_catch_handler() {
    return thread_local_top_.TryCatchHandler();
  }

Project: node
File: isolate.h

  static ThreadId FromInteger(int id) { return ThreadId(id); }

Project: node
File: isolate.h

  bool simulator_initialized() { return simulator_initialized_; }

Project: node
File: isolate.h

  void NotifyExtensionInstalled() {
    has_installed_extensions_ = true;
  }

Project: node
File: isolate.h

  ~AssertNoContextChange() {
    ASSERT(Isolate::Current()->context() == *context_);
  }

Project: node
File: isolate.h

  INLINE(bool IsValid() const) {
    return id_ != kInvalidId;
  }

Project: node
File: isolate.h

  ~SaveContext() {
    if (context_.is_null()) {
      Isolate* isolate = Isolate::Current();
      isolate->set_context(NULL);
      isolate->set_save_context(prev_);
    } else {
      Isolate* isolate = context_->GetIsolate();
      isolate->set_context(*context_);
      isolate->set_save_context(prev_);
    }
  }

Project: node
File: isolate.h

    EntryStackItem(PerIsolateThreadData* previous_thread_data,
                   Isolate* previous_isolate,
                   EntryStackItem* previous_item)
        : entry_count(1),
          previous_thread_data(previous_thread_data),
          previous_isolate(previous_isolate),
          previous_item(previous_item) { }

Project: node
File: isolate.h

  void set_thread_id(ThreadId id) { thread_local_top_.thread_id_ = id; }

Project: node
File: isolate.h

  bool* external_caught_exception_address() {
    return &thread_local_top_.external_caught_exception_;
  }

Project: node
File: isolate.h

  void set_save_context(SaveContext* save) {
    thread_local_top_.save_context_ = save;
  }

Project: node
File: isolate.h

  int ToInteger() const { return id_; }

Project: node
File: isolate.h

  StubCache* stub_cache() { return stub_cache_; }

Project: node
File: isolate.h

  AssertNoContextChange() { }

Project: node
File: isolate.h

  inline void set_try_catch_handler_address(Address address) {
    try_catch_handler_address_ = address;
  }

Project: node
File: isolate.h

  Counters* counters() {
    // Call InitializeLoggingAndCounters() if logging is needed before
    // the isolate is fully initialized.
    ASSERT(counters_ != NULL);
    return counters_;
  }

Project: node
File: isolate.h

    Simulator* simulator() const { return simulator_; }

Project: node
File: isolate.h

  static PerIsolateThreadData* CurrentPerIsolateThreadData() {
    return reinterpret_cast<PerIsolateThreadData*>(
        Thread::GetThreadLocal(per_isolate_thread_data_key_));
  }

Project: node
File: isolate.h

  RegExpStack* regexp_stack() { return regexp_stack_; }

Project: node
File: isolate.h

  static Address c_entry_fp(ThreadLocalTop* thread) {
    return thread->c_entry_fp_;
  }

Project: node
File: isolate.h

  static Thread::LocalStorageKey isolate_key() {
    return isolate_key_;
  }

Project: node
File: isolate.h

  int* code_kind_statistics() { return code_kind_statistics_; }

Project: node
File: isolate.h

  ThreadId() : id_(kInvalidId) {}

Project: node
File: isolate.h

      interp_canonicalize_mapping() {
    return &interp_canonicalize_mapping_;
  }

Project: node
File: isolate.h

  SaveContext* prev() { return prev_; }

Project: node
File: isolate.h

  MaybeObject** pending_exception_address() {
    return &thread_local_top_.pending_exception_;
  }

Project: node
File: isolate.h

  Factory* factory() { return reinterpret_cast<Factory*>(this); }

Project: node
File: isolate.h

  Logger* logger() {
    // Call InitializeLoggingAndCounters() if logging is needed before
    // the isolate is fully initialized.
    ASSERT(logger_ != NULL);
    return logger_;
  }

Project: node
File: isolate.h

  Heap* heap() { return &heap_; }

Project: node
File: isolate.h

  void set_simulator_initialized(bool initialized) {
    simulator_initialized_ = initialized;
  }

Project: node
File: isolate.h

  StaticResource<StringInputBuffer>* objects_string_input_buffer() {
    return &objects_string_input_buffer_;
  }

Project: node
File: isolate.h

  MaybeObject** scheduled_exception_address() {
    return &thread_local_top_.scheduled_exception_;
  }

Project: node
File: isolate.h

  void set_pending_exception(MaybeObject* exception) {
    thread_local_top_.pending_exception_ = exception;
  }

Project: node
File: isolate.h

  static Address handler(ThreadLocalTop* thread) { return thread->handler_; }

Project: node
File: isolate.h

  Address pending_message_obj_address() {
    return reinterpret_cast<Address>(&thread_local_top_.pending_message_obj_);
  }

Project: node
File: isolate.h

  bool fp_stubs_generated() { return fp_stubs_generated_; }

Project: node
File: isolate.h

    ~ExceptionScope() {
      isolate_->set_catcher(catcher_);
      isolate_->set_pending_exception(*pending_exception_);
    }

Project: node
File: isolate.h

  static ThreadId Invalid() { return ThreadId(kInvalidId); }

Project: node
File: isolate.h

  bool context_exit_happened() {
    return context_exit_happened_;
  }

Project: node
File: isolate.h

  InnerPointerToCodeCache* inner_pointer_to_code_cache() {
    return inner_pointer_to_code_cache_;
  }

Project: node
File: isolate.h

  void SetData(void* data) { embedder_data_ = data; }

Project: node
File: isolate.h

  MaybeObject* scheduled_exception() {
    ASSERT(has_scheduled_exception());
    return thread_local_top_.scheduled_exception_;
  }

Project: node
File: isolate.h

  Address pending_message_script_address() {
    return reinterpret_cast<Address>(
        &thread_local_top_.pending_message_script_);
  }

Project: node
File: isolate.h

  void SetTopLookupResult(LookupResult* top) {
    thread_local_top_.top_lookup_result_ = top;
  }

Project: node
File: isolate.h

    void set_thread_state(ThreadState* value) { thread_state_ = value; }

Project: node
File: isolate.h

  bool IsInitialized() { return state_ == INITIALIZED; }

Project: node
File: isolate.h

  ThreadManager* thread_manager() { return thread_manager_; }

Project: node
File: isolate.h

  static ThreadId Current() { return ThreadId(GetCurrentThreadId()); }

Project: node
File: isolate.h

  Map* empty_object_map() {
    return context()->native_context()->object_function()->map();
  }

Project: node
File: isolate.h

  Zone* runtime_zone() { return &runtime_zone_; }

Project: node
File: isolate.h

  LookupResult* top_lookup_result() {
    return thread_local_top_.top_lookup_result_;
  }

Project: node
File: isolate.h

    bool Matches(Isolate* isolate, ThreadId thread_id) const {
      return isolate_ == isolate && thread_id_.Equals(thread_id);
    }

Project: node
File: isolate.h

  void* formal_count_address() { return &thread_local_top_.formal_count_; }

Project: node
File: isolate.h

  void set_context_switcher(ContextSwitcher* switcher) {
    context_switcher_ = switcher;
  }

Project: node
File: isolate.h

  Address try_catch_handler_address() {
    return thread_local_top_.try_catch_handler_address();
  }

Project: node
File: isolate.h

  StringInputBuffer* write_input_buffer() { return write_input_buffer_; }

Project: node
File: isolate.h

  RuntimeState* runtime_state() { return &runtime_state_; }

Project: node
File: isolate.h

  HashMap* simulator_i_cache() { return simulator_i_cache_; }

Project: node
File: isolate.h

  void set_fp_stubs_generated(bool value) {
    fp_stubs_generated_ = value;
  }

Project: node
File: isolate.h

    PerIsolateThreadData(Isolate* isolate, ThreadId thread_id)
        : isolate_(isolate),
          thread_id_(thread_id),
          stack_limit_(0),
          thread_state_(NULL),
#if !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
    !defined(__mips__) && defined(V8_TARGET_ARCH_MIPS)
          simulator_(NULL),
#endif
          next_(NULL),
          prev_(NULL) { }

Project: node
File: isolate.h

  AssertNoContextChange() :
      scope_(Isolate::Current()),
      context_(Isolate::Current()->context(), Isolate::Current()) {
  }

Project: node
File: isolate.h

  static bool TryLock(Isolate* isolate) {
    return isolate->break_access_->TryLock();
  }

Project: node
File: isolate.h

  Context** context_address() { return &thread_local_top_.context_; }

Project: node
File: isolate.h

  void clear_pending_message() {
    thread_local_top_.has_pending_message_ = false;
    thread_local_top_.pending_message_obj_ = heap_.the_hole_value();
    thread_local_top_.pending_message_script_ = NULL;
  }

Project: node
File: isolate.h

  DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }

Project: node
File: isolate.h

  inline Address* js_entry_sp_address() {
    return &thread_local_top_.js_entry_sp_;
  }

Project: node
File: isolate.h

  v8::ImplementationUtilities::HandleScopeData* handle_scope_data() {
    return &handle_scope_data_;
  }

Project: node
File: isolate.h

  void set_simulator_i_cache(HashMap* hash_map) {
    simulator_i_cache_ = hash_map;
  }

Project: node
File: isolate.h

  CompilationCache* compilation_cache() { return compilation_cache_; }

Project: node
File: isolate.h

    Isolate* isolate() const { return isolate_; }

Project: node
File: isolate.h

  ContextSlotCache* context_slot_cache() {
    return context_slot_cache_;
  }