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