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