#ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS #endif #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #ifdef __MINIX #define util_time_tsc_read_ns(x) 0 #define util_strhash(x, y) 0 #define util_stacktrace_hash() 0 #define util_stacktrace_print_custom(x) #define util_stacktrace_hash_skip(x) 0 #else #include #include #include #endif #define DEBUG MAGIC_DEBUG_SET(0) #define DEBUG_TYPE_SIZE_MISMATCH MAGIC_DEBUG_SET(0) #if DEBUG #define MAGIC_MEM_PRINTF _magic_printf #else #define MAGIC_MEM_PRINTF magic_null_printf #endif /* CPU frequency (used for timestamp logging) */ PUBLIC double magic_cycles_per_ns = 0; /* * External callbacks. */ PUBLIC magic_mem_heap_alloc_cb_t magic_mem_heap_alloc_cb = NULL; PUBLIC magic_mem_create_dsentry_cb_t magic_mem_create_dsentry_cb = NULL; PUBLIC magic_mem_heap_free_cb_t magic_mem_heap_free_cb = NULL; PUBLIC short magic_mem_create_dsentry_site_id = 0; PUBLIC THREAD_LOCAL short magic_mem_wrapper_active = 0; PUBLIC THREAD_LOCAL short magic_mempool_mgmt_active_level = 0; PUBLIC THREAD_LOCAL short magic_mempool_ids[MAGIC_MEMPOOL_MAX_FUNC_RECURSIONS]; char* const MAGIC_MEMPOOL_NAME_UNKNOWN = "_magic_mempool_unknown#"; char* const MAGIC_MEMPOOL_NAME_DETACHED = "_magic_mempool_detached#"; __attribute__((weak)) int magic_mempool_allow_reset = 1; __attribute__((weak)) int magic_mempool_allow_reuse = 1; __attribute__((weak)) int magic_mempool_allow_external_alloc = 0; #define MAGIC_MEM_FAILED ((void*) -1) #ifndef SHM_REMAP #define SHM_REMAP 0 #endif EXTERN char **environ; PRIVATE magic_dsentry_cb_t magic_destroy_dsentry_ext_cb = NULL; /* Magic real mem function definitions. */ PUBLIC void *(*magic_real_malloc)(size_t size) = &malloc; PUBLIC void *(*magic_real_calloc)(size_t nmemb, size_t size) = &calloc; PUBLIC void (*magic_real_free)(void *ptr) = &free; PUBLIC void *(*magic_real_realloc)(void *ptr, size_t size) = &realloc; PUBLIC int (*magic_real_posix_memalign)(void **memptr, size_t alignment, size_t size) = &posix_memalign; #ifndef __MINIX PUBLIC void *(*magic_real_valloc)(size_t size) = &valloc; PUBLIC void *(*magic_real_memalign)(size_t boundary, size_t size) = &memalign; #endif PUBLIC void *(*magic_real_mmap)(void *start, size_t length, int prot, int flags, int fd, off_t offset) = &mmap; PUBLIC int (*magic_real_munmap)(void *start, size_t length) = &munmap; PUBLIC int (*magic_real_brk)(void *addr) = &brk; PUBLIC void *(*magic_real_sbrk)(intptr_t increment) = &sbrk; #ifndef __MINIX PUBLIC void *(*magic_real_shmat)(int shmid, const void *shmaddr, int shmflg) = &shmat; PUBLIC int (*magic_real_shmdt)(const void *shmaddr) = &shmdt; PUBLIC void *(*magic_real_mmap64)(void *start, size_t length, int prot, int flags, int fd, off_t pgoffset) = &mmap64; #else PUBLIC void *(*magic_real_vm_map_cacheblock)(dev_t dev, off_t dev_offset, ino_t ino, off_t ino_offset, u32_t *flags, int blocksize) = &vm_map_cacheblock; #endif /* Use magic_real* functions in the rest of the file. */ #include /* Macros for memory usage logging. */ #if MAGIC_MEM_USAGE_OUTPUT_CTL #define MAGIC_MEM_DEBUG_PREFIX "MEM_USAGE: " #define TIMESTAMP_STR "%llu" #define TIMESTAMP_ARG (util_time_tsc_read_ns(magic_cycles_per_ns)) #define MAGIC_MEM_DEBUG_EVENT(EVENT, FORMAT, ...) \ _magic_printf(MAGIC_MEM_DEBUG_PREFIX TIMESTAMP_STR " " #EVENT " " FORMAT "\n", TIMESTAMP_ARG, __VA_ARGS__) #define MAGIC_MEM_DEBUG_EVENT_1(event, ptr) MAGIC_MEM_DEBUG_EVENT(event, "%u", (unsigned) ptr) #define MAGIC_MEM_DEBUG_EVENT_2(event, ptr, type) MAGIC_MEM_DEBUG_EVENT(event, "%u %lu", (unsigned) ptr, type) #define MAGIC_MEM_DEBUG_EVENT_3(event, ptr, type, size) MAGIC_MEM_DEBUG_EVENT(event, "%u %lu %d", (unsigned) ptr, type, size) #if (MAGIC_MEM_USAGE_OUTPUT_CTL == 1) /* use the hash of the name (extended with line number & file name) as dynamic type */ #define MAGIC_MEM_GET_DTYPE() util_strhash(0, name) #elif (MAGIC_MEM_USAGE_OUTPUT_CTL == 2) /* use the hash of the stacktrace as a dynamic type */ #define MAGIC_MEM_GET_DTYPE() util_stacktrace_hash() #endif #define MAGIC_MEM_DEBUG_ALLOC(ptr, size) MAGIC_MEM_DEBUG_EVENT_3(alloc, ptr, (MAGIC_MEMPOOL_MGMT_IS_ACTIVE() ? MAGIC_MEMPOOL_GET_DTYPE() : MAGIC_MEM_GET_DTYPE()), size) #define MAGIC_MEM_DEBUG_FREE(ptr) MAGIC_MEM_DEBUG_EVENT_1(dealloc, ptr) #define MAGIC_MEM_DEBUG_RESET(ptr) MAGIC_MEM_DEBUG_EVENT_1(reset, ptr) #define MAGIC_MEM_DEBUG_REUSE(ptr, type) MAGIC_MEM_DEBUG_EVENT_2(reuse, ptr, type) #else #define MAGIC_MEM_GET_DTYPE() 0 #define MAGIC_MEM_DEBUG_ALLOC(ptr, size) #define MAGIC_MEM_DEBUG_FREE(ptr) #define MAGIC_MEM_DEBUG_RESET(ptr) #define MAGIC_MEM_DEBUG_REUSE(ptr, type) #endif /*===========================================================================* * magic_mempool_alloc_id * *===========================================================================*/ MAGIC_MACRO_FUNC short magic_mempool_alloc_id() { short i, id = -1; MAGIC_MPDESC_LOCK(); /* Find a free slot. */ for(i = 0; i < MAGIC_MAX_MEMPOOLS; i++) { if(MAGIC_MPDESC_IS_FREE(&_magic_mpdescs[i])) { MAGIC_MPDESC_ALLOC(&_magic_mpdescs[i]); id = i + 1; break; } } MAGIC_MPDESC_UNLOCK(); assert((id > 0) && (id <= MAGIC_MAX_MEMPOOLS) && "Ran out of memory pool descriptors!"); return id; } /*===========================================================================* * magic_mempool_create_begin * *===========================================================================*/ MAGIC_HOOK void magic_mempool_create_begin(__MDEBUG_ARGS__) { MAGIC_MEMPOOL_MGMT_SET_ACTIVE(); assert(MAGIC_MEMPOOL_MGMT_IS_ACTIVE()); MAGIC_MEMPOOL_SET_ID(magic_mempool_alloc_id()); MAGIC_MEMPOOL_SET_DTYPE(MAGIC_MEM_GET_DTYPE()); } /*===========================================================================* * magic_mempool_create_end * *===========================================================================*/ MAGIC_HOOK void magic_mempool_create_end(void* addr, int indirection) { void* pool; pool = (indirection && addr) ? *((void**)addr) : addr; assert(pool && "Cannot have a NULL pool pointer."); _magic_mpdescs[MAGIC_MEMPOOL_GET_ID() - 1].addr = pool; magic_mempool_mgmt_end(); } /*===========================================================================* * magic_mempool_lookup_by_addr * *===========================================================================*/ MAGIC_MACRO_FUNC short magic_mempool_lookup_by_addr(void* addr) { short i, id = MAGIC_MEMPOOL_ID_UNKNOWN; if (addr) { for(i = 0; i < MAGIC_MAX_MEMPOOLS; i++) { if(_magic_mpdescs[i].addr == addr) { id = i + 1; break; } } } return id; } /*===========================================================================* * magic_mempool_reset * *===========================================================================*/ MAGIC_MACRO_FUNC void magic_mempool_reset(char* mempool_name, int reset_name) { struct _magic_dsentry *prev_dsentry, *dsentry, *block_dsentry; struct _magic_sentry* sentry; MAGIC_DSENTRY_LOCK(); MAGIC_DSENTRY_MEMPOOL_ALIVE_ITER(_magic_first_mempool_dsentry, prev_dsentry, dsentry, sentry, if (sentry->name == mempool_name) { block_dsentry = MAGIC_DSENTRY_NEXT_MEMBLOCK(dsentry); if (block_dsentry != NULL) { struct _magic_dsentry *tmp_block_dsentry = MAGIC_CAS(&MAGIC_DSENTRY_NEXT_MEMBLOCK(dsentry), block_dsentry, NULL); assert(tmp_block_dsentry == block_dsentry && "New blocks have been allocated from a reseted mempool!"); } if (reset_name) { char *tmp_name = MAGIC_CAS(&sentry->name, mempool_name, MAGIC_MEMPOOL_NAME_UNKNOWN); assert(tmp_name == mempool_name && "The name of the mempool has changed while being reseted!"); } MAGIC_MEM_DEBUG_RESET((char*)dsentry); } ); MAGIC_DSENTRY_UNLOCK(); } /*===========================================================================* * magic_mempool_destroy_begin * *===========================================================================*/ MAGIC_HOOK void magic_mempool_destroy_begin(void* addr, int memory_reuse) { magic_mempool_mgmt_begin(addr); if (addr && memory_reuse) { assert(MAGIC_MEMPOOL_ID_IS_SET() && "Cannot destroy a pool with an unknown id."); magic_mempool_reset(MAGIC_MEMPOOL_GET_NAME(), TRUE); } } /*===========================================================================* * magic_mempool_destroy_end * *===========================================================================*/ MAGIC_HOOK void magic_mempool_destroy_end() { MAGIC_MPDESC_LOCK(); MAGIC_MPDESC_FREE(&_magic_mpdescs[MAGIC_MEMPOOL_GET_ID() - 1]); MAGIC_MPDESC_UNLOCK(); magic_mempool_mgmt_end(); } /*===========================================================================* * magic_mempool_mgmt_begin * *===========================================================================*/ MAGIC_HOOK void magic_mempool_mgmt_begin(void* addr) { short id; MAGIC_MEMPOOL_MGMT_SET_ACTIVE(); assert(MAGIC_MEMPOOL_MGMT_IS_ACTIVE()); id = magic_mempool_lookup_by_addr(addr); /* For some reason, this mempool has not been registered yet, reserve a new slot in the mempool array. */ if (addr && (id == MAGIC_MEMPOOL_ID_UNKNOWN)) { id = magic_mempool_alloc_id(); _magic_mpdescs[id - 1].addr = addr; } MAGIC_MEMPOOL_SET_ID(id); } /*===========================================================================* * magic_mempool_mgmt_end * *===========================================================================*/ MAGIC_HOOK void magic_mempool_mgmt_end() { MAGIC_MEMPOOL_SET_ID(MAGIC_MEMPOOL_ID_UNKNOWN); assert(MAGIC_MEMPOOL_MGMT_IS_ACTIVE()); MAGIC_MEMPOOL_MGMT_UNSET_ACTIVE(); } /*===========================================================================* * magic_mempool_reset_begin * *===========================================================================*/ MAGIC_HOOK void magic_mempool_reset_begin(void* addr) { magic_mempool_mgmt_begin(addr); /* skip reset when it has been disabled by the application. */ if (magic_mempool_allow_reset) { if (addr != NULL) { assert(MAGIC_MEMPOOL_ID_IS_SET() && "Cannot reset a pool with an unknown id."); magic_mempool_reset(MAGIC_MEMPOOL_GET_NAME(), TRUE); } } } /*===========================================================================* * magic_mempool_dsentry_set_name * *===========================================================================*/ MAGIC_MACRO_FUNC void magic_mempool_dsentry_set_name(struct _magic_dsentry* dsentry, char* name) { char *old_name, *ret; if ((name == MAGIC_MEMPOOL_NAME_UNKNOWN) || (name == MAGIC_MEMPOOL_NAME_DETACHED)) { do { old_name = MAGIC_DSENTRY_TO_SENTRY(dsentry)->name; } while (MAGIC_CAS(&MAGIC_DSENTRY_TO_SENTRY(dsentry)->name, old_name, name) != old_name && old_name != name); } else { old_name = MAGIC_DSENTRY_TO_SENTRY(dsentry)->name; if (old_name != name) { if (!strncmp(old_name, MAGIC_MEMPOOL_NAME_PREFIX, strlen(MAGIC_MEMPOOL_NAME_PREFIX))) { assert(((old_name == MAGIC_MEMPOOL_NAME_UNKNOWN) || (old_name == MAGIC_MEMPOOL_NAME_DETACHED)) && "Cannot overwrite an already existing valid memory pool name!"); } ret = MAGIC_CAS(&MAGIC_DSENTRY_TO_SENTRY(dsentry)->name, old_name, name); assert((ret == old_name || ret == name) && "Cannot overwrite an already existing valid memory pool name!"); } } } /*===========================================================================* * magic_mempool_dsentry_update * *===========================================================================*/ MAGIC_MACRO_FUNC void magic_mempool_dsentry_update(struct _magic_dsentry* dsentry, char* name) { struct _magic_sentry* sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry); struct _magic_dsentry* next_mempool_dsentry; int flags; /* set the magic state mempool flag atomically */ do { flags = sentry->flags; } while (MAGIC_CAS(&sentry->flags, flags, flags | MAGIC_STATE_MEMPOOL) != flags && !(flags & MAGIC_STATE_MEMPOOL)); magic_mempool_dsentry_set_name(dsentry, name); /* the thread that updates the id adds the dsentry to the mempool dsentry list */ if (!(flags & MAGIC_STATE_MEMPOOL)) { /* Add the new dsentry before the first dsentry atomically. */ do { next_mempool_dsentry = _magic_first_mempool_dsentry; MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) = next_mempool_dsentry; } while(MAGIC_CAS(&_magic_first_mempool_dsentry, next_mempool_dsentry, dsentry) != next_mempool_dsentry); } } /*===========================================================================* * mempool_block_alloc_template * *===========================================================================*/ MAGIC_FUNC void *mempool_block_alloc_template(void* addr, size_t size) { return NULL; } /*===========================================================================* * magic_mempool_block_alloc_template * *===========================================================================*/ PUBLIC void *magic_mempool_block_alloc_template(__MA_ARGS__ void* addr, size_t size) { void *ptr, *data_ptr; struct _magic_sentry* mempool_sentry; struct _magic_dsentry* mempool_dsentry, *next_block_dsentry; magic_mempool_mgmt_begin(addr); /* don't set the memory wrapper flag, this function is supposed to allocate memory from a pool and not using the standard allocators. it might also call other memory pool management functions */ if(size > 0) { /* this call should be replaced with a call to a "real" block allocation function, when generating custom wrappers */ ptr = mempool_block_alloc_template(addr, MAGIC_SIZE_TO_REAL(size) + magic_asr_get_padding_size(MAGIC_STATE_HEAP)); MAGIC_MEM_PRINTF("%s: ptr = malloc(size) <-> 0x%08x = malloc(%d)\n", __FUNCTION__,(unsigned) ptr, MAGIC_SIZE_TO_REAL(size)); data_ptr = magic_alloc(__MA_VALUES__ ptr, size, MAGIC_STATE_HEAP | MAGIC_STATE_MEMBLOCK); if(data_ptr == MAGIC_MEM_FAILED) { /* cannot free individual blocks inside the pool */ data_ptr = NULL; errno = ENOMEM; } else if (ptr) { /* lookup the pool buffer dsentry from which this block was allocated */ mempool_sentry = magic_mempool_sentry_lookup_by_range(ptr, NULL); if (!mempool_sentry && magic_mempool_allow_external_alloc) { mempool_sentry = magic_sentry_lookup_by_range(ptr, NULL); if (mempool_sentry) { magic_mempool_dsentry_update(MAGIC_DSENTRY_FROM_SENTRY(mempool_sentry), MAGIC_MEMPOOL_GET_NAME()); } } assert(mempool_sentry && "XXX Mempool dsentry not found for this memblock dsentry: memory not allocated from a memory pool management function?"); mempool_dsentry = MAGIC_DSENTRY_FROM_SENTRY(mempool_sentry); /* Reuse of buffers across pools - propagate the new pool name */ if (MAGIC_MEMPOOL_ID_IS_SET() && (mempool_sentry->name != MAGIC_MEMPOOL_GET_NAME())) { assert(magic_mempool_allow_reuse && "Pool memory reuse is disabled!"); magic_mempool_dsentry_set_name(mempool_dsentry, MAGIC_MEMPOOL_GET_NAME()); } MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr)))->name = mempool_sentry->name; /* Add the new dsentry before the first block dsentry chained to the memory pool dsentry, atomically. The list should be circular - the last block dsentry (first one added) points to the memory pool dsentry. */ do { next_block_dsentry = MAGIC_DSENTRY_NEXT_MEMBLOCK(mempool_dsentry); MAGIC_DSENTRY_NEXT_MEMBLOCK(MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))) = next_block_dsentry ? next_block_dsentry : mempool_dsentry; }while(MAGIC_CAS(&(MAGIC_DSENTRY_NEXT_MEMBLOCK(mempool_dsentry)), next_block_dsentry, MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))) != next_block_dsentry); /* First write to this pool buffer, potential reuse */ if (next_block_dsentry == NULL) { MAGIC_MEM_DEBUG_REUSE((char*)mempool_dsentry, MAGIC_MEMPOOL_GET_DTYPE()); } } } else { /* Some applications return a valid pointer even if size is 0... */ data_ptr = mempool_block_alloc_template(addr, size); } magic_mempool_mgmt_end(); return data_ptr; } /*===========================================================================* * magic_create_dsentry * *===========================================================================*/ PUBLIC int magic_create_dsentry(struct _magic_dsentry *dsentry, void *data_ptr, struct _magic_type *type, size_t size, int flags, char *name, char *parent_name) { /* This function does not require any dsentry locking. */ struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry); struct _magic_dsentry *next_dsentry, *next_mempool_dsentry; size_t type_size; int is_varsized = 0; int num_vsa_elements = 0; struct _magic_dsentry *saved_next = dsentry->next; int save_linkage = ((flags & MAGIC_STATE_HEAP) && _magic_vars->fake_malloc); MAGIC_MEM_PRINTF("Dsentry created from stacktrace:\n"); if (MAGIC_MEM_PRINTF != magic_null_printf) { MAGIC_MEM_WRAPPER_BEGIN(); util_stacktrace_print_custom(MAGIC_MEM_PRINTF); MAGIC_MEM_WRAPPER_END(); } if (!type) { type = MAGIC_VOID_TYPE; } type_size = type->size; assert(size > 0); memcpy(dsentry, &magic_default_dsentry, sizeof(struct _magic_dsentry)); /* Catch variable-sized struct allocation. */ if (magic_type_alloc_needs_varsized_array(type, size, &num_vsa_elements)) { is_varsized = 1; } if (size % type_size != 0 && !is_varsized) { /* This should only happen for uncaught variable-sized struct allocations. */ #if DEBUG_TYPE_SIZE_MISMATCH _magic_printf("magic_create_dsentry: type <-> size mismatch, reverting to void type: size=%d, type=", size); MAGIC_TYPE_PRINT(type, MAGIC_EXPAND_TYPE_STR); _magic_printf("\n"); #endif type = MAGIC_VOID_TYPE; type_size = type->size; flags |= MAGIC_STATE_TYPE_SIZE_MISMATCH; } if (size == type_size && !is_varsized) { sentry->type = type; } else { struct _magic_type *array_type = &(dsentry->type); MAGIC_TYPE_ARRAY_CREATE_FROM_SIZE(array_type, type, MAGIC_DSENTRY_TO_TYPE_ARR(dsentry), size, num_vsa_elements); array_type->id = MAGIC_FAA(&_magic_types_next_id, 1); assert(_magic_types_next_id < MAGIC_ID_MAX); sentry->type = array_type; } sentry->flags |= flags; sentry->address = data_ptr; if (name) { sentry->name = name; } if (parent_name) { dsentry->parent_name = parent_name; } sentry->id = MAGIC_FAA(&_magic_sentries_next_id, 1); assert(_magic_sentries_next_id < MAGIC_ID_MAX); /* * TODO: Also add per-callsite index to handle the following: * for (;;) { p = malloc(); } */ if (magic_mem_create_dsentry_site_id) { MAGIC_MEM_WRAPPER_BEGIN(); /* * XXX: This is so damn ugly, but we don't want to include * any magic_* functions in the stacktrace hash. * This should probably be done in a much more elegant manner. */ dsentry->site_id = util_stacktrace_hash_skip((char *)"("MAGIC_PREFIX_STR); MAGIC_MEM_WRAPPER_END(); } if (save_linkage) { dsentry->next = saved_next; return 0; } /* Add the new dsentry before the first dsentry atomically. * Skip memblock dsentries to make pool reset/destruction faster. */ if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_MEMBLOCK)) { do { next_dsentry = _magic_first_dsentry; MAGIC_DSENTRY_NEXT(dsentry) = next_dsentry; } while(MAGIC_CAS(&_magic_first_dsentry, next_dsentry, dsentry) != next_dsentry); } #if MAGIC_DSENTRY_ALLOW_PREV next_dsentry = MAGIC_DSENTRY_NEXT(dsentry); if (next_dsentry) { MAGIC_DSENTRY_PREV(next_dsentry) = dsentry; } MAGIC_DSENTRY_PREV(dsentry) = NULL; #endif if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_MEMPOOL)) { /* Add the new dsentry before the first mempool dsentry atomically. */ do { next_mempool_dsentry = _magic_first_mempool_dsentry; MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) = next_mempool_dsentry; } while(MAGIC_CAS(&_magic_first_mempool_dsentry, next_mempool_dsentry, dsentry) != next_mempool_dsentry); } magic_update_dsentry_ranges = 1; if (magic_mem_create_dsentry_cb) magic_mem_create_dsentry_cb(dsentry); return 0; } /*===========================================================================* * magic_create_obdsentry * *===========================================================================*/ PUBLIC struct _magic_obdsentry* magic_create_obdsentry(void *data_ptr, struct _magic_type *type, size_t size, int flags, char *name, char *parent_name) { struct _magic_obdsentry *obdsentry = NULL; int i, ret; /* Check name. */ if(!name || !strcmp(name, "")) { return NULL; } else if(strlen(name) >= MAGIC_MAX_OBDSENTRY_NAME_LEN) { return NULL; } /* Check parent name. */ if(!parent_name || !strcmp(parent_name, "")) { parent_name = MAGIC_OBDSENTRY_DEFAULT_PARENT_NAME; } if(strlen(parent_name) >= MAGIC_MAX_OBDSENTRY_PARENT_NAME_LEN) { return NULL; } MAGIC_MEM_WRAPPER_LBEGIN(); /* Find a free slot. */ for(i=0;iname, name); strcpy(obdsentry->parent_name, parent_name); flags |= MAGIC_STATE_OUT_OF_BAND; ret = magic_create_dsentry(MAGIC_OBDSENTRY_TO_DSENTRY(obdsentry), data_ptr, type, size, flags, obdsentry->name, obdsentry->parent_name); MAGIC_MEM_WRAPPER_LEND(); if(ret < 0) { return NULL; } assert(!MAGIC_OBDSENTRY_IS_FREE(obdsentry)); return obdsentry; } /*===========================================================================* * magic_update_dsentry_state * *===========================================================================*/ PUBLIC int magic_update_dsentry_state(struct _magic_dsentry *dsentry, unsigned long state) { int ret = 0; unsigned long old_state; unsigned long num_dead_dsentries; unsigned long size, size_dead_dsentries; size = MAGIC_DSENTRY_TO_SENTRY(dsentry)->type->size; switch(state) { case MAGIC_DSENTRY_MSTATE_FREED: old_state = MAGIC_CAS(&dsentry->magic_state, MAGIC_DSENTRY_MSTATE_DEAD, MAGIC_DSENTRY_MSTATE_FREED); if(old_state != MAGIC_DSENTRY_MSTATE_DEAD) { ret = MAGIC_EBADMSTATE; break; } if (!MAGIC_STATE_FLAG(MAGIC_DSENTRY_TO_SENTRY(dsentry), MAGIC_STATE_MEMBLOCK)) { num_dead_dsentries = MAGIC_FAS(&magic_num_dead_dsentries, 1) - 1; size_dead_dsentries = MAGIC_FAS(&magic_size_dead_dsentries, size) - size; MAGIC_MEM_PRINTF("magic_update_dsentry_state: --magic_num_dead_dsentries (num=%d, size=%d)\n", num_dead_dsentries, size_dead_dsentries); } break; case MAGIC_DSENTRY_MSTATE_DEAD: old_state = MAGIC_CAS(&dsentry->magic_state, MAGIC_DSENTRY_MSTATE_ALIVE, MAGIC_DSENTRY_MSTATE_DEAD); if(old_state != MAGIC_DSENTRY_MSTATE_ALIVE) { ret = (old_state == MAGIC_DSENTRY_MSTATE_DEAD || old_state == MAGIC_DSENTRY_MSTATE_FREED) ? MAGIC_EBADMSTATE : MAGIC_EBADENT; break; } MAGIC_DSENTRY_TO_SENTRY(dsentry)->id = MAGIC_FAA(&_magic_sentries_next_id, 1); assert(_magic_sentries_next_id < MAGIC_ID_MAX); if (!MAGIC_STATE_FLAG(MAGIC_DSENTRY_TO_SENTRY(dsentry), MAGIC_STATE_MEMBLOCK)) { num_dead_dsentries = MAGIC_FAA(&magic_num_dead_dsentries, 1) + 1; size_dead_dsentries = MAGIC_FAA(&magic_size_dead_dsentries, size) + size; MAGIC_MEM_PRINTF("magic_update_dsentry_state: ++magic_num_dead_dsentries (num=%d, size=%d)\n", num_dead_dsentries, size_dead_dsentries); if(!magic_ignore_dead_dsentries && MAGIC_DEAD_DSENTRIES_NEED_FREEING()) { magic_free_dead_dsentries(); } } break; default: ret = MAGIC_EINVAL; break; } return ret; } /*===========================================================================* * magic_free_dsentry * *===========================================================================*/ PRIVATE int magic_free_dsentry(struct _magic_dsentry *dsentry) { int ret = 0; struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry); int region = MAGIC_STATE_REGION(sentry); void *ptr = MAGIC_PTR_FROM_DSENTRY(dsentry); size_t page_size, size; void *data_ptr, *aligned_ptr; int from_wrapper = MAGIC_MEM_WRAPPER_IS_ACTIVE(); assert(dsentry->magic_number == MAGIC_DSENTRY_MNUM_NULL); if (!from_wrapper) { MAGIC_MEM_WRAPPER_BEGIN(); } if (magic_mem_heap_free_cb) { ret = magic_mem_heap_free_cb(dsentry); if (ret != MAGIC_ENOENT) return ret; /* * If the callback returned MAGIC_ENOENT, fallback to * the default behavior. */ ret = 0; } /* A MAP_SHARED region will have both MAGIC_STATE_MAP and MAGIC_STATE_SHM. */ if (region == (MAGIC_STATE_MAP | MAGIC_STATE_SHM)) region = MAGIC_STATE_MAP; switch (region) { case MAGIC_STATE_HEAP: MAGIC_MEM_DEBUG_FREE(ptr); free(ptr); break; case MAGIC_STATE_MAP: case MAGIC_STATE_SHM: page_size = MAGIC_PAGE_SIZE; size = MAGIC_DSENTRY_TO_SENTRY(dsentry)->type->size; data_ptr = MAGIC_PTR_TO_DATA(ptr); aligned_ptr = ((char *)data_ptr) - page_size; if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_DETACHED)) { size_t padding_size = (size_t) dsentry->ext; MAGIC_MEM_DEBUG_FREE(ptr); ret = munmap(((char *)aligned_ptr) - padding_size, page_size + size + padding_size); } else { #ifndef __MINIX if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_SHM)) ret = shmdt(data_ptr); else #endif ret = munmap(data_ptr, size); MAGIC_MEM_DEBUG_FREE(ptr); munmap(aligned_ptr, page_size); } if (ret != 0) { ret = MAGIC_EBADENT; } break; default: ret = MAGIC_EBADENT; break; } if (!from_wrapper) { MAGIC_MEM_WRAPPER_END(); } return ret; } /*===========================================================================* * magic_free_dead_dsentries * *===========================================================================*/ PUBLIC void magic_free_dead_dsentries() { struct _magic_dsentry *prev_dsentry, *dsentry, *next_first_dsentry, *skipped_dsentry; struct _magic_sentry *sentry; unsigned long num_dead_dsentries; int dead_dsentries_left; int ret; MAGIC_DSENTRY_LOCK(); skipped_dsentry = NULL; num_dead_dsentries = magic_num_dead_dsentries; next_first_dsentry = _magic_first_dsentry; if (next_first_dsentry) { /* if the first dsentry is dead, skip it to eliminate contention on the list head */ if (next_first_dsentry->magic_state == MAGIC_DSENTRY_MSTATE_DEAD){ num_dead_dsentries--; } } if(!next_first_dsentry || num_dead_dsentries == 0) { MAGIC_DSENTRY_UNLOCK(); return; } MAGIC_MEM_PRINTF("magic_free_dead_dsentries: Freeing %d dead dsentries...\n", num_dead_dsentries); /* Eliminate the dead dsentries but always skip the first one to eliminate contention on the head. */ do { dead_dsentries_left = 0; MAGIC_DSENTRY_ITER(next_first_dsentry->next, prev_dsentry, dsentry, sentry, /* normal dsentry to be freed */ if ((dsentry->magic_state != MAGIC_DSENTRY_MSTATE_DEAD) || (magic_update_dsentry_state(dsentry, MAGIC_DSENTRY_MSTATE_FREED) < 0)) { next_first_dsentry = dsentry; } else { magic_destroy_dsentry(dsentry, prev_dsentry); ret = magic_free_dsentry(dsentry); if(ret != 0) { _magic_printf("Warning: magic_free_dsentry failed with return code %d for: ", ret); MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR); MAGIC_MEM_PRINTF("\n"); } num_dead_dsentries--; dead_dsentries_left = 1; break; } ); } while(dead_dsentries_left && num_dead_dsentries > 0); assert(num_dead_dsentries == 0); MAGIC_DSENTRY_UNLOCK(); } /*===========================================================================* * magic_destroy_dsentry * *===========================================================================*/ PUBLIC void magic_destroy_dsentry(struct _magic_dsentry *dsentry, struct _magic_dsentry *prev_dsentry) { struct _magic_dsentry *next_dsentry, *next_mempool_dsentry, *prev_mempool_dsentry = NULL; int dsentry_destroyed, mempool_dsentry_destroyed; if(magic_destroy_dsentry_ext_cb && MAGIC_DSENTRY_HAS_EXT(dsentry)) { magic_destroy_dsentry_ext_cb(dsentry); } if (MAGIC_STATE_FLAG(MAGIC_DSENTRY_TO_SENTRY(dsentry), MAGIC_STATE_MEMPOOL)) { do { if(!prev_mempool_dsentry) { if(MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) != MAGIC_DSENTRY_NEXT_MEMPOOL(_magic_first_mempool_dsentry)) { prev_mempool_dsentry = magic_mempool_dsentry_prev_lookup(dsentry); assert(prev_mempool_dsentry != (struct _magic_dsentry *) MAGIC_ENOPTR && "Dsentry not found!"); } } if(prev_mempool_dsentry) { MAGIC_DSENTRY_NEXT_MEMPOOL(prev_mempool_dsentry) = MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry); mempool_dsentry_destroyed = 1; } else { /* Remove the first dsentry atomically. */ next_mempool_dsentry = MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry); mempool_dsentry_destroyed = (MAGIC_CAS(&_magic_first_mempool_dsentry, dsentry, next_mempool_dsentry) == dsentry); } } while(!mempool_dsentry_destroyed); MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) = NULL; } do { #if MAGIC_DSENTRY_ALLOW_PREV prev_dsentry = MAGIC_DSENTRY_PREV(dsentry); #else if(!prev_dsentry) { if(MAGIC_DSENTRY_NEXT(dsentry) != MAGIC_DSENTRY_NEXT(_magic_first_dsentry)) { prev_dsentry = magic_dsentry_prev_lookup(dsentry); assert(prev_dsentry != (struct _magic_dsentry *) MAGIC_ENOPTR && "Dsentry not found!"); } } #endif if(prev_dsentry) { MAGIC_DSENTRY_NEXT(prev_dsentry) = MAGIC_DSENTRY_NEXT(dsentry); dsentry_destroyed = 1; } else { /* Remove the first dsentry atomically. */ next_dsentry = MAGIC_DSENTRY_NEXT(dsentry); dsentry_destroyed = (MAGIC_CAS(&_magic_first_dsentry, dsentry, next_dsentry) == dsentry); } } while(!dsentry_destroyed); #if MAGIC_DSENTRY_ALLOW_PREV next_dsentry = MAGIC_DSENTRY_NEXT(dsentry); if(next_dsentry) { MAGIC_DSENTRY_PREV(next_dsentry) = MAGIC_DSENTRY_PREV(dsentry); } MAGIC_DSENTRY_PREV(dsentry) = NULL; #endif dsentry->magic_number = MAGIC_DSENTRY_MNUM_NULL; MAGIC_DSENTRY_NEXT(dsentry) = NULL; } /*===========================================================================* * magic_destroy_obdsentry_by_addr * *===========================================================================*/ PUBLIC int magic_destroy_obdsentry_by_addr(void *data_ptr) { struct _magic_sentry *sentry; struct _magic_dsentry *dsentry; struct _magic_obdsentry *obdsentry; int obflags = (MAGIC_STATE_DYNAMIC|MAGIC_STATE_OUT_OF_BAND); MAGIC_MEM_WRAPPER_LBEGIN(); /* Lookup the obdsentry. */ sentry = magic_sentry_lookup_by_addr(data_ptr, NULL); if(!sentry || ((sentry->flags & obflags) != obflags)) { MAGIC_MEM_WRAPPER_LEND(); return MAGIC_EINVAL; } dsentry = MAGIC_DSENTRY_FROM_SENTRY(sentry); obdsentry = MAGIC_OBDSENTRY_FROM_DSENTRY(dsentry); /* Destroy it and free obdsentry slot. */ magic_destroy_dsentry(dsentry, NULL); MAGIC_OBDSENTRY_FREE(obdsentry); MAGIC_MEM_WRAPPER_LEND(); return 0; } /*===========================================================================* * magic_destroy_dsentry_set_ext_cb * *===========================================================================*/ PUBLIC void magic_destroy_dsentry_set_ext_cb(const magic_dsentry_cb_t cb) { magic_destroy_dsentry_ext_cb = cb; } /*===========================================================================* * magic_update_dsentry * *===========================================================================*/ PUBLIC int magic_update_dsentry(void* addr, struct _magic_type *type) { struct _magic_dsentry *prev_dsentry, *dsentry; struct _magic_sentry* sentry; size_t size, type_size; int is_varsized = 0; int num_vsa_elements = 0; MAGIC_DSENTRY_LOCK(); MAGIC_DSENTRY_ALIVE_BLOCK_ITER(_magic_first_dsentry, prev_dsentry, dsentry, sentry, if(sentry->address == addr) { size = sentry->type->size; type_size = type->size; /* Catch variable-sized struct allocation. */ if(magic_type_alloc_needs_varsized_array(type, size, &num_vsa_elements)) { is_varsized = 1; } if(size % type_size != 0 && !is_varsized) { return MAGIC_EBADENT; } if(size == type_size && !is_varsized) { sentry->type = type; } else { struct _magic_type *array_type = &(dsentry->type); MAGIC_TYPE_ARRAY_CREATE_FROM_SIZE(array_type, type, MAGIC_DSENTRY_TO_TYPE_ARR(dsentry), size, num_vsa_elements); array_type->id = MAGIC_FAA(&_magic_types_next_id, 1); assert(_magic_types_next_id < MAGIC_ID_MAX); sentry->type = array_type; } return 0; } ); MAGIC_DSENTRY_UNLOCK(); return MAGIC_ENOENT; } /*===========================================================================* * magic_stack_init * *===========================================================================*/ PUBLIC void magic_stack_init() { struct _magic_obdsentry *obdsentry; char **ptr; size_t size; void *initial_stack_bottom, *initial_stack_top; unsigned long* word_ptr; int argc; char **argv; assert(!_magic_first_stack_dsentry && !_magic_last_stack_dsentry); /* Find initial stack bottom and top, this should be portable enough */ for (ptr = environ ; *ptr ; ptr++); if (ptr == environ){ /* the environment is empty, and ptr still points to environ. * decrement ptr twice: once to point at the argv terminator, * and once to point to point at the last argument, which will be the stack bottom */ ptr -= 2; } else { /* environment is not empty. decrement the pointer, * because the for loop walked past the last env variable pointer. */ ptr--; } if (*ptr) { initial_stack_bottom = *ptr+strlen(*ptr)+1; word_ptr = (unsigned long*) environ; word_ptr--; assert(*word_ptr == 0); /* argv terminator */ word_ptr--; argc = 0; while(*word_ptr != (unsigned long) argc) { argc++; word_ptr--; } argv = (char**) (word_ptr+1); initial_stack_top = argv; } else { /* Environ and argv empty?. Resort to defaults. */ initial_stack_top = ptr; initial_stack_bottom = environ; } size = (size_t)initial_stack_bottom - (size_t)initial_stack_top + 1; /* Create the first stack dsentry. */ obdsentry = magic_create_obdsentry(initial_stack_top, MAGIC_VOID_TYPE, size, MAGIC_STATE_STACK, MAGIC_ALLOC_INITIAL_STACK_NAME, NULL); assert(obdsentry); _magic_first_stack_dsentry = _magic_last_stack_dsentry = MAGIC_OBDSENTRY_TO_DSENTRY(obdsentry); } /*===========================================================================* * magic_stack_dsentries_create * *===========================================================================*/ PUBLIC void magic_stack_dsentries_create( struct _magic_dsentry **prev_last_stack_dsentry, int num_dsentries, /* struct _magic_dsentry *dsentry, struct _magic_type *type, void* data_ptr, char* function_name, char* name, */ ...) { int i; struct _magic_dsentry *dsentry; struct _magic_type *type; void* data_ptr; char* function_name; char* name; char *min_data_ptr = NULL, *max_data_ptr = NULL; va_list va; assert(num_dsentries > 0); MAGIC_DSENTRY_LOCK(); assert(_magic_first_stack_dsentry && "First stack dsentry not found!"); va_start(va, num_dsentries); *prev_last_stack_dsentry = _magic_last_stack_dsentry; for (i = 0 ; i < num_dsentries ; i++) { dsentry = va_arg(va, struct _magic_dsentry *); type = va_arg(va, struct _magic_type *); data_ptr = va_arg(va, void *); function_name = va_arg(va, char *); name = va_arg(va, char *); if (i == num_dsentries - 1) { /* Return address. */ int *value_set = (void*) type; data_ptr = MAGIC_FRAMEADDR_TO_RETADDR_PTR(data_ptr); type = &(dsentry->type); magic_create_dsentry(dsentry, data_ptr, MAGIC_VOID_TYPE, MAGIC_VOID_TYPE->size, MAGIC_STATE_STACK | MAGIC_STATE_CONSTANT | MAGIC_STATE_ADDR_NOT_TAKEN, name, function_name); memcpy(type, &magic_default_ret_addr_type, sizeof(struct _magic_type)); type->contained_types = MAGIC_DSENTRY_TO_TYPE_ARR(dsentry); type->contained_types[0] = MAGIC_VOID_TYPE; type->value_set = value_set; value_set[0] = 1; value_set[1] = *((int *)data_ptr); /* Safe to override the type non-atomically. * The new type is only more restrictive, and nobody could * have touched this stack dsentry in the meantime. */ MAGIC_DSENTRY_TO_SENTRY(dsentry)->type = type; } else { /* Local variable. */ magic_create_dsentry(dsentry, data_ptr, type, type->size, MAGIC_STATE_STACK, name, function_name); if (!min_data_ptr || min_data_ptr > (char *) data_ptr) { min_data_ptr = (char *) data_ptr; } if (!max_data_ptr || max_data_ptr < (char *) data_ptr) { max_data_ptr = (char *) data_ptr; } } } #if MAGIC_FORCE_DYN_MEM_ZERO_INIT if (min_data_ptr && max_data_ptr) { memset(min_data_ptr, 0, max_data_ptr - min_data_ptr + 1); } #endif _magic_last_stack_dsentry = dsentry; MAGIC_DSENTRY_UNLOCK(); va_end(va); } /*===========================================================================* * magic_stack_dsentries_destroy * *===========================================================================*/ PUBLIC void magic_stack_dsentries_destroy( struct _magic_dsentry **prev_last_stack_dsentry, int num_dsentries, /* struct _magic_dsentry *dsentry, */ ...) { int i; struct _magic_dsentry *dsentry; va_list va; assert(num_dsentries > 0); MAGIC_MEM_WRAPPER_LBEGIN(); va_start(va, num_dsentries); _magic_last_stack_dsentry = *prev_last_stack_dsentry; for (i = 0 ; i < num_dsentries ; i++) { dsentry = va_arg(va, struct _magic_dsentry *); magic_destroy_dsentry(dsentry, NULL); } va_end(va); MAGIC_MEM_WRAPPER_LEND(); } /*===========================================================================* * magic_create_dfunction * *===========================================================================*/ PUBLIC int magic_create_dfunction(struct _magic_dfunction *dfunction, void *data_ptr, struct _magic_type *type, int flags, char *name, char *parent_name) { struct _magic_function *function = MAGIC_DFUNCTION_TO_FUNCTION(dfunction); if(!type) { type = MAGIC_VOID_TYPE; } memcpy(dfunction, &magic_default_dfunction, sizeof(struct _magic_dfunction)); assert(!(type->flags & MAGIC_TYPE_DYNAMIC) && "bad type!"); function->type = type; function->flags |= flags; function->address = data_ptr; if(name) { function->name = name; } if(parent_name) { dfunction->parent_name = parent_name; } function->id = MAGIC_FAA(&_magic_functions_next_id, 1); assert(_magic_functions_next_id < MAGIC_ID_MAX); if(_magic_first_dfunction) { assert(_magic_last_dfunction); MAGIC_DFUNCTION_NEXT(_magic_last_dfunction) = dfunction; } else { assert(!_magic_last_dfunction); assert(_magic_dfunctions_num == 0); _magic_first_dfunction = dfunction; } MAGIC_DFUNCTION_PREV(dfunction) = _magic_last_dfunction; MAGIC_DFUNCTION_NEXT(dfunction) = NULL; _magic_last_dfunction = dfunction; _magic_dfunctions_num++; assert(magic_check_dfunction(dfunction, 0) && "Bad magic dfunction created!"); magic_update_dfunction_ranges = 1; return 0; } /*===========================================================================* * magic_destroy_dfunction * *===========================================================================*/ PUBLIC void magic_destroy_dfunction(struct _magic_dfunction *dfunction) { dfunction->magic_number = MAGIC_DFUNCTION_MNUM_NULL; if(MAGIC_DFUNCTION_HAS_NEXT(dfunction)) { MAGIC_DFUNCTION_PREV(MAGIC_DFUNCTION_NEXT(dfunction)) = MAGIC_DFUNCTION_PREV(dfunction); } else { _magic_last_dfunction = MAGIC_DFUNCTION_PREV(dfunction); } if(MAGIC_DFUNCTION_HAS_PREV(dfunction)) { MAGIC_DFUNCTION_NEXT(MAGIC_DFUNCTION_PREV(dfunction)) = MAGIC_DFUNCTION_NEXT(dfunction); } else { _magic_first_dfunction = MAGIC_DFUNCTION_NEXT(dfunction); } MAGIC_DFUNCTION_NEXT(dfunction) = NULL; MAGIC_DFUNCTION_PREV(dfunction) = NULL; _magic_dfunctions_num--; if(_magic_dfunctions_num == 0) { assert(!_magic_first_dfunction && !_magic_last_dfunction); } else { assert(_magic_first_dfunction && _magic_last_dfunction); } } /*===========================================================================* * magic_create_sodesc * *===========================================================================*/ PUBLIC int magic_create_sodesc(struct _magic_sodesc *sodesc) { if(_magic_first_sodesc) { assert(_magic_last_sodesc); MAGIC_SODESC_NEXT(_magic_last_sodesc) = sodesc; } else { assert(!_magic_last_sodesc); assert(_magic_sodescs_num == 0); _magic_first_sodesc = sodesc; } MAGIC_SODESC_PREV(sodesc) = _magic_last_sodesc; MAGIC_SODESC_NEXT(sodesc) = NULL; _magic_last_sodesc = sodesc; _magic_sodescs_num++; return 0; } /*===========================================================================* * magic_destroy_sodesc * *===========================================================================*/ PUBLIC int magic_destroy_sodesc(struct _magic_sodesc *sodesc) { /* * NB!: This function requires the calling thread to already * hold the DSENTRY and DFUNCTION locks. */ int ret; const char *name; struct _magic_dsentry *prev_dsentry, *dsentry, *last_dsentry; struct _magic_sentry *sentry; struct _magic_dfunction *dfunction, *last_dfunction; /* * Time to destroy all the dsentries and dfunctions * linked to the descriptor. */ name = sodesc->lib.name; last_dsentry = NULL; MAGIC_DSENTRY_ALIVE_ITER(_magic_first_dsentry, prev_dsentry, dsentry, sentry, if (last_dsentry) magic_destroy_dsentry(last_dsentry, NULL); last_dsentry = dsentry->parent_name == name ? dsentry : NULL; ); if (last_dsentry) magic_destroy_dsentry(last_dsentry, NULL); last_dfunction = NULL; MAGIC_DFUNCTION_ITER(_magic_first_dfunction, dfunction, if (last_dfunction) magic_destroy_dfunction(last_dfunction); last_dfunction = dfunction->parent_name == name ? dfunction : NULL; ); if(last_dfunction) magic_destroy_dfunction(last_dfunction); /* Now get rid of the descriptor. */ if (MAGIC_SODESC_HAS_NEXT(sodesc)) { MAGIC_SODESC_PREV(MAGIC_SODESC_NEXT(sodesc)) = MAGIC_SODESC_PREV(sodesc); } else { _magic_last_sodesc = MAGIC_SODESC_PREV(sodesc); } if (MAGIC_SODESC_HAS_PREV(sodesc)) { MAGIC_SODESC_NEXT(MAGIC_SODESC_PREV(sodesc)) = MAGIC_SODESC_NEXT(sodesc); } else { _magic_first_sodesc = MAGIC_SODESC_NEXT(sodesc); } MAGIC_SODESC_NEXT(sodesc) = NULL; MAGIC_SODESC_PREV(sodesc) = NULL; _magic_sodescs_num--; if (_magic_sodescs_num == 0) { assert(!_magic_first_sodesc && !_magic_last_sodesc); } else { assert(_magic_first_sodesc && _magic_last_sodesc); } /* * Unmap the memory area that contained the dsentries and dfunctions * of this descriptor. */ ret = munmap(sodesc->lib.alloc_address, sodesc->lib.alloc_size); assert(ret == 0 && "Unable to unmap SODESC memory segment!"); return 0; } /*===========================================================================* * magic_create_dsodesc * *===========================================================================*/ PUBLIC int magic_create_dsodesc(struct _magic_dsodesc *dsodesc) { /* * NB!: This function requires the calling thread to already * hold the DSODESC lock. */ if (_magic_first_dsodesc) { assert(_magic_last_dsodesc); MAGIC_DSODESC_NEXT(_magic_last_dsodesc) = dsodesc; } else { assert(!_magic_last_dsodesc); assert(_magic_dsodescs_num == 0); _magic_first_dsodesc = dsodesc; } MAGIC_DSODESC_PREV(dsodesc) = _magic_last_dsodesc; MAGIC_DSODESC_NEXT(dsodesc) = NULL; _magic_last_dsodesc = dsodesc; _magic_dsodescs_num++; return 0; } /*===========================================================================* * magic_destroy_dsodesc * *===========================================================================*/ PUBLIC int magic_destroy_dsodesc(struct _magic_dsodesc *dsodesc) { /* * NB!: This function requires the calling thread to already * hold the DSENTRY, DFUNCTION and DSODESC locks. */ int ret; const char *name; struct _magic_dsentry *prev_dsentry, *dsentry, *last_dsentry; struct _magic_sentry *sentry; struct _magic_dfunction *dfunction, *last_dfunction; dsodesc->ref_count--; /* Don't destroy the DSO descriptor quite yet, we still have references. */ if (dsodesc->ref_count > 0) { return dsodesc->ref_count; } /* * Time to destroy all the dsentries and dfunctions * linked to the descriptor. */ name = dsodesc->lib.name; last_dsentry = NULL; MAGIC_DSENTRY_ALIVE_ITER(_magic_first_dsentry, prev_dsentry, dsentry, sentry, if (last_dsentry) magic_destroy_dsentry(last_dsentry, NULL); last_dsentry = dsentry->parent_name == name ? dsentry : NULL; ); if (last_dsentry) magic_destroy_dsentry(last_dsentry, NULL); last_dfunction = NULL; MAGIC_DFUNCTION_ITER(_magic_first_dfunction, dfunction, if (last_dfunction) magic_destroy_dfunction(last_dfunction); last_dfunction = dfunction->parent_name == name ? dfunction : NULL; ); if (last_dfunction) magic_destroy_dfunction(last_dfunction); /* Now get rid of the descriptor. */ if (MAGIC_DSODESC_HAS_NEXT(dsodesc)) { MAGIC_DSODESC_PREV(MAGIC_DSODESC_NEXT(dsodesc)) = MAGIC_DSODESC_PREV(dsodesc); } else { _magic_last_dsodesc = MAGIC_DSODESC_PREV(dsodesc); } if (MAGIC_DSODESC_HAS_PREV(dsodesc)) { MAGIC_DSODESC_NEXT(MAGIC_DSODESC_PREV(dsodesc)) = MAGIC_DSODESC_NEXT(dsodesc); } else { _magic_first_dsodesc = MAGIC_DSODESC_NEXT(dsodesc); } MAGIC_DSODESC_NEXT(dsodesc) = NULL; MAGIC_DSODESC_PREV(dsodesc) = NULL; _magic_dsodescs_num--; if (_magic_dsodescs_num == 0) { assert(!_magic_first_dsodesc && !_magic_last_dsodesc); } else { assert(_magic_first_dsodesc && _magic_last_dsodesc); } /* * Unmap the memory area that contained the dsentries and dfunctions * of this descriptor. */ ret = munmap(dsodesc->lib.alloc_address, dsodesc->lib.alloc_size); assert(ret == 0 && "Unable to unmap DSODESC memory segment!"); return 0; /* no more references, descriptor is gone. */ } /*===========================================================================* * magic_alloc * *===========================================================================*/ PUBLIC void *magic_alloc(__MA_ARGS__ void *ptr, size_t size, int flags) { int ret; void *data_ptr; struct _magic_dsentry *dsentry; if(ptr == NULL) { return NULL; } data_ptr = MAGIC_PTR_TO_DATA(ptr); dsentry = MAGIC_PTR_TO_DSENTRY(ptr); /* Catch pool allocations and update the name & flags */ if (MAGIC_MEMPOOL_MGMT_IS_ACTIVE() && !(flags & MAGIC_STATE_MEMBLOCK)) { flags |= MAGIC_STATE_MEMPOOL; name = MAGIC_MEMPOOL_GET_NAME(); } ret = magic_create_dsentry(dsentry, data_ptr, type, size, flags, name, parent_name); MAGIC_MEM_PRINTF("magic_alloc: ret = magic_create_dsentry(dsentry, data_ptr, type, size, flags, NULL, NULL) <-> %d = magic_create_dsentry(0x%08x, 0x%08x, 0x%08x, %d, 0x%08x, NULL, NULL)\n", ret, (unsigned) dsentry, (unsigned) data_ptr, type, size, flags); if(ret < 0) { return MAGIC_MEM_FAILED; } /* this way we skip the memory pool blocks -they are not real allocations and should not be logged */ if (!(flags & MAGIC_STATE_MEMBLOCK)) { MAGIC_MEM_DEBUG_ALLOC(ptr, (MAGIC_SIZE_TO_REAL(size))); } #if DEBUG MAGIC_MEM_PRINTF("magic_alloc: magic_create_dsentry created sentry: "); MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR); MAGIC_MEM_PRINTF("\n"); #endif MAGIC_MEM_PRINTF("magic_alloc: return 0x%08x\n", (unsigned) data_ptr); return data_ptr; } /*===========================================================================* * magic_malloc_positioned * *===========================================================================*/ PUBLIC void *magic_malloc_positioned(__MA_ARGS__ size_t size, void *ptr) { void *data_ptr; int dsentry_flags = MAGIC_STATE_HEAP; #if MAGIC_FORCE_DYN_MEM_ZERO_INIT assert(!_magic_vars->fake_malloc); do { return magic_calloc(__MA_VALUES__ size, 1); } while(0); #endif MAGIC_MEM_WRAPPER_BEGIN(); if(size > 0) { if (!ptr || !_magic_vars->fake_malloc) { /* * Check the external callback first. */ if (magic_mem_heap_alloc_cb) ptr = magic_mem_heap_alloc_cb(MAGIC_SIZE_TO_REAL(size) + magic_asr_get_padding_size(MAGIC_STATE_HEAP), name, parent_name); if (!ptr) ptr = malloc(MAGIC_SIZE_TO_REAL(size) + magic_asr_get_padding_size(MAGIC_STATE_HEAP)); MAGIC_MEM_PRINTF("magic_malloc: ptr = malloc(size) <-> 0x%08x = malloc(%d)\n", (unsigned) ptr, MAGIC_SIZE_TO_REAL(size)); } data_ptr = magic_alloc(__MA_VALUES__ ptr, size, dsentry_flags); if (data_ptr == MAGIC_MEM_FAILED) { /* * XXX: This doesn't seem likely to happen. However, if it does, * we need to distinguish between regular malloc() memory * and super-objects. See llvm/shared/libst/include/heap.h for * more information. */ free(ptr); data_ptr = NULL; errno = ENOMEM; } magic_heap_end = ((char *)sbrk(0)) - 1; } else { data_ptr = NULL; } MAGIC_MEM_WRAPPER_END(); return data_ptr; } /*===========================================================================* * magic_malloc * *===========================================================================*/ PUBLIC void *magic_malloc(__MA_ARGS__ size_t size) { return magic_malloc_positioned(__MA_VALUES__ size, NULL); } /*===========================================================================* * magic_calloc * *===========================================================================*/ PUBLIC void *magic_calloc(__MA_ARGS__ size_t nmemb, size_t size) { void *ptr = NULL, *data_ptr; size_t real_size; int dsentry_flags = MAGIC_STATE_HEAP; MAGIC_MEM_WRAPPER_BEGIN(); if(size > 0) { real_size = MAGIC_SIZE_TO_REAL(size*nmemb); /* * Check the external callback first. */ if (magic_mem_heap_alloc_cb) ptr = magic_mem_heap_alloc_cb(real_size + magic_asr_get_padding_size(MAGIC_STATE_HEAP), name, parent_name); if (!ptr) ptr = calloc(real_size + magic_asr_get_padding_size(MAGIC_STATE_HEAP), 1); MAGIC_MEM_PRINTF("magic_calloc: ptr = calloc(nmemb, size) <-> 0x%08x = calloc(%d, %d)\n", (unsigned) ptr, nmemb, real_size); data_ptr = magic_alloc(__MA_VALUES__ ptr, size*nmemb, dsentry_flags); if(data_ptr == MAGIC_MEM_FAILED) { /* * XXX: This doesn't seem likely to happen. However, if it does, * we need to distinguish between regular malloc() memory * and super-objects. See llvm/shared/libst/include/heap.h for * more information. */ free(ptr); data_ptr = NULL; errno = ENOMEM; } magic_heap_end = ((char*)sbrk(0))-1; } else { data_ptr = NULL; } MAGIC_MEM_WRAPPER_END(); return data_ptr; } /*===========================================================================* * magic_free * *===========================================================================*/ PUBLIC void magic_free(__MD_ARGS__ void *data_ptr) { void *ptr; int ret; MAGIC_MEM_WRAPPER_BEGIN(); if(data_ptr) { ptr = MAGIC_PTR_FROM_DATA(data_ptr); /* Check for legitimate non-indexed chunks of memory and skip. */ if((!magic_libcommon_active || !MAGIC_USE_DYN_MEM_WRAPPERS) && !MAGIC_DSENTRY_MNUM_OK(MAGIC_PTR_TO_DSENTRY(ptr))) { MAGIC_MEM_WRAPPER_END(); free(data_ptr); return; } MAGIC_MEM_PRINTF("magic_free: magic_free(0x%08x) / free(0x%08x)\n", (unsigned) data_ptr, (unsigned) ptr); assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_HEAP) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?"); if(magic_allow_dead_dsentries) { ret = magic_update_dsentry_state(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_DSENTRY_MSTATE_DEAD); assert(ret == 0 && "Bad free!"); } else { MAGIC_DSENTRY_LOCK(); magic_destroy_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), NULL); ret = magic_free_dsentry(MAGIC_PTR_TO_DSENTRY(ptr)); assert(ret == 0 && "Bad free!"); MAGIC_DSENTRY_UNLOCK(); } } MAGIC_MEM_WRAPPER_END(); } /*===========================================================================* * magic_realloc * *===========================================================================*/ PUBLIC void *magic_realloc(__MA_ARGS__ void *data_ptr, size_t size) { void *ptr, *new_ptr, *new_data_ptr; size_t old_size; if(!data_ptr) { return magic_malloc(__MA_VALUES__ size); } if(size == 0) { magic_free(__MD_VALUES_DEFAULT__ data_ptr); return NULL; } ptr = MAGIC_PTR_FROM_DATA(data_ptr); new_data_ptr = magic_malloc(__MA_VALUES__ size); if(!new_data_ptr) { return NULL; } new_ptr = MAGIC_PTR_FROM_DATA(new_data_ptr); assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_HEAP) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?"); MAGIC_MEM_PRINTF("magic_realloc: ptr = realloc(ptr, size) <-> 0x%08x = realloc(0x%08x, %d)\n", (unsigned) new_ptr, (unsigned) ptr, MAGIC_SIZE_TO_REAL(size)); old_size = MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(ptr))->type->size; memcpy(new_data_ptr, data_ptr, old_size < size ? old_size : size); magic_free(__MD_VALUES_DEFAULT__ data_ptr); return new_data_ptr; } /*===========================================================================* * magic_posix_memalign * *===========================================================================*/ PUBLIC int magic_posix_memalign(__MA_ARGS__ void **memptr, size_t alignment, size_t size) { int ret = 0; void *ptr = NULL, *data_ptr; int dsentry_flags = MAGIC_STATE_HEAP; MAGIC_MEM_WRAPPER_BEGIN(); if(size > 0) { /* * Check the external callback first. */ if (magic_mem_heap_alloc_cb) ptr = magic_mem_heap_alloc_cb(MAGIC_SIZE_TO_REAL(size), name, parent_name); if (!ptr) ret = posix_memalign(&ptr, alignment, MAGIC_SIZE_TO_REAL(size)); MAGIC_MEM_PRINTF("magic_posix_memalign: ret = posix_memalign(ptr, alignment, size) <-> %d = posix_memalign(%p, %d, %d)\n", ret, ptr, alignment, MAGIC_SIZE_TO_REAL(size)); if(ret == 0) { data_ptr = magic_alloc(__MA_VALUES__ ptr, size, dsentry_flags); if(data_ptr == MAGIC_MEM_FAILED) { /* * XXX: This doesn't seem likely to happen. However, if it does, * we need to distinguish between regular malloc() memory * and super-objects. See llvm/shared/libst/include/heap.h for * more information. */ free(ptr); ret = ENOMEM; } else { *memptr = data_ptr; #if MAGIC_FORCE_DYN_MEM_ZERO_INIT memset(data_ptr, 0, size); #endif } magic_heap_end = ((char*)sbrk(0))-1; } } else { ret = EINVAL; } MAGIC_MEM_WRAPPER_END(); return ret; } #ifndef __MINIX /*===========================================================================* * magic_valloc * *===========================================================================*/ PUBLIC void *magic_valloc(__MA_ARGS__ size_t size) { return magic_memalign(__MA_VALUES__ MAGIC_PAGE_SIZE, size); } /*===========================================================================* * magic_memalign * *===========================================================================*/ PUBLIC void *magic_memalign(__MA_ARGS__ size_t boundary, size_t size) { void *ptr; int ret = magic_posix_memalign(__MA_VALUES__ &ptr, boundary, size); if(ret != 0) { return NULL; } return ptr; } #endif /*===========================================================================* * magic_mmap_positioned * *===========================================================================*/ PUBLIC void *magic_mmap_positioned(__MA_ARGS__ void *start, size_t length, int prot, int flags, int fd, off_t offset, struct _magic_dsentry *cached_dsentry) { void *ptr, *data_ptr, *aligned_start, *aligned_ptr; void *new_ptr, *new_start; int dsentry_flags = MAGIC_STATE_MAP; size_t alloc_length; size_t page_size = MAGIC_PAGE_SIZE; int padding_type, padding_size; static THREAD_LOCAL int magic_is_first_mmap = 1; MAGIC_MEM_WRAPPER_BEGIN(); if (flags & MAP_FIXED) { /* Allow safe overmapping. */ struct _magic_sentry *sentry = magic_sentry_lookup_by_range(start, NULL); if (sentry && sentry == magic_sentry_lookup_by_range((char*)start+length-1, NULL)) return mmap(start, length, prot, flags, fd, offset); } assert(!(flags & MAP_FIXED) && "MAP_FIXED may override existing mapping, currently not implemented!"); if (length > 0) { if (magic_is_first_mmap) { magic_is_first_mmap = 0; padding_type = MAGIC_STATE_MAP | MAGIC_ASR_FLAG_INIT; } else { padding_type = MAGIC_STATE_MAP; } padding_size = start ? 0 : magic_asr_get_padding_size(padding_type); assert(MAGIC_SIZE_TO_REAL(length) <= page_size + length); aligned_start = start ? ((char *)start) - page_size : NULL; alloc_length = length + (length % page_size == 0 ? 0 : page_size - (length % page_size)); #if 0 if (_magic_vars->do_skip_mmap) { ptr = cached_dsentry ? ((char *)cached_dsentry) - (padding_size + page_size - MAGIC_SIZE_TO_REAL(0)) : NULL; } else #endif if (!(flags & MAP_ANONYMOUS) && !(flags & MAP_SHARED) && ((prot & magic_mmap_dsentry_header_prot) == magic_mmap_dsentry_header_prot)) { ptr = mmap(aligned_start, page_size + alloc_length + padding_size, prot, flags, fd, offset); } else { /* Preallocate memory for metadata + data. */ ptr = mmap(aligned_start, page_size + alloc_length + padding_size, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS | MAP_PRIVATE | (flags & MAP_FIXED), -1, 0); /* Remap the data part the way the caller wants us to. */ if (ptr != MAP_FAILED) { new_start = ((char *)ptr) + page_size; new_ptr = mmap(new_start, length, prot, flags | MAP_FIXED, fd, offset); if (new_ptr == MAP_FAILED) { munmap(ptr, page_size + alloc_length); ptr = MAP_FAILED; } } } aligned_ptr = ptr; MAGIC_MEM_PRINTF("magic_mmap: ptr = mmap(start, length, prot, flags, fd, offset) <-> 0x%08x = mmap(0x%08x, %d, 0x%08x, 0x%08x, %d, %d)\n", (unsigned) aligned_ptr, aligned_start, page_size + length, prot, flags, fd, offset); if (ptr != MAP_FAILED) { ptr = ((char *)ptr) + padding_size + page_size - MAGIC_SIZE_TO_REAL(0); } else { ptr = NULL; } if (flags & MAP_SHARED) dsentry_flags |= MAGIC_STATE_SHM; data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, dsentry_flags); if (data_ptr == MAGIC_MEM_FAILED) { munmap(aligned_ptr, page_size + length); data_ptr = NULL; errno = ENOMEM; } if (!data_ptr) { errno = ENOMEM; data_ptr = MAP_FAILED; } else { MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = flags; MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = prot; MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->ext = (void *) padding_size; } } else { data_ptr = MAP_FAILED; errno = EINVAL; } MAGIC_MEM_WRAPPER_END(); return data_ptr; } /*===========================================================================* * magic_mmap * *===========================================================================*/ PUBLIC void *magic_mmap(__MA_ARGS__ void *start, size_t length, int prot, int flags, int fd, off_t offset) { return magic_mmap_positioned(__MA_VALUES__ start, length, prot, flags, fd, offset, NULL); } /*===========================================================================* * magic_munmap * *===========================================================================*/ PUBLIC int magic_munmap(__MD_ARGS__ void *data_ptr, size_t length) { int ret; void *ptr, *aligned_ptr; struct _magic_sentry *sentry; size_t alloc_length, old_size; size_t page_size = MAGIC_PAGE_SIZE; MAGIC_MEM_WRAPPER_BEGIN(); if(data_ptr) { ptr = MAGIC_PTR_FROM_DATA(data_ptr); /* Check for legitimate non-indexed chunks of memory and skip. */ if((!magic_libcommon_active || !MAGIC_USE_DYN_MEM_WRAPPERS) && !MAGIC_DSENTRY_MNUM_OK(MAGIC_PTR_TO_DSENTRY(ptr))) { MAGIC_MEM_WRAPPER_END(); return munmap(data_ptr, length); } sentry = MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(ptr)); aligned_ptr = ((char*)data_ptr) - page_size; MAGIC_MEM_PRINTF("magic_munmap: magic_munmap(0x%08x, %d) / unmap(0x%08x, %d)\n", (unsigned) data_ptr, length, (unsigned) aligned_ptr, page_size+length); assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_MAP) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?"); old_size = MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(ptr))->type->size; alloc_length = length + (length % page_size == 0 ? 0 : page_size-(length % page_size)); if(alloc_length != old_size) { assert(alloc_length >= old_size && "Partial unmapping not supported!"); ret = -1; errno = EINVAL; } else { if(magic_allow_dead_dsentries) { ret = magic_update_dsentry_state(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_DSENTRY_MSTATE_DEAD); assert(ret == 0 && "Bad munmap!"); } else { MAGIC_DSENTRY_LOCK(); magic_destroy_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), NULL); ret = magic_free_dsentry(MAGIC_PTR_TO_DSENTRY(ptr)); assert(ret == 0 && "Bad munmap!"); MAGIC_DSENTRY_UNLOCK(); } } } else { ret = -1; errno = EINVAL; } MAGIC_MEM_WRAPPER_END(); return ret; } /*===========================================================================* * magic_brk * *===========================================================================*/ PUBLIC int magic_brk(__MA_ARGS__ void *addr) { void *ptr; void *break_addr; int ret; MAGIC_MEM_PRINTF("magic_brk: Warning: somebody calling magic_brk()!"); MAGIC_MEM_WRAPPER_LBLOCK( break_addr = sbrk(0); ); if(addr >= break_addr) { ptr = magic_sbrk(__MA_VALUES__ (char*)addr - (char*)break_addr); ret = (ptr == (void*) -1 ? -1 : 0); if(ret == -1) { errno = ENOMEM; } } else { magic_free(__MD_VALUES_DEFAULT__ addr); ret = 0; } return ret; } /*===========================================================================* * magic_sbrk * *===========================================================================*/ PUBLIC void *magic_sbrk(__MA_ARGS__ intptr_t increment) { void *ptr; if(increment == 0) { MAGIC_MEM_WRAPPER_LBLOCK( ptr = sbrk(0); ); } else { MAGIC_MEM_PRINTF("magic_sbrk: Warning: somebody calling magic_sbrk(), resorting to magic_malloc()!"); ptr = magic_malloc(__MA_VALUES__ increment); } return ptr; } #ifndef __MINIX /*===========================================================================* * magic_shmat * *===========================================================================*/ PUBLIC void *magic_shmat(__MA_ARGS__ int shmid, const void *shmaddr, int shmflg) { void *ptr, *data_ptr, *aligned_shmaddr, *aligned_ptr; void *new_ptr, *new_shmaddr; size_t size; struct shmid_ds buf; int ret, flags; size_t page_size = MAGIC_PAGE_SIZE; MAGIC_MEM_WRAPPER_BEGIN(); assert(!(shmflg & SHM_REMAP) && "Linux-specific SHM_REMAP not supported!"); ret = shmctl(shmid, IPC_STAT, &buf); if (ret == -1) { MAGIC_MEM_WRAPPER_END(); return NULL; } size = buf.shm_segsz; if (size > 0) { assert(size % page_size == 0); assert(MAGIC_SIZE_TO_REAL(size) <= size + page_size); if (shmaddr && (shmflg & SHM_RND)) { unsigned long shmlba = SHMLBA; shmflg &= ~SHM_RND; shmaddr = (void *) ((((unsigned long)shmaddr) / shmlba) * shmlba); } /* Preallocate memory for metadata + data. */ aligned_shmaddr = shmaddr ? ((char *)shmaddr) - page_size : NULL; flags = MAP_ANONYMOUS | MAP_PRIVATE | (aligned_shmaddr ? MAP_FIXED : 0); ptr = mmap(aligned_shmaddr, page_size + size, magic_mmap_dsentry_header_prot, flags, -1, 0); /* Remap the data part the way the caller wants us to. */ if (ptr != MAP_FAILED) { new_shmaddr = ((char *)ptr) + page_size; munmap(new_shmaddr, size); new_ptr = shmat(shmid, new_shmaddr, shmflg); if(new_ptr == (void *) -1) { munmap(ptr, page_size); ptr = MAP_FAILED; } } aligned_ptr = ptr; MAGIC_MEM_PRINTF("magic_shmat: ptr = shmat(shmid, shmaddr, shmflg) <-> 0x%08x = shmat(%d, 0x%08x, 0x%08x)\n", (unsigned) aligned_ptr, shmid, aligned_shmaddr, shmflg); if (ptr != MAP_FAILED) { ptr = ((char *)ptr) + page_size - MAGIC_SIZE_TO_REAL(0); } else { ptr = NULL; } data_ptr = magic_alloc(__MA_VALUES__ ptr, size, MAGIC_STATE_SHM | MAGIC_STATE_DETACHED); if (data_ptr == MAGIC_MEM_FAILED) { munmap(aligned_ptr, page_size); munmap(new_ptr, size); data_ptr = (void *) -1; errno = ENOMEM; } else { MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_shmat_flags = shmflg; MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_shmat_shmid = shmid; } } else { data_ptr = (void *) -1; errno = EINVAL; } MAGIC_MEM_WRAPPER_END(); return data_ptr; } /*===========================================================================* * magic_shmdt * *===========================================================================*/ PUBLIC int magic_shmdt(__MD_ARGS__ const void *data_ptr) { int ret; void *ptr, *aligned_ptr; size_t page_size = MAGIC_PAGE_SIZE; MAGIC_MEM_WRAPPER_LBEGIN(); if (data_ptr) { ptr = MAGIC_PTR_FROM_DATA(data_ptr); /* Check for legitimate non-indexed chunks of memory and skip. */ if ((!magic_libcommon_active || !MAGIC_USE_DYN_MEM_WRAPPERS) && !MAGIC_DSENTRY_MNUM_OK(MAGIC_PTR_TO_DSENTRY(ptr))) { MAGIC_MEM_WRAPPER_LEND(); return shmdt(data_ptr); } aligned_ptr = ((char*)data_ptr) - page_size; MAGIC_MEM_PRINTF("magic_shmdt: magic_shmdt(0x%08x) / shmdt(0x%08x)\n", (unsigned) data_ptr, (unsigned) aligned_ptr); assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_SHM) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?"); ret = shmdt(data_ptr); if (ret == 0) { magic_destroy_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), NULL); munmap(aligned_ptr, page_size); } } else { ret = -1; errno = EINVAL; } MAGIC_MEM_WRAPPER_LEND(); return ret; } /*===========================================================================* * magic_mmap64 * *===========================================================================*/ PUBLIC void *magic_mmap64(__MA_ARGS__ void *start, size_t length, int prot, int flags, int fd, off_t pgoffset) { void *ptr, *data_ptr, *aligned_start, *aligned_ptr; void *new_ptr, *new_start; int dsentry_flags = MAGIC_STATE_MAP; size_t alloc_length; size_t page_size = MAGIC_PAGE_SIZE; MAGIC_MEM_WRAPPER_BEGIN(); if (flags & MAP_FIXED) { /* Allow safe overmapping. */ struct _magic_sentry *sentry = magic_sentry_lookup_by_range(start, NULL); if (sentry && sentry == magic_sentry_lookup_by_range((char*)start+length-1, NULL)) return mmap64(start, length, prot, flags, fd, pgoffset); } assert(!(flags & MAP_FIXED) && "MAP_FIXED may override existing mapping, currently not implemented!"); if(length > 0) { assert(MAGIC_SIZE_TO_REAL(length) <= page_size+length); aligned_start = start ? ((char*)start) - page_size : NULL; alloc_length = length + (length % page_size == 0 ? 0 : page_size-(length % page_size)); if((flags & MAP_ANONYMOUS) && !(flags & MAP_SHARED) && ((prot & magic_mmap_dsentry_header_prot) == magic_mmap_dsentry_header_prot)) { ptr = mmap64(aligned_start, page_size+length, prot, flags, fd, pgoffset); } else { /* Preallocate memory for metadata + data. */ ptr = mmap64(aligned_start, page_size+alloc_length, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS|MAP_PRIVATE|(flags & MAP_FIXED), -1, 0); /* Remap the data part the way the caller wants us to. */ if(ptr != MAP_FAILED) { new_start = ((char*)ptr) + page_size; new_ptr = mmap64(new_start, length, prot, flags|MAP_FIXED, fd, pgoffset); if(new_ptr == MAP_FAILED) { munmap(ptr, page_size+alloc_length); ptr = MAP_FAILED; } } } aligned_ptr = ptr; MAGIC_MEM_PRINTF("magic_mmap64: ptr = mmap64(start, length, prot, flags, fd, pgoffset) <-> 0x%08x = mmap64(0x%08x, %d, 0x%08x, 0x%08x, %d, %d)\n", (unsigned) aligned_ptr, aligned_start, page_size+length, prot, flags, fd, pgoffset); if(ptr != MAP_FAILED) { ptr = ((char*)ptr) + page_size - MAGIC_SIZE_TO_REAL(0); } else { ptr = NULL; } if (flags & MAP_SHARED) dsentry_flags |= MAGIC_STATE_SHM; data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, dsentry_flags); if(data_ptr == MAGIC_MEM_FAILED) { munmap(aligned_ptr, page_size+length); data_ptr = NULL; errno = ENOMEM; } if(!data_ptr) { errno = ENOMEM; data_ptr = MAP_FAILED; } else { MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = flags; MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = prot; } } else { data_ptr = MAP_FAILED; errno = EINVAL; } MAGIC_MEM_WRAPPER_END(); return data_ptr; } #else /*===========================================================================* * magic_vm_map_cacheblock * *===========================================================================*/ PUBLIC void *magic_vm_map_cacheblock(__MA_ARGS__ dev_t dev, off_t dev_offset, ino_t ino, off_t ino_offset, u32_t *flags, int length) { void *ptr, *data_ptr, *aligned_ptr; int dsentry_flags = MAGIC_STATE_MAP; size_t alloc_length; size_t page_size = MAGIC_PAGE_SIZE; MAGIC_MEM_WRAPPER_BEGIN(); if(length > 0) { assert(MAGIC_SIZE_TO_REAL(length) <= page_size+length); alloc_length = length + (length % page_size == 0 ? 0 : page_size-(length % page_size)); data_ptr = vm_map_cacheblock(dev, dev_offset, ino, ino_offset, flags, length); if (data_ptr != MAP_FAILED) { ptr = mmap(data_ptr-page_size, page_size, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); MAGIC_MEM_PRINTF("vm_map_cacheblock: ptr = mmap(start, length, prot, flags, fd, offset) <-> 0x%08x = mmap(0x%08x, %d, 0x%08x, 0x%08x, %d, %d)\n", (unsigned) ptr, data_ptr-page_size, page_size, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); assert(ptr == data_ptr-page_size); /* Ensured by VM. */ aligned_ptr = ptr; ptr = ((char*)ptr) + page_size - MAGIC_SIZE_TO_REAL(0); } else { aligned_ptr = NULL; ptr = NULL; } data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, dsentry_flags); if(data_ptr == MAGIC_MEM_FAILED) { munmap(aligned_ptr, page_size+length); data_ptr = NULL; } if(!data_ptr) { data_ptr = MAP_FAILED; errno = ENOMEM; } else { MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = MAP_ANONYMOUS | MAP_PRIVATE; MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = magic_mmap_dsentry_header_prot; } } else { data_ptr = MAP_FAILED; errno = EINVAL; } MAGIC_MEM_WRAPPER_END(); return data_ptr; } /*===========================================================================* * magic_nested_mmap * *===========================================================================*/ void * magic_nested_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) { void *ptr; int i; ptr = mmap(start, length, prot, flags, fd, offset); if (ptr != MAP_FAILED) { MAGIC_MEM_PRINTF("MAGIC: nested mmap (%p, %zu)\n", ptr, length); /* * Find a free entry. We do not expect the malloc code to have * more than two areas mapped at any time. */ for (i = 0; i < MAGIC_UNMAP_MEM_ENTRIES; i++) if (_magic_unmap_mem[i].length == 0) break; assert(i < MAGIC_UNMAP_MEM_ENTRIES); /* Store the mapping in this entry. */ _magic_unmap_mem[i].start = ptr; _magic_unmap_mem[i].length = length; } return ptr; } /*===========================================================================* * magic_nested_munmap * *===========================================================================*/ int magic_nested_munmap(void *start, size_t length) { int i, r; r = munmap(start, length); if (r == 0) { MAGIC_MEM_PRINTF("MAGIC: nested munmap (%p, %zu)\n", start, length); /* Find the corresponding entry. This must always succeed. */ for (i = 0; i < MAGIC_UNMAP_MEM_ENTRIES; i++) if (_magic_unmap_mem[i].start == start && _magic_unmap_mem[i].length == length) break; assert(i < MAGIC_UNMAP_MEM_ENTRIES); /* Clear the entry. */ _magic_unmap_mem[i].start = NULL; _magic_unmap_mem[i].length = 0; } return r; } #endif