chromium/android_webview/js_sandbox/service/js_sandbox_array_buffer_allocator.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ANDROID_WEBVIEW_JS_SANDBOX_SERVICE_JS_SANDBOX_ARRAY_BUFFER_ALLOCATOR_H_
#define ANDROID_WEBVIEW_JS_SANDBOX_SERVICE_JS_SANDBOX_ARRAY_BUFFER_ALLOCATOR_H_

#include <cstddef>

#include "base/memory/raw_ref.h"
#include "v8/include/v8-array-buffer.h"

namespace android_webview {

// A v8::ArrayBuffer::Allocator which imposes a limit on the amount of
// simultaneously allocated memory. See also the V8 documentation for
// v8::ArrayBuffer::Allocator.
//
// This allocator must only be used from one thread at a time. Allocation and
// deallocation methods should only happen on the isolate thread, but
// construction and destruction will typically happen outside of the isolate
// thread.
class JsSandboxArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
 public:
  // Value to supply as a budget to indicate an unlimited budget.
  static constexpr size_t kUnlimitedBudget = SIZE_MAX;

  // Wrap the given inner allocator and impose a maximum allocation budget.
  //
  // The JsSandboxArrayBufferAllocator allocator does NOT assume ownership of or
  // copy the inner allocator, so the caller must ensure that it outlives the
  // JsSandboxArrayBufferAllocator.
  //
  // The memory budget specifies the maximum amount of memory which can be
  // allocated at any given time. Note that a value of 0 will disallow all
  // allocations. Use kUnlimitedBudget if you want no effective limit.
  //
  // The page size should desribe the page size of the allocator used (this is
  // not necessarily the OS page size), and is used to (pessimistically) account
  // for possible internal memory fragmentation in the budget.
  explicit JsSandboxArrayBufferAllocator(v8::ArrayBuffer::Allocator& inner,
                                         size_t memory_budget,
                                         size_t page_size);
  JsSandboxArrayBufferAllocator(const JsSandboxArrayBufferAllocator&) = delete;
  JsSandboxArrayBufferAllocator& operator=(
      const JsSandboxArrayBufferAllocator&) = delete;
  // All prior allocations must be freed before the allocator is destructed.
  ~JsSandboxArrayBufferAllocator() override;
  // Attempt to allocate length bytes (zero-initialized).
  //
  // The tracked memory usage may be rounded up to a multiple of the page size.
  //
  // nullptr will be returned if the request exceeds the memory budget or the
  // inner allocator returns nullptr.
  void* Allocate(size_t length) override;
  // Similar to Allocate(), but does not explicitly zero-(re)initialize memory
  // in userspace.
  void* AllocateUninitialized(size_t length) override;
  // Deallocate a currently allocated memory region that was previously returned
  // by Allocate.
  //
  // The length must match the current length associated with the region.
  void Free(void* data, size_t length) override;

  // Return the amount of allocated (used) memory budget.
  size_t GetUsage() const;

 private:
  // Try to allocate some budget and return true iff successful
  bool AllocateBudget(size_t amount);
  // Deallocate some budget
  void FreeBudget(size_t amount);

  // The underlying allocator which does the actual memory management.
  const raw_ref<v8::ArrayBuffer::Allocator> inner_allocator_;
  // The amount of unused budget
  size_t remaining_;
  // The overall memory budget this allocator has to stay within
  const size_t budget_;
  // Page size for rounding up allocation amounts.
  const size_t page_size_;
};

}  // namespace android_webview

#endif  // ANDROID_WEBVIEW_JS_SANDBOX_SERVICE_JS_SANDBOX_ARRAY_BUFFER_ALLOCATOR_H_