Commit message: deps: backport 3a9bfec from v8 upstream

Some of the logic from `zone.cc` is found in `zone-inl.h` in this
release stream.

Original commit message:

  Fix overflow issue in Zone::New

  When requesting a large allocation near the end of the address space,
  the computation could overflow and erroneously *not* grow the Zone
  as required.

	BUG=chromium:606115
	LOG=y

  Review-Url: https://codereview.chromium.org/1930873002
  Cr-Commit-Position: refs/heads/master@{#35903}

PR-URL: https://github.com/nodejs/node-private/pull/43
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Rod Vagg <rod@vagg.org>
CVE: CVE-2016-1669
CWE: CWE-119
Vulnerability description: The Zone::New function in zone.cc in Google V8 before 5.0.71.47, as used in Google Chrome before 50.0.2661.102, does not properly determine when to expand certain memory allocations, which allows remote attackers to cause a denial of service (buffer overflow) or possibly have unspecified other impact via crafted JavaScript code.

Project: node

void Zone::DeleteAll() {
#ifdef DEBUG
  // Constant byte value used for zapping dead memory in debug mode.
  static const unsigned char kZapDeadByte = 0xcd;
#endif

  // Find a segment with a suitable size to keep around.
  Segment* keep = segment_head_;
  while (keep != NULL && keep->size() > kMaximumKeptSegmentSize) {
    keep = keep->next();
  }

  // Traverse the chained list of segments, zapping (in debug mode)
  // and freeing every segment except the one we wish to keep.
  Segment* current = segment_head_;
  while (current != NULL) {
    Segment* next = current->next();
    if (current == keep) {
      // Unlink the segment we wish to keep from the list.
      current->clear_next();
    } else {
      int size = current->size();
#ifdef DEBUG
      // Zap the entire current segment (including the header).
      memset(current, kZapDeadByte, size);
#endif
      DeleteSegment(current, size);
    }
    current = next;
  }

  // If we have found a segment we want to keep, we must recompute the
  // variables 'position' and 'limit' to prepare for future allocate
  // attempts. Otherwise, we must clear the position and limit to
  // force a new segment to be allocated on demand.
  if (keep != NULL) {
    Address start = keep->start();
    position_ = RoundUp(start, kAlignment);
    limit_ = keep->end();
#ifdef DEBUG
    // Zap the contents of the kept segment (but not the header).
    memset(start, kZapDeadByte, keep->capacity());
#endif
  } else {
    position_ = limit_ = 0;
  }

  // Update the head segment to be the kept segment (if any).
  segment_head_ = keep;
}

Project: node

ZoneSplayTree<Config>::~ZoneSplayTree() {
  // Reset the root to avoid unneeded iteration over all tree nodes
  // in the destructor.  For a zone-allocated tree, nodes will be
  // freed by the Zone.
  SplayTree<Config, ZoneAllocationPolicy>::ResetRoot();
}

Project: node

  Address address(int n) const {
    return Address(this) + n;
  }

Project: node

  Segment* next() const { return next_; }

Project: node

void* ZoneObject::operator new(size_t size, Zone* zone) {
  return zone->New(static_cast<int>(size));
}

Project: node

bool ZoneScope::ShouldDeleteOnExit() {
  return zone_->scope_nesting_ == 1 && mode_ == DELETE_ON_EXIT;
}

Project: node

inline void* ZoneAllocationPolicy::New(size_t size) {
  ASSERT(zone_);
  return zone_->New(static_cast<int>(size));
}

Project: node

bool Zone::excess_allocation() {
  return segment_bytes_allocated_ > zone_excess_limit_;
}

Project: node

T* Zone::NewArray(int length) {
  return static_cast<T*>(New(length * sizeof(T)));
}

Project: node

  int capacity() const { return size_ - sizeof(Segment); }

Project: node

void Zone::DeleteKeptSegment() {
  if (segment_head_ != NULL) {
    DeleteSegment(segment_head_, segment_head_->size());
    segment_head_ = NULL;
  }
}

Project: node

void* ZoneList<T>::operator new(size_t size, Zone* zone) {
  return zone->New(static_cast<int>(size));
}

Project: node

Segment* Zone::NewSegment(int size) {
  Segment* result = reinterpret_cast<Segment*>(Malloced::New(size));
  adjust_segment_bytes_allocated(size);
  if (result != NULL) {
    result->Initialize(segment_head_, size);
    segment_head_ = result;
  }
  return result;
}

Project: node

Zone::Zone(Isolate* isolate)
    : zone_excess_limit_(256 * MB),
      segment_bytes_allocated_(0),
      position_(0),
      limit_(0),
      scope_nesting_(0),
      segment_head_(NULL),
      isolate_(isolate) {
}

Project: node

  Address end() const { return address(size_); }

Project: node

ZoneScope::~ZoneScope() {
  if (ShouldDeleteOnExit()) zone_->DeleteAll();
  zone_->scope_nesting_--;
}

Project: node

  void Initialize(Segment* next, int size) {
    next_ = next;
    size_ = size;
  }

Project: node

  void clear_next() { next_ = NULL; }

Project: node

ZoneScope::ZoneScope(Zone* zone, ZoneScopeMode mode)
    : zone_(zone), mode_(mode) {
  zone_->scope_nesting_++;
}

Project: node

  Address start() const { return address(sizeof(Segment)); }

Project: node

void Zone::DeleteSegment(Segment* segment, int size) {
  adjust_segment_bytes_allocated(-size);
  Malloced::Delete(segment);
}

Project: node

void Zone::adjust_segment_bytes_allocated(int delta) {
  segment_bytes_allocated_ += delta;
  isolate_->counters()->zone_segment_bytes()->Set(segment_bytes_allocated_);
}

Project: node

Address Zone::NewExpand(int size) {
  // Make sure the requested size is already properly aligned and that
  // there isn't enough room in the Zone to satisfy the request.
  ASSERT(size == RoundDown(size, kAlignment));
  ASSERT(limit_ < position_ ||
         reinterpret_cast<uintptr_t>(limit_) -
                 reinterpret_cast<uintptr_t>(position_) <
             size);

  // Compute the new segment size. We use a 'high water mark'
  // strategy, where we increase the segment size every time we expand
  // except that we employ a maximum segment size when we delete. This
  // is to avoid excessive malloc() and free() overhead.
  Segment* head = segment_head_;
  int old_size = (head == NULL) ? 0 : head->size();
  static const int kSegmentOverhead = sizeof(Segment) + kAlignment;
  int new_size_no_overhead = size + (old_size << 1);
  int new_size = kSegmentOverhead + new_size_no_overhead;
  // Guard against integer overflow.
  if (new_size_no_overhead < size || new_size < kSegmentOverhead) {
    V8::FatalProcessOutOfMemory("Zone");
    return NULL;
  }
  if (new_size < kMinimumSegmentSize) {
    new_size = kMinimumSegmentSize;
  } else if (new_size > kMaximumSegmentSize) {
    // Limit the size of new segments to avoid growing the segment size
    // exponentially, thus putting pressure on contiguous virtual address space.
    // All the while making sure to allocate a segment large enough to hold the
    // requested size.
    new_size = Max(kSegmentOverhead + size, kMaximumSegmentSize);
  }
  Segment* segment = NewSegment(new_size);
  if (segment == NULL) {
    V8::FatalProcessOutOfMemory("Zone");
    return NULL;
  }

  // Recompute 'top' and 'limit' based on the new segment.
  Address result = RoundUp(segment->start(), kAlignment);
  position_ = result + size;
  // Check for address overflow.
  if (position_ < result) {
    V8::FatalProcessOutOfMemory("Zone");
    return NULL;
  }
  limit_ = segment->end();
  ASSERT(position_ <= limit_);
  return result;
}

Project: node

inline void* Zone::New(int size) {
  ASSERT(scope_nesting_ > 0);
  // Round up the requested size to fit the alignment.
  size = RoundUp(size, kAlignment);

  // If the allocation size is divisible by 8 then we return an 8-byte aligned
  // address.
  if (kPointerSize == 4 && kAlignment == 4) {
    position_ += ((~size) & 4) & (reinterpret_cast<intptr_t>(position_) & 4);
  } else {
    ASSERT(kAlignment >= kPointerSize);
  }

  // Check if the requested size is available without expanding.
  Address result = position_;

  const uintptr_t limit = reinterpret_cast<uintptr_t>(limit_);
  const uintptr_t position = reinterpret_cast<uintptr_t>(position_);
  // position_ > limit_ can be true after the alignment correction above.
  if (limit < position || size > limit - position) {
     result = NewExpand(size);
  } else {
     position_ += size;
  }

  // Check that the result has the proper alignment and return it.
  ASSERT(IsAddressAligned(result, kAlignment, 0));
  allocation_size_ += size;
  return reinterpret_cast<void*>(result);
}