1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
android_webview / js_sandbox / service / js_sandbox_array_buffer_allocator.cc [blame]
// 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.
#include "android_webview/js_sandbox/service/js_sandbox_array_buffer_allocator.h"
#include <cstddef>
#include <memory>
#include "base/check.h"
#include "base/check_op.h"
#include "base/logging.h"
#include "base/memory/raw_ref.h"
#include "v8/include/v8-array-buffer.h"
namespace {
size_t RoundUpToPage(const size_t amount, const size_t page_size) {
CHECK_LE(amount, SIZE_MAX / page_size * page_size);
return ((amount + page_size - 1) / page_size) * page_size;
}
} // namespace
namespace android_webview {
JsSandboxArrayBufferAllocator::JsSandboxArrayBufferAllocator(
v8::ArrayBuffer::Allocator& inner,
const size_t budget,
const size_t page_size)
: inner_allocator_(inner),
remaining_(budget),
budget_(budget),
page_size_(page_size) {
DCHECK_GT(page_size, size_t{0});
}
JsSandboxArrayBufferAllocator::~JsSandboxArrayBufferAllocator() {
// Note, remaining_ <= budget is an invariant maintained by CHECKs, so this
// should only ever fail if remaining_ < budget_.
DCHECK_EQ(remaining_, budget_) << "Memory leaked: " << (budget_ - remaining_)
<< " bytes of array buffers not freed before "
"array buffer allocator destruction";
}
void* JsSandboxArrayBufferAllocator::Allocate(const size_t length) {
if (!AllocateBudget(length)) {
return nullptr;
}
void* const buffer = inner_allocator_->Allocate(length);
if (!buffer) {
FreeBudget(length);
return nullptr;
}
return buffer;
}
void* JsSandboxArrayBufferAllocator::AllocateUninitialized(
const size_t length) {
if (!AllocateBudget(length)) {
return nullptr;
}
void* const buffer = inner_allocator_->AllocateUninitialized(length);
if (!buffer) {
FreeBudget(length);
return nullptr;
}
return buffer;
}
void JsSandboxArrayBufferAllocator::Free(void* const data,
const size_t length) {
inner_allocator_->Free(data, length);
FreeBudget(length);
}
bool JsSandboxArrayBufferAllocator::AllocateBudget(const size_t amount) {
const size_t rounded_amount = RoundUpToPage(amount, page_size_);
if (remaining_ < rounded_amount) {
return false;
}
remaining_ -= rounded_amount;
return true;
}
void JsSandboxArrayBufferAllocator::FreeBudget(const size_t amount) {
const size_t rounded_amount = RoundUpToPage(amount, page_size_);
CHECK_LE(amount, GetUsage())
<< "attempted to free more array buffer memory than is allocated";
remaining_ += rounded_amount;
}
size_t JsSandboxArrayBufferAllocator::GetUsage() const {
return budget_ - remaining_;
}
} // namespace android_webview