1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "dfx_allocator.h"
16 
17 #include <fcntl.h>
18 #include <securec.h>
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <sys/cdefs.h>
24 #include <sys/mman.h>
25 #include <sys/prctl.h>
26 #include <sys/stat.h>
27 #include <sys/user.h>
28 
29 #include "dfx_define.h"
30 #include "dfx_log.h"
31 #include "musl_malloc_dispatch_table.h"
32 #define HOOK_ENABLE
33 #include "musl_preinit_common.h"
34 
35 #define ALIGN_SIZE 16
36 
37 #define DFX_PAGE_SIZE 4096
38 #define DFX_PAGE_MASK (~(DFX_PAGE_SIZE - 1))
39 
40 #define DFX_MEMPOOL_MIN_TYPE 4
41 #define DFX_MEMPOOL_MAX_TYPE 10
42 #define DFX_MEMPOOL_MAX_BLOCK_SIZE 1024
43 #define DFX_MMAP_TYPE 111
44 
45 static struct MallocDispatchType g_dfxCustomMallocDispatch;
46 static const size_t PAGE_INFO_SIZE = ((sizeof(PageInfo) + ALIGN_SIZE - 1) & ~(ALIGN_SIZE - 1));
47 static const char DFX_MEM_PAGE_SIGN[DFX_MEMPOOL_TAG_SIZE] = {'D', 'F', 'X', 1};
48 static DfxAllocator g_dfxAllocator = {
49     .initFlag = 0,
50     .pageList = NULL,
51 };
52 
PageStart(uintptr_t addr)53 static inline uintptr_t PageStart(uintptr_t addr)
54 {
55     return (addr & DFX_PAGE_MASK);
56 }
57 
PageEnd(uintptr_t addr)58 static inline uintptr_t PageEnd(uintptr_t addr)
59 {
60     return PageStart(addr + (DFX_PAGE_SIZE - 1));
61 }
62 
AlignRoundUp(size_t val,size_t align)63 static inline size_t AlignRoundUp(size_t val, size_t align)
64 {
65     size_t size = align;
66     if (size == 0) {
67         size = 1;
68     }
69     return ((val + size - 1) & ~(size - 1));
70 }
71 
AddPage(PageInfo ** pageList,PageInfo * page)72 static void AddPage(PageInfo** pageList, PageInfo* page)
73 {
74     if (pageList == NULL || page == NULL) {
75         return;
76     }
77     page->next = *pageList;
78     page->prev = NULL;
79     if (*pageList) {
80         (*pageList)->prev = page;
81     }
82     *pageList = page;
83 }
84 
RemovePage(PageInfo ** pageList,PageInfo * page)85 static void RemovePage(PageInfo** pageList, PageInfo* page)
86 {
87     if (pageList == NULL || page == NULL) {
88         return;
89     }
90     if (page->prev) {
91         page->prev->next = page->next;
92     }
93     if (page->next) {
94         page->next->prev = page->prev;
95     }
96     if (*pageList == page) {
97         *pageList = page->next;
98     }
99     page->prev = NULL;
100     page->next = NULL;
101 }
102 
MempoolAddPage(DfxMempool * mempool,PageInfo * page)103 static void MempoolAddPage(DfxMempool* mempool, PageInfo* page)
104 {
105     if (mempool == NULL) {
106         DFXLOG_ERROR("MempoolAddPage Invalid mempool!");
107         return;
108     }
109     return AddPage(&(mempool->pageList), page);
110 }
111 
MempoolRemovePage(DfxMempool * mempool,PageInfo * page)112 static void MempoolRemovePage(DfxMempool* mempool, PageInfo* page)
113 {
114     if (mempool == NULL) {
115         DFXLOG_ERROR("MempoolRemovePage Invalid mempool!");
116         return;
117     }
118     return RemovePage(&(mempool->pageList), page);
119 }
120 
MempoolAllocPage(DfxMempool * mempool)121 static void MempoolAllocPage(DfxMempool* mempool)
122 {
123     void* mptr = mmap(NULL, DFX_PAGE_SIZE, PROT_READ | PROT_WRITE,
124         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
125     if (mptr == MAP_FAILED) {
126         DFXLOG_ERROR("Mempool AllocPage mmap failed!");
127         return;
128     }
129     PageInfo* page = (PageInfo*)(mptr);
130     // fill PageInfo
131     if (memcpy_s(page->tag.tagInfo, sizeof(page->tag.tagInfo),
132         DFX_MEM_PAGE_SIGN, sizeof(DFX_MEM_PAGE_SIGN)) != EOK) {
133         munmap(mptr, DFX_PAGE_SIZE);
134         DFXLOG_ERROR("Mempool AllocPage fill tag failed!");
135         return;
136     }
137     page->tag.type = mempool->type;
138     page->tag.mempool = mempool;
139     page->freeBlocksCnt = mempool->blocksPerPage;
140     // Page firstblockaddr = page start addr + PageInfo size round up by blocksize
141     uintptr_t firstBlockAddr = AlignRoundUp((uintptr_t)(page + 1), mempool->blockSize);
142     BlockInfo* firstBlock = (BlockInfo*)(firstBlockAddr);
143     // here first block is not alloced block,freeblockList point it
144     firstBlock->freeBlocksCnt = mempool->blocksPerPage;
145     page->freeBlockList = firstBlock;
146     MempoolAddPage(mempool, page);
147     mempool->freePagesCnt++;
148 }
149 
MempoolFreePage(DfxMempool * mempool,PageInfo * page)150 static void MempoolFreePage(DfxMempool* mempool, PageInfo* page)
151 {
152     if (page->freeBlocksCnt != mempool->blocksPerPage) {
153         DFXLOG_ERROR("MempoolFreePage Invalid Page free cnt!");
154         return;
155     }
156     MempoolRemovePage(mempool, page);
157     munmap(page, DFX_PAGE_SIZE);
158     mempool->freePagesCnt--;
159 }
160 
MempoolAlloc(DfxMempool * mempool)161 static void* MempoolAlloc(DfxMempool* mempool)
162 {
163     if (mempool == NULL || mempool->blockSize == 0) {
164         return NULL;
165     }
166     // first alloc memory block, or all page memory blocks have been allocated
167     if (mempool->pageList == NULL) {
168         MempoolAllocPage(mempool);
169     }
170     PageInfo* page = mempool->pageList;
171     if (page == NULL || page->freeBlockList == NULL) {
172         DFXLOG_ERROR("MempoolAlloc Alloc Page Failed or Invalid blocklist!");
173         return NULL;
174     }
175     BlockInfo* block = page->freeBlockList;
176     if (block->freeBlocksCnt > 1) {
177         // freeBlocksCnt > 1 stand for page have free block's room
178         // when a block will be allocated,
179         // freeblocklist have no node, need alloc a new block in page room ,then add to freeblocklist
180         BlockInfo* nextfree = (BlockInfo*)((uint8_t*)(block) + mempool->blockSize);
181         nextfree->next = block->next;
182         nextfree->freeBlocksCnt = block->freeBlocksCnt - 1;
183         page->freeBlockList = nextfree;
184     } else {
185         // last freed block in freeblocklist or the last one block in page
186         page->freeBlockList = block->next;
187     }
188 
189     // new alloc page, free cnt -1 when used, free cnt +1 when allocated
190     if (page->freeBlocksCnt == mempool->blocksPerPage) {
191         mempool->freePagesCnt--;
192     }
193     if (page->freeBlocksCnt > 0) {
194         page->freeBlocksCnt--;
195     }
196     (void)memset_s(block, mempool->blockSize, 0, mempool->blockSize);
197     // when page's blocks all allocated, remove from pagelist but not free
198     // then pagelist will be point page which have unused blocks
199     // or pagelist be NULL will alloc new page
200     if (page->freeBlocksCnt == 0) {
201         MempoolRemovePage(mempool, page);
202     }
203     return (void*)block;
204 }
205 
MempoolFree(DfxMempool * mempool,void * ptr)206 static void MempoolFree(DfxMempool* mempool, void* ptr)
207 {
208     PageInfo * const page = (PageInfo*)(PageStart((uintptr_t)(ptr)));
209 
210     if (mempool == NULL || ptr == NULL || mempool->blockSize == 0 ||
211         ((uintptr_t)(ptr)) % (mempool->blockSize) != 0) {
212         DFXLOG_ERROR("MempoolFree Invalid mempool or address!");
213         return;
214     }
215     // find ptr's page,and page's freeblocklist
216     (void)memset_s(ptr, mempool->blockSize, 0, mempool->blockSize);
217     BlockInfo* block = (BlockInfo*)(ptr);
218     block->next = page->freeBlockList;
219     block->freeBlocksCnt = 1;
220     page->freeBlockList = block;
221     page->freeBlocksCnt++;
222 
223     // all page's blocks have been freed, unmap the page
224     // page have only one block be freed, need add page to pageList
225     if (page->freeBlocksCnt == mempool->blocksPerPage) {
226         mempool->freePagesCnt++;
227         if (mempool->freePagesCnt >= 1) {
228             MempoolFreePage(mempool, page);
229         }
230     } else if (page->freeBlocksCnt == 1) {
231         MempoolAddPage(mempool, page);
232     }
233 }
234 
SelectMempoolType(size_t num)235 static inline uint32_t SelectMempoolType(size_t num)
236 {
237     uint32_t res = 0;
238     size_t n = num - 1;
239     // alloc size(1~1024), use diffrent block size mempool
240     // The 16-byte size range is the smallest.
241     // Each subsequent level is twice that of the previous level. The maximum is 1024 bytes
242     while (n != 0) {
243         res++;
244         n >>= 1;
245     }
246     return res;
247 }
248 
InitDfxAllocator(void)249 static void InitDfxAllocator(void)
250 {
251     for (uint32_t i = 0; i < DFX_MEMPOOLS_NUM; i++) {
252         g_dfxAllocator.dfxMempoolBuf[i].type = i + DFX_MEMPOOL_MIN_TYPE;
253         g_dfxAllocator.dfxMempoolBuf[i].blockSize = (1UL << g_dfxAllocator.dfxMempoolBuf[i].type);
254         g_dfxAllocator.dfxMempoolBuf[i].blocksPerPage =
255             (DFX_PAGE_SIZE - sizeof(PageInfo)) / (g_dfxAllocator.dfxMempoolBuf[i].blockSize);
256         g_dfxAllocator.dfxMempoolBuf[i].freePagesCnt = 0;
257         g_dfxAllocator.dfxMempoolBuf[i].pageList = NULL;
258     }
259     g_dfxAllocator.initFlag = 1;
260 }
261 
GetPageUnchecked(void * ptr)262 static inline PageInfo* GetPageUnchecked(void* ptr)
263 {
264     uintptr_t pageHead = PageStart((uintptr_t)(ptr) - PAGE_INFO_SIZE);
265     return (PageInfo*)(pageHead);
266 }
267 
GetPage(void * ptr)268 static inline PageInfo* GetPage(void* ptr)
269 {
270     PageInfo* page = GetPageUnchecked(ptr);
271     if (memcmp(page->tag.tagInfo, DFX_MEM_PAGE_SIGN, sizeof(DFX_MEM_PAGE_SIGN)) != 0) {
272         DFXLOG_ERROR("GetPage untagged address!");
273         return NULL;
274     }
275     return page;
276 }
277 
AllocMmap(size_t align,size_t size)278 static void* AllocMmap(size_t align, size_t size)
279 {
280     const size_t headSize = AlignRoundUp(PAGE_INFO_SIZE, align);
281     size_t allocSize = 0;
282 
283     // mmap size page allign
284     if (__builtin_add_overflow(headSize, size, &allocSize) ||
285         PageEnd(allocSize) < allocSize) {
286         DFXLOG_ERROR("Invalid mmap size!");
287         return NULL;
288     }
289     allocSize = PageEnd(allocSize);
290     void* mptr = mmap(NULL, allocSize, PROT_READ | PROT_WRITE,
291         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
292     if (mptr == MAP_FAILED) {
293         DFXLOG_ERROR("AllocMmap failed!");
294         return NULL;
295     }
296     void* result = (void*)((char*)(mptr) + headSize);
297     PageInfo* page = GetPageUnchecked(result);
298     if (memcpy_s(page->tag.tagInfo, sizeof(page->tag.tagInfo),
299         DFX_MEM_PAGE_SIGN, sizeof(DFX_MEM_PAGE_SIGN)) != EOK) {
300         munmap(mptr, allocSize);
301         DFXLOG_ERROR("AllocMmap fill tag failed!");
302         return NULL;
303     }
304     page->tag.type = DFX_MMAP_TYPE;
305     page->tag.mMapAllocSize = allocSize;
306     page->prev = NULL;
307     page->next = NULL;
308     page->freeBlockList = NULL;
309     page->freeBlocksCnt = 0;
310     AddPage(&(g_dfxAllocator.pageList), page);
311     return result;
312 }
313 
AllocImpl(size_t align,size_t size)314 static void* AllocImpl(size_t align, size_t size)
315 {
316     uint32_t type = 0;
317     DfxMempool* mempool = NULL;
318 
319     if (g_dfxAllocator.initFlag == 0) {
320         InitDfxAllocator();
321     }
322     if (size > DFX_MEMPOOL_MAX_BLOCK_SIZE) {
323         return AllocMmap(align, size);
324     }
325 
326     type = SelectMempoolType(size);
327     if (type < DFX_MEMPOOL_MIN_TYPE) {
328         type = DFX_MEMPOOL_MIN_TYPE;
329     }
330     mempool = &(g_dfxAllocator.dfxMempoolBuf[type - DFX_MEMPOOL_MIN_TYPE]);
331     return MempoolAlloc(mempool);
332 }
333 
DfxAlloc(size_t size)334 static void* DfxAlloc(size_t size)
335 {
336     size_t realSize = size;
337     if (size == 0) {
338         realSize = 1;
339     }
340     return AllocImpl(ALIGN_SIZE, realSize);
341 }
342 
GetChunkSize(void * ptr)343 static size_t GetChunkSize(void* ptr)
344 {
345     if (ptr == NULL) {
346         DFXLOG_ERROR("GetChunkSize Invalid ptr!");
347         return 0;
348     }
349     PageInfo* page = GetPage(ptr);
350     if (page == NULL || (page->tag.type != DFX_MMAP_TYPE &&
351         (page->tag.type < DFX_MEMPOOL_MIN_TYPE || page->tag.type > DFX_MEMPOOL_MAX_TYPE))) {
352         DFXLOG_ERROR("GetChunkSize Invalid page!");
353         return 0;
354     }
355     if (page->tag.type == DFX_MMAP_TYPE) {
356         return page->tag.mMapAllocSize - (size_t)((uintptr_t)(ptr) - (uintptr_t)(page));
357     }
358     return g_dfxAllocator.dfxMempoolBuf[page->tag.type - DFX_MEMPOOL_MIN_TYPE].blockSize;
359 }
360 
DfxFree(void * ptr)361 static void DfxFree(void* ptr)
362 {
363     if (g_dfxAllocator.initFlag == 0) {
364         return;
365     }
366     if (ptr == NULL) {
367         return;
368     }
369     PageInfo* page = GetPage(ptr);
370     if (page == NULL) {
371         DFXLOG_ERROR("DfxFree Invalid page!");
372         return;
373     }
374     if (page->tag.type == DFX_MMAP_TYPE) {
375         RemovePage(&(g_dfxAllocator.pageList), page);
376         munmap(page, page->tag.mMapAllocSize);
377     } else if (page->tag.type <= DFX_MEMPOOL_MAX_TYPE && page->tag.type >= DFX_MEMPOOL_MIN_TYPE) {
378         DfxMempool* mempool = &(g_dfxAllocator.dfxMempoolBuf[page->tag.type - DFX_MEMPOOL_MIN_TYPE]);
379         MempoolFree(mempool, ptr);
380     }
381 }
382 
DfxRealloc(void * ptr,size_t size)383 static void* DfxRealloc(void* ptr, size_t size)
384 {
385     if (g_dfxAllocator.initFlag == 0) {
386         InitDfxAllocator();
387     }
388     if (ptr == NULL) {
389         return DfxAlloc(size);
390     }
391     if (size == 0) {
392         return NULL;
393     }
394     size_t oldsize = GetChunkSize(ptr);
395     if (oldsize == 0) {
396         return NULL;
397     }
398     if (oldsize < size) {
399         void* res = DfxAlloc(size);
400         if (res) {
401             if (memcpy_s(res, size, ptr, oldsize) != EOK) {
402                 DFXLOG_ERROR("DfxRealloc memcpy fail");
403             }
404             DfxFree(ptr);
405             return res;
406         }
407     }
408     return ptr;
409 }
410 
HookMalloc(size_t size)411 static void* HookMalloc(size_t size)
412 {
413     return DfxAlloc(size);
414 }
415 
HookCalloc(size_t count,size_t size)416 static void* HookCalloc(size_t count, size_t size)
417 {
418     return DfxAlloc(count * size);
419 }
420 
HookRealloc(void * ptr,size_t size)421 static void* HookRealloc(void* ptr, size_t size)
422 {
423     return DfxRealloc(ptr, size);
424 }
425 
HookFree(void * ptr)426 static void HookFree(void* ptr)
427 {
428     return DfxFree(ptr);
429 }
430 
RegisterAllocator(void)431 void RegisterAllocator(void)
432 {
433 #ifndef DFX_ALLOCATE_ASAN
434     if (memcpy_s(&g_dfxCustomMallocDispatch, sizeof(g_dfxCustomMallocDispatch),
435         &(__libc_malloc_default_dispatch), sizeof(__libc_malloc_default_dispatch)) != EOK) {
436         DFXLOG_ERROR("RegisterAllocator memcpy fail");
437     }
438 #endif
439     g_dfxCustomMallocDispatch.malloc = HookMalloc;
440     g_dfxCustomMallocDispatch.calloc = HookCalloc;
441     g_dfxCustomMallocDispatch.realloc = HookRealloc;
442     g_dfxCustomMallocDispatch.free = HookFree;
443 #ifndef DFX_ALLOCATE_ASAN
444     atomic_store_explicit(&ohos_malloc_hook_shared_library, -1, memory_order_seq_cst);
445     atomic_store_explicit(&__hook_enable_hook_flag, true, memory_order_seq_cst);
446     atomic_store_explicit(&__musl_libc_globals.current_dispatch_table,
447         (volatile const long long)&g_dfxCustomMallocDispatch, memory_order_seq_cst);
448 #endif
449 }
450 
UnregisterAllocator(void)451 void UnregisterAllocator(void)
452 {
453 #ifndef DFX_ALLOCATE_ASAN
454     atomic_store_explicit(&ohos_malloc_hook_shared_library, 0, memory_order_seq_cst);
455     atomic_store_explicit(&__hook_enable_hook_flag, false, memory_order_seq_cst);
456     atomic_store_explicit(&__musl_libc_globals.current_dispatch_table, 0, memory_order_seq_cst);
457 #endif
458 }
459 
GetDfxAllocator(void)460 DfxAllocator* GetDfxAllocator(void)
461 {
462     return &g_dfxAllocator;
463 }
464 
IsDfxAllocatorMem(void * ptr)465 int IsDfxAllocatorMem(void* ptr)
466 {
467     if (!ptr) {
468         return 0;
469     }
470     if (GetPage(ptr)) {
471         return 1;
472     }
473     return 0;
474 }
475