1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef CHRE_PLATFORM_SHARED_MEMORY_DEBUG_H_
18 #define CHRE_PLATFORM_SHARED_MEMORY_DEBUG_H_
19
20 /**
21 * @file
22 * Debug-only code that can be used to catch dynamic memory errors at runtime.
23 * To use, replace calls to malloc/free (or their equivalents) with:
24 *
25 * void *buf = debugAlloc(malloc, allocSize);
26 * ..
27 * debugFree(free, buf);
28 */
29
30 #include <string.h>
31 #include <cstddef>
32 #include <cstdint>
33
34 #include "chre/platform/fatal_error.h"
35
36 namespace chre {
37
38 constexpr uint64_t kMagicAllocated = UINT64_C(0x997af173b998b686);
39 constexpr uint64_t kMagicFreed = UINT64_C(0x1a16b89bf3d69842);
40 static_assert(sizeof(kMagicAllocated) == sizeof(kMagicFreed),
41 "Trailer magic values need to be the same size");
42
43 constexpr size_t kMaxAllocSize = 8 * 1024 * 1024; // 8 MiB
44 constexpr uint32_t kPreambleGuard = UINT32_C(0x16b89bf3);
45
alignas(alignof (max_align_t))46 struct alignas(alignof(max_align_t)) MemoryDebugPreamble {
47 size_t allocSize;
48 uint32_t guard;
49 };
50
51 typedef void *(MemoryAllocFunction)(size_t);
52 typedef void(MemoryFreeFunction)(void *);
53
54 /**
55 * Allocate memory, and prepend + append debugging information to help detect
56 * dynamic memory errors (double-free, buffer overflow, etc).
57 *
58 * @param allocFunc Function to use to allocate memory (e.g. malloc)
59 * @param size Number of bytes to allocate
60 *
61 * @return Aligned memory buffer for caller to use directly; note that
62 * this pointer must be given to debugFree to release it
63 */
debugAlloc(MemoryAllocFunction * allocFunc,size_t size)64 inline void *debugAlloc(MemoryAllocFunction *allocFunc, size_t size) {
65 constexpr size_t kDebugOverhead =
66 sizeof(MemoryDebugPreamble) + sizeof(kMagicAllocated);
67
68 void *mem = nullptr;
69 if (size > kMaxAllocSize) {
70 LOGE("Invalid allocation size %zu (max %zu)", size, kMaxAllocSize);
71 } else {
72 mem = allocFunc(size + kDebugOverhead);
73 if (mem != nullptr) {
74 // Prepend size of the allocation
75 auto *preamble = static_cast<MemoryDebugPreamble *>(mem);
76 preamble->allocSize = size;
77 preamble->guard = kPreambleGuard;
78 mem = static_cast<void *>(preamble + 1);
79
80 // Append a guard
81 uint8_t *trailer = static_cast<uint8_t *>(mem) + size;
82 memcpy(trailer, &kMagicAllocated, sizeof(kMagicAllocated));
83 }
84 }
85
86 return mem;
87 }
88
89 /**
90 * Free memory previously allocated from debugAlloc, but first perform some
91 * consistency checks, triggering ERR_FATAL if they fail.
92 *
93 * @param freeFunc Function to call to actually free memory (e.g. free)
94 * @param pointer Pointer previously returned by debugAlloc
95 */
debugFree(MemoryFreeFunction * freeFunc,void * pointer)96 inline void debugFree(MemoryFreeFunction *freeFunc, void *pointer) {
97 if (pointer != nullptr) {
98 auto *preamble = static_cast<MemoryDebugPreamble *>(pointer) - 1;
99 if (preamble->allocSize > kMaxAllocSize) {
100 FATAL_ERROR("Invalid allocation size %zu", preamble->allocSize);
101 } else if (preamble->guard != kPreambleGuard) {
102 FATAL_ERROR("Corruption of preamble detected");
103 }
104
105 uint8_t *trailer = static_cast<uint8_t *>(pointer) + preamble->allocSize;
106 if (memcmp(trailer, &kMagicFreed, sizeof(kMagicFreed)) == 0) {
107 FATAL_ERROR("Double-free detected");
108 } else if (memcmp(trailer, &kMagicAllocated, sizeof(kMagicAllocated)) !=
109 0) {
110 FATAL_ERROR("Buffer overflow detected (or maybe double free)");
111 }
112
113 memcpy(trailer, &kMagicFreed, sizeof(kMagicFreed));
114 freeFunc(preamble);
115 }
116 }
117
118 } // namespace chre
119
120 #endif // CHRE_PLATFORM_SHARED_MEMORY_DEBUG_H_
121