11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/sandbox/external-pointer-table.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <algorithm>
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci#include "src/execution/isolate.h"
101cb0ef41Sopenharmony_ci#include "src/logging/counters.h"
111cb0ef41Sopenharmony_ci#include "src/sandbox/external-pointer-table-inl.h"
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ci#ifdef V8_SANDBOX_IS_AVAILABLE
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_cinamespace v8 {
161cb0ef41Sopenharmony_cinamespace internal {
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciSTATIC_ASSERT(sizeof(ExternalPointerTable) == ExternalPointerTable::kSize);
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci// static
211cb0ef41Sopenharmony_ciuint32_t ExternalPointerTable::AllocateEntry(ExternalPointerTable* table) {
221cb0ef41Sopenharmony_ci  return table->Allocate();
231cb0ef41Sopenharmony_ci}
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciuint32_t ExternalPointerTable::Sweep(Isolate* isolate) {
261cb0ef41Sopenharmony_ci  // Sweep top to bottom and rebuild the freelist from newly dead and
271cb0ef41Sopenharmony_ci  // previously freed entries. This way, the freelist ends up sorted by index,
281cb0ef41Sopenharmony_ci  // which helps defragment the table. This method must run either on the
291cb0ef41Sopenharmony_ci  // mutator thread or while the mutator is stopped. Also clear marking bits on
301cb0ef41Sopenharmony_ci  // live entries.
311cb0ef41Sopenharmony_ci  // TODO(v8:10391, saelo) could also shrink the table using DecommitPages() if
321cb0ef41Sopenharmony_ci  // elements at the end are free. This might require some form of compaction.
331cb0ef41Sopenharmony_ci  uint32_t freelist_size = 0;
341cb0ef41Sopenharmony_ci  uint32_t current_freelist_head = 0;
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ci  // Skip the special null entry.
371cb0ef41Sopenharmony_ci  DCHECK_GE(capacity_, 1);
381cb0ef41Sopenharmony_ci  for (uint32_t i = capacity_ - 1; i > 0; i--) {
391cb0ef41Sopenharmony_ci    // No other threads are active during sweep, so there is no need to use
401cb0ef41Sopenharmony_ci    // atomic operations here.
411cb0ef41Sopenharmony_ci    Address entry = load(i);
421cb0ef41Sopenharmony_ci    if (!is_marked(entry)) {
431cb0ef41Sopenharmony_ci      store(i, make_freelist_entry(current_freelist_head));
441cb0ef41Sopenharmony_ci      current_freelist_head = i;
451cb0ef41Sopenharmony_ci      freelist_size++;
461cb0ef41Sopenharmony_ci    } else {
471cb0ef41Sopenharmony_ci      store(i, clear_mark_bit(entry));
481cb0ef41Sopenharmony_ci    }
491cb0ef41Sopenharmony_ci  }
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci  freelist_head_ = current_freelist_head;
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  uint32_t num_active_entries = capacity_ - freelist_size;
541cb0ef41Sopenharmony_ci  isolate->counters()->sandboxed_external_pointers_count()->AddSample(
551cb0ef41Sopenharmony_ci      num_active_entries);
561cb0ef41Sopenharmony_ci  return num_active_entries;
571cb0ef41Sopenharmony_ci}
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ciuint32_t ExternalPointerTable::Grow() {
601cb0ef41Sopenharmony_ci  // Freelist should be empty.
611cb0ef41Sopenharmony_ci  DCHECK_EQ(0, freelist_head_);
621cb0ef41Sopenharmony_ci  // Mutex must be held when calling this method.
631cb0ef41Sopenharmony_ci  mutex_->AssertHeld();
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci  // Grow the table by one block.
661cb0ef41Sopenharmony_ci  uint32_t old_capacity = capacity_;
671cb0ef41Sopenharmony_ci  uint32_t new_capacity = old_capacity + kEntriesPerBlock;
681cb0ef41Sopenharmony_ci  CHECK_LE(new_capacity, kMaxSandboxedExternalPointers);
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci  // Failure likely means OOM. TODO(saelo) handle this.
711cb0ef41Sopenharmony_ci  VirtualAddressSpace* root_space = GetPlatformVirtualAddressSpace();
721cb0ef41Sopenharmony_ci  DCHECK(IsAligned(kBlockSize, root_space->page_size()));
731cb0ef41Sopenharmony_ci  CHECK(root_space->SetPagePermissions(buffer_ + old_capacity * sizeof(Address),
741cb0ef41Sopenharmony_ci                                       kBlockSize,
751cb0ef41Sopenharmony_ci                                       PagePermissions::kReadWrite));
761cb0ef41Sopenharmony_ci  capacity_ = new_capacity;
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  // Build freelist bottom to top, which might be more cache friendly.
791cb0ef41Sopenharmony_ci  uint32_t start = std::max<uint32_t>(old_capacity, 1);  // Skip entry zero
801cb0ef41Sopenharmony_ci  uint32_t last = new_capacity - 1;
811cb0ef41Sopenharmony_ci  for (uint32_t i = start; i < last; i++) {
821cb0ef41Sopenharmony_ci    store(i, make_freelist_entry(i + 1));
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci  store(last, make_freelist_entry(0));
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  // This must be a release store to prevent reordering of the preceeding
871cb0ef41Sopenharmony_ci  // stores to the freelist from being reordered past this store. See
881cb0ef41Sopenharmony_ci  // Allocate() for more details.
891cb0ef41Sopenharmony_ci  base::Release_Store(reinterpret_cast<base::Atomic32*>(&freelist_head_),
901cb0ef41Sopenharmony_ci                      start);
911cb0ef41Sopenharmony_ci  return start;
921cb0ef41Sopenharmony_ci}
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci}  // namespace internal
951cb0ef41Sopenharmony_ci}  // namespace v8
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci#endif  // V8_SANDBOX_IS_AVAILABLE
98