Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * Copyright 2019 Google Inc. All Rights Reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "PrivateHeap.h"
20 :
21 : #include <string.h>
22 :
23 : #include <lib/support/CodeUtils.h>
24 : #include <lib/support/logging/CHIPLogging.h>
25 :
26 : namespace {
27 :
28 : constexpr uint32_t kInvalidHeapBlockSize = 0xFFFFFFFF;
29 : constexpr int32_t kHeapBlockInUse = 0x01;
30 : constexpr int32_t kHeapBlockFree = 0x10;
31 : constexpr int32_t kInvalidHeaderState = 0xff;
32 :
33 : using internal::PrivateHeapBlockHeader;
34 :
35 : // this makes life easier, no need to add align offsets.
36 : static_assert(sizeof(PrivateHeapBlockHeader) % kPrivateHeapAllocationAlignment == 0, "Invalid block size.");
37 :
38 1703 : uint32_t ComputeHeapBlockChecksum(const PrivateHeapBlockHeader * header)
39 : {
40 1703 : uint32_t checksum = header->prevBytes;
41 1703 : checksum *= 31;
42 1703 : checksum += header->nextBytes;
43 1703 : checksum *= 31;
44 1703 : checksum += header->state;
45 1703 : return checksum;
46 : }
47 :
48 : // Advances the heap block to the next value
49 633 : PrivateHeapBlockHeader * NextHeader(PrivateHeapBlockHeader * start)
50 : {
51 633 : if (start->nextBytes == kInvalidHeapBlockSize)
52 : {
53 27 : return nullptr;
54 : }
55 :
56 606 : if (start->checksum != ComputeHeapBlockChecksum(start))
57 : {
58 0 : ChipLogError(Support, "Corrupted heap: checksum is invalid");
59 0 : chipDie();
60 : }
61 :
62 606 : return reinterpret_cast<PrivateHeapBlockHeader *>(reinterpret_cast<char *>(start) + sizeof(PrivateHeapBlockHeader) +
63 606 : start->nextBytes);
64 : }
65 :
66 : // Advances the heap block to the previous value
67 103 : PrivateHeapBlockHeader * PreviousHeader(PrivateHeapBlockHeader * start)
68 : {
69 103 : if (start->prevBytes == kInvalidHeapBlockSize)
70 : {
71 47 : return nullptr;
72 : }
73 :
74 56 : if (start->checksum != ComputeHeapBlockChecksum(start))
75 : {
76 0 : ChipLogError(Support, "Corrupted heap: checksum is invalid");
77 0 : chipDie();
78 : }
79 :
80 56 : return reinterpret_cast<PrivateHeapBlockHeader *>(reinterpret_cast<char *>(start) - sizeof(PrivateHeapBlockHeader) -
81 56 : start->prevBytes);
82 : }
83 :
84 649 : void ValidateHeader(const PrivateHeapBlockHeader * header)
85 : {
86 649 : if (header->state != kHeapBlockFree && header->state != kHeapBlockInUse)
87 : {
88 0 : ChipLogError(Support, "Invalid header state (neither free nor in use) at %p", header);
89 0 : chipDie();
90 : }
91 :
92 649 : if (header->checksum != ComputeHeapBlockChecksum(header))
93 : {
94 0 : ChipLogError(Support, "Corrupted heap: checksum is invalid at %p", header);
95 0 : chipDie();
96 : }
97 649 : }
98 :
99 : } // namespace
100 :
101 9 : extern "C" void PrivateHeapInit(void * heap, size_t size)
102 : {
103 9 : if (heap == nullptr)
104 : {
105 0 : ChipLogError(Support, "Cannot initialize null heap");
106 0 : chipDie();
107 : }
108 :
109 9 : if (size < 2 * sizeof(PrivateHeapBlockHeader))
110 : {
111 0 : ChipLogError(Support, "Insufficient space in private heap");
112 0 : chipDie();
113 : }
114 :
115 9 : if (reinterpret_cast<uintptr_t>(heap) % kPrivateHeapAllocationAlignment != 0)
116 : {
117 0 : ChipLogError(Support, "Invalid alignment for private heap initialization");
118 0 : chipDie();
119 : }
120 :
121 9 : PrivateHeapBlockHeader * header = reinterpret_cast<PrivateHeapBlockHeader *>(heap);
122 :
123 9 : header->prevBytes = kInvalidHeapBlockSize;
124 9 : header->nextBytes = static_cast<uint32_t>(size - 2 * sizeof(PrivateHeapBlockHeader));
125 9 : header->state = kHeapBlockFree;
126 9 : header->checksum = ComputeHeapBlockChecksum(header);
127 :
128 9 : header = NextHeader(header);
129 9 : header->nextBytes = kInvalidHeapBlockSize;
130 9 : header->prevBytes = static_cast<uint32_t>(size - 2 * sizeof(PrivateHeapBlockHeader));
131 9 : header->state = kHeapBlockFree; // does not matter really
132 9 : header->checksum = ComputeHeapBlockChecksum(header);
133 9 : }
134 :
135 130 : extern "C" void * PrivateHeapAlloc(void * heap, size_t size)
136 : {
137 130 : PrivateHeapBlockHeader * header = reinterpret_cast<PrivateHeapBlockHeader *>(heap);
138 :
139 : // we allocate aligned, no matter what
140 130 : if (size % kPrivateHeapAllocationAlignment != 0)
141 : {
142 40 : size += kPrivateHeapAllocationAlignment - (size % kPrivateHeapAllocationAlignment);
143 : }
144 :
145 567 : for (; header != nullptr; header = NextHeader(header))
146 : {
147 540 : ValidateHeader(header);
148 :
149 540 : if (header->nextBytes == kInvalidHeapBlockSize)
150 : {
151 27 : continue;
152 : }
153 :
154 513 : if (header->state != kHeapBlockFree)
155 : {
156 403 : continue; // not free
157 : }
158 :
159 110 : if (header->nextBytes < size)
160 : {
161 7 : continue; // insufficient space
162 : }
163 :
164 103 : if (header->nextBytes - size < sizeof(PrivateHeapBlockHeader) + kPrivateHeapAllocationAlignment)
165 : {
166 : // allocate the entire block
167 61 : header->state = kHeapBlockInUse;
168 61 : header->checksum = ComputeHeapBlockChecksum(header);
169 : }
170 : else
171 : {
172 : // splits the block into two
173 : //
174 : // +--------+ +--------+ +------+
175 : // | header | ---> | middle | ---> | next |
176 : // +--------+ +--------+ +------+
177 : //
178 42 : PrivateHeapBlockHeader * next = NextHeader(header);
179 42 : PrivateHeapBlockHeader * middle =
180 42 : reinterpret_cast<PrivateHeapBlockHeader *>(reinterpret_cast<char *>(header + 1) + size);
181 :
182 : // middle is a new block
183 42 : middle->nextBytes = static_cast<uint32_t>(header->nextBytes - size - sizeof(PrivateHeapBlockHeader));
184 42 : middle->prevBytes = static_cast<uint32_t>(size);
185 42 : middle->state = kHeapBlockFree;
186 42 : middle->checksum = ComputeHeapBlockChecksum(middle);
187 :
188 : // fix up the header
189 42 : header->nextBytes = static_cast<uint32_t>(size);
190 42 : header->state = kHeapBlockInUse;
191 42 : header->checksum = ComputeHeapBlockChecksum(header);
192 :
193 : // fix up the final block
194 42 : if (next != nullptr)
195 : {
196 42 : next->prevBytes = middle->nextBytes;
197 42 : next->checksum = ComputeHeapBlockChecksum(next);
198 : }
199 : }
200 :
201 : // we can now use the header
202 103 : return header + 1; // data right after the header
203 : }
204 :
205 : // no space found
206 27 : return nullptr;
207 : }
208 :
209 103 : extern "C" void PrivateHeapFree(void * ptr)
210 : {
211 103 : if (ptr == nullptr)
212 : {
213 : // freeing NULL pointers is always acceptable and a noop
214 0 : return;
215 : }
216 :
217 103 : PrivateHeapBlockHeader * header =
218 : reinterpret_cast<PrivateHeapBlockHeader *>(static_cast<char *>(ptr) - sizeof(PrivateHeapBlockHeader));
219 :
220 103 : ValidateHeader(header);
221 103 : header->state = kHeapBlockFree;
222 103 : header->checksum = ComputeHeapBlockChecksum(header);
223 :
224 : // Merge with previous
225 : //
226 : // +-------+ +--------+
227 : // | other | ----- nextBytes -----> | header |
228 : // +-------+ +--------+
229 : //
230 103 : PrivateHeapBlockHeader * other = PreviousHeader(header);
231 103 : if (other != nullptr && other->state == kHeapBlockFree && other->nextBytes != kInvalidHeapBlockSize)
232 : {
233 : // includes the free bytes in this block in the previous
234 20 : other->nextBytes += static_cast<uint32_t>(header->nextBytes + sizeof(PrivateHeapBlockHeader));
235 20 : other->checksum = ComputeHeapBlockChecksum(other);
236 20 : header->state = kInvalidHeaderState;
237 20 : header = other;
238 :
239 : // fixes up the next block
240 20 : other = NextHeader(header);
241 20 : if (other != nullptr)
242 : {
243 20 : other->prevBytes = header->nextBytes;
244 20 : other->checksum = ComputeHeapBlockChecksum(other);
245 : }
246 : }
247 :
248 : // Merge with next
249 : //
250 : // +--------+ +-------+
251 : // | header | ----- nextBytes -----> | other |
252 : // +--------+ +-------+
253 : //
254 103 : other = NextHeader(header);
255 103 : if (other != nullptr && other->state == kHeapBlockFree && other->nextBytes != kInvalidHeapBlockSize)
256 : {
257 : // includes the free bytes in the next block
258 22 : other->state = kInvalidHeaderState;
259 22 : header->nextBytes += static_cast<uint32_t>(other->nextBytes + sizeof(PrivateHeapBlockHeader));
260 22 : header->checksum = ComputeHeapBlockChecksum(header);
261 :
262 : // fixes up the next block
263 22 : other = NextHeader(header);
264 22 : if (other != nullptr)
265 : {
266 22 : other->prevBytes = header->nextBytes;
267 22 : other->checksum = ComputeHeapBlockChecksum(other);
268 : }
269 : }
270 : }
271 :
272 7 : void * PrivateHeapRealloc(void * heap, void * ptr, size_t size)
273 : {
274 7 : if (ptr == nullptr)
275 : {
276 1 : return PrivateHeapAlloc(heap, size);
277 : }
278 :
279 6 : if (size == 0)
280 : {
281 0 : PrivateHeapFree(ptr);
282 0 : return nullptr;
283 : }
284 :
285 6 : PrivateHeapBlockHeader * header =
286 : reinterpret_cast<PrivateHeapBlockHeader *>(static_cast<char *>(ptr) - sizeof(PrivateHeapBlockHeader));
287 :
288 6 : ValidateHeader(header);
289 :
290 6 : if (header->nextBytes >= size)
291 : {
292 3 : return ptr; // no reallocation needed
293 : }
294 :
295 3 : void * largerCopy = PrivateHeapAlloc(heap, size);
296 3 : if (largerCopy == nullptr)
297 : {
298 : // NOTE: original is left untouched (not freed) to match realloc() libc
299 : // functionality
300 2 : return nullptr;
301 : }
302 :
303 1 : memcpy(largerCopy, ptr, header->nextBytes);
304 1 : PrivateHeapFree(ptr);
305 :
306 1 : return largerCopy;
307 : }
308 :
309 0 : extern "C" void PrivateHeapDump(void * top)
310 : {
311 0 : PrivateHeapBlockHeader * header = reinterpret_cast<PrivateHeapBlockHeader *>(top);
312 :
313 0 : ChipLogProgress(Support, "========= HEAP ===========");
314 0 : while (header->nextBytes != kInvalidHeapBlockSize)
315 : {
316 0 : ChipLogProgress(Support, " %td: size: %d, state: %d", reinterpret_cast<char *>(header) - reinterpret_cast<char *>(top),
317 : static_cast<int>(header->nextBytes), static_cast<int>(header->state));
318 :
319 0 : header = NextHeader(header);
320 : }
321 0 : }
|