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 45static struct MallocDispatchType g_dfxCustomMallocDispatch; 46static const size_t PAGE_INFO_SIZE = ((sizeof(PageInfo) + ALIGN_SIZE - 1) & ~(ALIGN_SIZE - 1)); 47static const char DFX_MEM_PAGE_SIGN[DFX_MEMPOOL_TAG_SIZE] = {'D', 'F', 'X', 1}; 48static DfxAllocator g_dfxAllocator = { 49 .initFlag = 0, 50 .pageList = NULL, 51}; 52 53static inline uintptr_t PageStart(uintptr_t addr) 54{ 55 return (addr & DFX_PAGE_MASK); 56} 57 58static inline uintptr_t PageEnd(uintptr_t addr) 59{ 60 return PageStart(addr + (DFX_PAGE_SIZE - 1)); 61} 62 63static 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 72static 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 85static 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 103static void MempoolAddPage(DfxMempool* mempool, PageInfo* page) 104{ 105 if (mempool == NULL) { 106 DFXLOGE("MempoolAddPage Invalid mempool!"); 107 return; 108 } 109 return AddPage(&(mempool->pageList), page); 110} 111 112static void MempoolRemovePage(DfxMempool* mempool, PageInfo* page) 113{ 114 if (mempool == NULL) { 115 DFXLOGE("MempoolRemovePage Invalid mempool!"); 116 return; 117 } 118 return RemovePage(&(mempool->pageList), page); 119} 120 121static 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 DFXLOGE("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 DFXLOGE("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 150static void MempoolFreePage(DfxMempool* mempool, PageInfo* page) 151{ 152 if (page->freeBlocksCnt != mempool->blocksPerPage) { 153 DFXLOGE("MempoolFreePage Invalid Page free cnt!"); 154 return; 155 } 156 MempoolRemovePage(mempool, page); 157 munmap(page, DFX_PAGE_SIZE); 158 mempool->freePagesCnt--; 159} 160 161static 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 DFXLOGE("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 206static 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 DFXLOGE("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 235static 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 249static 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 262static inline PageInfo* GetPageUnchecked(void* ptr) 263{ 264 uintptr_t pageHead = PageStart((uintptr_t)(ptr) - PAGE_INFO_SIZE); 265 return (PageInfo*)(pageHead); 266} 267 268static 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 DFXLOGE("GetPage untagged address!"); 273 return NULL; 274 } 275 return page; 276} 277 278static 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 DFXLOGE("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 DFXLOGE("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 DFXLOGE("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 314static 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 334static 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 343static size_t GetChunkSize(void* ptr) 344{ 345 if (ptr == NULL) { 346 DFXLOGE("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 DFXLOGE("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 361static 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 DFXLOGE("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 383static 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 DFXLOGE("DfxRealloc memcpy fail"); 403 } 404 DfxFree(ptr); 405 return res; 406 } 407 } 408 return ptr; 409} 410 411static void* HookMalloc(size_t size) 412{ 413 return DfxAlloc(size); 414} 415 416static void* HookCalloc(size_t count, size_t size) 417{ 418 return DfxAlloc(count * size); 419} 420 421static void* HookRealloc(void* ptr, size_t size) 422{ 423 return DfxRealloc(ptr, size); 424} 425 426static void HookFree(void* ptr) 427{ 428 return DfxFree(ptr); 429} 430 431void 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 DFXLOGE("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 451void 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 460DfxAllocator* GetDfxAllocator(void) 461{ 462 return &g_dfxAllocator; 463} 464 465int IsDfxAllocatorMem(void* ptr) 466{ 467 if (!ptr) { 468 return 0; 469 } 470 if (GetPage(ptr)) { 471 return 1; 472 } 473 return 0; 474} 475