Skip to content

Commit ceb27a6

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
More overflow checks in memory allocator. (#15581)
Summary: - Safer overflow math for high alignments - Only over-allocate up to alignment - 1 bytes when a higher-than-malloc alignment is requested - Move EXECUTORCH_TRACK_ALLOCATION to after a successful allocation and use the final size Reviewed By: JacobSzwejbka Differential Revision: D86228757
1 parent 993254c commit ceb27a6

File tree

3 files changed

+52
-42
lines changed

3 files changed

+52
-42
lines changed

extension/memory_allocator/malloc_memory_allocator.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <cstddef>
1212
#include <cstdint>
13+
#include <cstdlib>
1314
#include <vector>
1415

1516
#include <executorch/runtime/core/memory_allocator.h>
@@ -45,8 +46,6 @@ class MallocMemoryAllocator : public executorch::runtime::MemoryAllocator {
4546
* memory alignment size.
4647
*/
4748
void* allocate(size_t size, size_t alignment = kDefaultAlignment) override {
48-
EXECUTORCH_TRACK_ALLOCATION(prof_id(), size);
49-
5049
if (!isPowerOf2(alignment)) {
5150
ET_LOG(Error, "Alignment %zu is not a power of 2", alignment);
5251
return nullptr;
@@ -56,30 +55,30 @@ class MallocMemoryAllocator : public executorch::runtime::MemoryAllocator {
5655
static constexpr size_t kMallocAlignment = alignof(std::max_align_t);
5756
if (alignment > kMallocAlignment) {
5857
// To get higher alignments, allocate extra and then align the returned
59-
// pointer. This will waste an extra `alignment` bytes every time, but
58+
// pointer. This will waste an extra `alignment - 1` bytes every time, but
6059
// this is the only portable way to get aligned memory from the heap.
61-
62-
// Check for overflow before adding alignment to size
63-
if (size > SIZE_MAX - alignment) {
60+
const size_t extra = alignment - 1;
61+
if ET_UNLIKELY (extra > SIZE_MAX - size) {
6462
ET_LOG(
65-
Error, "Size %zu + alignment %zu would overflow", size, alignment);
63+
Error, "Malloc size overflow: size=%zu + extra=%zu", size, extra);
6664
return nullptr;
6765
}
68-
size += alignment;
66+
size += extra;
6967
}
7068
void* mem_ptr = std::malloc(size);
71-
if (mem_ptr == nullptr) {
72-
ET_LOG(Error, "Failed to allocate %zu bytes", size);
69+
if (!mem_ptr) {
70+
ET_LOG(Error, "Malloc failed to allocate %zu bytes", size);
7371
return nullptr;
7472
}
7573
mem_ptrs_.emplace_back(mem_ptr);
74+
EXECUTORCH_TRACK_ALLOCATION(prof_id(), size);
7675
return alignPointer(mem_ptrs_.back(), alignment);
7776
}
7877

7978
// Free up each hosted memory pointer. The memory was created via malloc.
8079
void reset() override {
8180
for (auto mem_ptr : mem_ptrs_) {
82-
free(mem_ptr);
81+
std::free(mem_ptr);
8382
}
8483
mem_ptrs_.clear();
8584
}

runtime/core/hierarchical_allocator.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@
99
#pragma once
1010

1111
#include <c10/util/irange.h>
12+
1213
#include <executorch/runtime/core/memory_allocator.h>
1314
#include <executorch/runtime/core/result.h>
1415
#include <executorch/runtime/core/span.h>
15-
#include <executorch/runtime/platform/assert.h>
16-
#include <executorch/runtime/platform/compiler.h>
17-
#include <executorch/runtime/platform/log.h>
18-
#include <cstdint>
1916

2017
namespace executorch {
2118
namespace runtime {

runtime/core/memory_allocator.h

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,12 @@
88

99
#pragma once
1010

11-
#include <stdio.h>
1211
#include <cinttypes>
13-
#include <cstdint>
1412

1513
#include <c10/util/safe_numerics.h>
1614

1715
#include <executorch/runtime/core/error.h>
1816
#include <executorch/runtime/platform/assert.h>
19-
#include <executorch/runtime/platform/compiler.h>
20-
#include <executorch/runtime/platform/log.h>
2117
#include <executorch/runtime/platform/profiler.h>
2218

2319
namespace executorch {
@@ -59,9 +55,28 @@ class MemoryAllocator {
5955
*/
6056
MemoryAllocator(uint32_t size, uint8_t* base_address)
6157
: begin_(base_address),
62-
end_(base_address + size),
58+
end_(
59+
base_address
60+
? (UINTPTR_MAX - reinterpret_cast<uintptr_t>(base_address) >=
61+
size
62+
? base_address + size
63+
: nullptr)
64+
: nullptr),
6365
cur_(base_address),
64-
size_(size) {}
66+
size_(size) {
67+
ET_CHECK_MSG(
68+
base_address || size == 0,
69+
"Base address is null but size=%" PRIu32,
70+
size);
71+
ET_CHECK_MSG(
72+
!base_address || size == 0 ||
73+
(UINTPTR_MAX - reinterpret_cast<uintptr_t>(base_address) >= size),
74+
"Address space overflow in allocator");
75+
}
76+
77+
MemoryAllocator(const MemoryAllocator&) = delete;
78+
MemoryAllocator& operator=(const MemoryAllocator&) = delete;
79+
virtual ~MemoryAllocator() = default;
6580

6681
/**
6782
* Allocates `size` bytes of memory.
@@ -74,6 +89,10 @@ class MemoryAllocator {
7489
* @retval nullptr Not enough memory, or `alignment` was not a power of 2.
7590
*/
7691
virtual void* allocate(size_t size, size_t alignment = kDefaultAlignment) {
92+
if ET_UNLIKELY (!begin_ || !end_) {
93+
ET_LOG(Error, "allocate() on zero-capacity allocator");
94+
return nullptr;
95+
}
7796
if (!isPowerOf2(alignment)) {
7897
ET_LOG(Error, "Alignment %zu is not a power of 2", alignment);
7998
return nullptr;
@@ -82,18 +101,17 @@ class MemoryAllocator {
82101
// The allocation will occupy [start, end), where the start is the next
83102
// position that's a multiple of alignment.
84103
uint8_t* start = alignPointer(cur_, alignment);
85-
uint8_t* end = start + size;
86-
87-
// If the end of this allocation exceeds the end of this allocator, print
88-
// error messages and return nullptr
89-
if (end > end_ || end < start) {
104+
size_t padding = static_cast<size_t>(start - cur_);
105+
size_t available = static_cast<size_t>(end_ - cur_);
106+
if ET_UNLIKELY (padding > available || size > available - padding) {
90107
ET_LOG(
91108
Error,
92109
"Memory allocation failed: %zuB requested (adjusted for alignment), %zuB available",
93-
static_cast<size_t>(end - cur_),
94-
static_cast<size_t>(end_ - cur_));
110+
padding + size,
111+
available);
95112
return nullptr;
96113
}
114+
uint8_t* end = start + size;
97115

98116
// Otherwise, record how many bytes were used, advance cur_ to the new end,
99117
// and then return start. Note that the number of bytes used is (end - cur_)
@@ -144,8 +162,9 @@ class MemoryAllocator {
144162
if (overflow) {
145163
ET_LOG(
146164
Error,
147-
"Failed to allocate list of type %zu: size * sizeof(T) overflowed",
148-
size);
165+
"Failed to allocate list: size(%zu) * sizeof(T)(%zu) overflowed",
166+
size,
167+
sizeof(T));
149168
return nullptr;
150169
}
151170
return static_cast<T*>(this->allocate(bytes_size, alignment));
@@ -171,8 +190,6 @@ class MemoryAllocator {
171190
prof_id_ = EXECUTORCH_TRACK_ALLOCATOR(name);
172191
}
173192

174-
virtual ~MemoryAllocator() {}
175-
176193
protected:
177194
/**
178195
* Returns the profiler ID for this allocator.
@@ -184,21 +201,18 @@ class MemoryAllocator {
184201
/**
185202
* Returns true if the value is an integer power of 2.
186203
*/
187-
static bool isPowerOf2(size_t value) {
188-
return value > 0 && (value & ~(value - 1)) == value;
204+
static constexpr bool isPowerOf2(size_t value) {
205+
return value && !(value & (value - 1));
189206
}
190207

191208
/**
192209
* Returns the next alignment for a given pointer.
193210
*/
194-
static uint8_t* alignPointer(void* ptr, size_t alignment) {
195-
intptr_t addr = reinterpret_cast<intptr_t>(ptr);
196-
if ((addr & (alignment - 1)) == 0) {
197-
// Already aligned.
198-
return reinterpret_cast<uint8_t*>(ptr);
199-
}
200-
addr = (addr | (alignment - 1)) + 1;
201-
return reinterpret_cast<uint8_t*>(addr);
211+
static inline uint8_t* alignPointer(void* ptr, size_t alignment) {
212+
uintptr_t address = reinterpret_cast<uintptr_t>(ptr);
213+
uintptr_t mask = static_cast<uintptr_t>(alignment - 1);
214+
address = (address + mask) & ~mask;
215+
return reinterpret_cast<uint8_t*>(address);
202216
}
203217

204218
private:

0 commit comments

Comments
 (0)