1 // Copyright 2022 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxcrt/fx_memory.h"
8
9 #include "base/allocator/partition_allocator/partition_alloc.h"
10 #include "core/fxcrt/fx_safe_types.h"
11 #include "third_party/base/no_destructor.h"
12
13 #if !defined(PDF_USE_PARTITION_ALLOC)
14 #error "File compiled under wrong build option."
15 #endif
16
17 namespace {
18
19 constexpr partition_alloc::PartitionOptions kOptions = {};
20
21 #ifndef V8_ENABLE_SANDBOX
GetArrayBufferPartitionAllocator()22 partition_alloc::PartitionAllocator& GetArrayBufferPartitionAllocator() {
23 static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
24 s_array_buffer_allocator(kOptions);
25 return *s_array_buffer_allocator;
26 }
27 #endif // V8_ENABLE_SANDBOX
28
GetGeneralPartitionAllocator()29 partition_alloc::PartitionAllocator& GetGeneralPartitionAllocator() {
30 static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
31 s_general_allocator(kOptions);
32 return *s_general_allocator;
33 }
34
GetStringPartitionAllocator()35 partition_alloc::PartitionAllocator& GetStringPartitionAllocator() {
36 static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
37 s_string_allocator(kOptions);
38 return *s_string_allocator;
39 }
40
41 } // namespace
42
43 namespace pdfium {
44 namespace internal {
45
Alloc(size_t num_members,size_t member_size)46 void* Alloc(size_t num_members, size_t member_size) {
47 FX_SAFE_SIZE_T total = member_size;
48 total *= num_members;
49 if (!total.IsValid())
50 return nullptr;
51
52 return GetGeneralPartitionAllocator().root()->AllocWithFlags(
53 partition_alloc::AllocFlags::kReturnNull, total.ValueOrDie(),
54 "GeneralPartition");
55 }
56
Calloc(size_t num_members,size_t member_size)57 void* Calloc(size_t num_members, size_t member_size) {
58 FX_SAFE_SIZE_T total = member_size;
59 total *= num_members;
60 if (!total.IsValid())
61 return nullptr;
62
63 return GetGeneralPartitionAllocator().root()->AllocWithFlags(
64 partition_alloc::AllocFlags::kReturnNull |
65 partition_alloc::AllocFlags::kZeroFill,
66 total.ValueOrDie(), "GeneralPartition");
67 }
68
Realloc(void * ptr,size_t num_members,size_t member_size)69 void* Realloc(void* ptr, size_t num_members, size_t member_size) {
70 FX_SAFE_SIZE_T size = num_members;
71 size *= member_size;
72 if (!size.IsValid())
73 return nullptr;
74
75 return GetGeneralPartitionAllocator().root()->ReallocWithFlags(
76 partition_alloc::AllocFlags::kReturnNull, ptr, size.ValueOrDie(),
77 "GeneralPartition");
78 }
79
Dealloc(void * ptr)80 void Dealloc(void* ptr) {
81 // TODO(palmer): Removing this check exposes crashes when PDFium callers
82 // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
83 // other Partition Alloc callers need this tolerant behavior. Additionally,
84 // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
85 // not have to have that.
86 //
87 // So this check is hiding (what I consider to be) bugs, and we should try to
88 // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
89 if (ptr) {
90 GetGeneralPartitionAllocator().root()->Free(ptr);
91 }
92 }
93
StringAlloc(size_t num_members,size_t member_size)94 void* StringAlloc(size_t num_members, size_t member_size) {
95 FX_SAFE_SIZE_T total = member_size;
96 total *= num_members;
97 if (!total.IsValid())
98 return nullptr;
99
100 return GetStringPartitionAllocator().root()->AllocWithFlags(
101 partition_alloc::AllocFlags::kReturnNull, total.ValueOrDie(),
102 "StringPartition");
103 }
104
StringDealloc(void * ptr)105 void StringDealloc(void* ptr) {
106 // TODO(palmer): Removing this check exposes crashes when PDFium callers
107 // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
108 // other Partition Alloc callers need this tolerant behavior. Additionally,
109 // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
110 // not have to have that.
111 //
112 // So this check is hiding (what I consider to be) bugs, and we should try to
113 // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
114 if (ptr) {
115 GetStringPartitionAllocator().root()->Free(ptr);
116 }
117 }
118
119 } // namespace internal
120 } // namespace pdfium
121
FX_InitializeMemoryAllocators()122 void FX_InitializeMemoryAllocators() {
123 static bool s_partition_allocators_initialized = false;
124 if (!s_partition_allocators_initialized) {
125 partition_alloc::PartitionAllocGlobalInit(FX_OutOfMemoryTerminate);
126 // These calls force the allocators to be created and initialized (via magic
127 // of static local variables).
128 #ifndef V8_ENABLE_SANDBOX
129 GetArrayBufferPartitionAllocator();
130 #endif // V8_ENABLE_SANDBOX
131 GetGeneralPartitionAllocator();
132 GetStringPartitionAllocator();
133 s_partition_allocators_initialized = true;
134 }
135 }
136
137 #ifndef V8_ENABLE_SANDBOX
FX_ArrayBufferAllocate(size_t length)138 void* FX_ArrayBufferAllocate(size_t length) {
139 return GetArrayBufferPartitionAllocator().root()->AllocWithFlags(
140 partition_alloc::AllocFlags::kZeroFill, length, "FXArrayBuffer");
141 }
142
FX_ArrayBufferAllocateUninitialized(size_t length)143 void* FX_ArrayBufferAllocateUninitialized(size_t length) {
144 return GetArrayBufferPartitionAllocator().root()->Alloc(length,
145 "FXArrayBuffer");
146 }
147
FX_ArrayBufferFree(void * data)148 void FX_ArrayBufferFree(void* data) {
149 GetArrayBufferPartitionAllocator().root()->Free(data);
150 }
151 #endif // V8_ENABLE_SANDBOX
152