| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| /* |
| * Contains implementation of memory allocation routines instrumented for |
| * usage in the emulator to detect memory allocation violations, such as |
| * memory leaks, buffer overruns, etc. |
| * Code, implemented here is intended to run in the emulated environment only, |
| * and serves simply as hooks into memory allocation routines. Main job of this |
| * code is to notify the emulator about memory being allocated/deallocated, |
| * providing information about each allocation. The idea is that emulator will |
| * keep list of currently allocated blocks, and, knowing boundaries of each |
| * block it will be able to verify that ld/st access to these blocks don't step |
| * over boundaries set for the user. To enforce that, each memory block |
| * allocated by this code is guarded with "prefix" and "suffix" areas, so |
| * every time emulator detects access to any of these guarding areas, it can be |
| * considered as access violation. |
| */ |
| |
| #include <stdlib.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/param.h> |
| #include <pthread.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include "malloc_debug_common.h" |
| #include "private/bionic_macros.h" |
| #include "private/libc_logging.h" |
| |
| /* This file should be included into the build only when |
| * MALLOC_QEMU_INSTRUMENT macro is defined. */ |
| #ifndef MALLOC_QEMU_INSTRUMENT |
| #error MALLOC_QEMU_INSTRUMENT is not defined. |
| #endif // !MALLOC_QEMU_INSTRUMENT |
| |
| /* Controls access violation test performed to make sure that we catch AVs |
| * all the time they occur. See test_access_violation for more info. This macro |
| * is used for internal testing purposes and should always be set to zero for |
| * the production builds. */ |
| #define TEST_ACCESS_VIOLATIONS 0 |
| |
| // ============================================================================= |
| // Communication structures |
| // ============================================================================= |
| |
| /* Describes memory block allocated from the heap. This structure is passed |
| * along with TRACE_DEV_REG_MALLOC event. This descriptor is used to inform |
| * the emulator about new memory block being allocated from the heap. The entire |
| * structure is initialized by the guest system before event is fired up. It is |
| * important to remember that same structure (an exact copy, except for |
| * replacing pointers with target_ulong) is also declared in the emulator's |
| * sources (file memcheck/memcheck_common.h). So, every time a change is made to |
| * any of these two declaration, another one must be also updated accordingly. |
| */ |
| struct MallocDesc { |
| /* Pointer to the memory block actually allocated from the heap. Note that |
| * this is not the pointer that is returned to the malloc's caller. Pointer |
| * returned to the caller is calculated by adding value stored in this field |
| * to the value stored in prefix_size field of this structure. |
| */ |
| void* ptr; |
| |
| /* Number of bytes requested by the malloc's caller. */ |
| uint32_t requested_bytes; |
| |
| /* Byte size of the prefix data. Actual pointer returned to the malloc's |
| * caller is calculated by adding value stored in this field to the value |
| * stored in in the ptr field of this structure. |
| */ |
| uint32_t prefix_size; |
| |
| /* Byte size of the suffix data. */ |
| uint32_t suffix_size; |
| |
| /* Id of the process that initialized libc instance, in which allocation |
| * has occurred. This field is used by the emulator to report errors in |
| * the course of TRACE_DEV_REG_MALLOC event handling. In case of an error, |
| * emulator sets this field to zero (invalid value for a process ID). |
| */ |
| uint32_t libc_pid; |
| |
| /* Id of the process in context of which allocation has occurred. |
| * Value in this field may differ from libc_pid value, if process that |
| * is doing allocation has been forked from the process that initialized |
| * libc instance. |
| */ |
| uint32_t allocator_pid; |
| |
| /* Number of access violations detected on this allocation. */ |
| uint32_t av_count; |
| }; |
| |
| /* Describes memory block info queried from emulator. This structure is passed |
| * along with TRACE_DEV_REG_QUERY_MALLOC event. When handling free and realloc |
| * calls, it is required that we have information about memory blocks that were |
| * actually allocated in previous calls to malloc, calloc, memalign, or realloc. |
| * Since we don't keep this information directly in the allocated block, but |
| * rather we keep it in the emulator, we need to query emulator for that |
| * information with TRACE_DEV_REG_QUERY_MALLOC query. The entire structure is |
| * initialized by the guest system before event is fired up. It is important to |
| * remember that same structure (an exact copy, except for replacing pointers |
| * with target_ulong) is also declared in the emulator's sources (file |
| * memcheck/memecheck_common.h). So, every time a change is made to any of these |
| * two declaration, another one must be also updated accordingly. |
| */ |
| struct MallocDescQuery { |
| /* Pointer, for which information is queried. Note that this pointer doesn't |
| * have to be exact pointer returned to malloc's caller, but can point |
| * anywhere inside an allocated block, including guarding areas. Emulator |
| * will respond with information about allocated block that contains this |
| * pointer. |
| */ |
| const void* ptr; |
| |
| /* Id of the process that initialized libc instance, in which this query |
| * is called. This field is used by the emulator to report errors in |
| * the course of TRACE_DEV_REG_QUERY_MALLOC event handling. In case of an |
| * error, emulator sets this field to zero (invalid value for a process ID). |
| */ |
| uint32_t libc_pid; |
| |
| /* Process ID in context of which query is made. */ |
| uint32_t query_pid; |
| |
| /* Code of the allocation routine, in context of which query has been made: |
| * 1 - free |
| * 2 - realloc |
| */ |
| uint32_t routine; |
| |
| /* Address of memory allocation descriptor for the queried pointer. |
| * Descriptor, addressed by this field is initialized by the emulator in |
| * response to the query. |
| */ |
| MallocDesc* desc; |
| }; |
| |
| /* Describes memory block that is being freed back to the heap. This structure |
| * is passed along with TRACE_DEV_REG_FREE_PTR event. The entire structure is |
| * initialized by the guest system before event is fired up. It is important to |
| * remember that same structure (an exact copy, except for replacing pointers |
| * with target_ulong) is also declared in the emulator's sources (file |
| * memcheck/memecheck_common.h). So, every time a change is made to any of these |
| * two declaration, another one must be also updated accordingly. |
| */ |
| struct MallocFree { |
| /* Pointer to be freed. */ |
| void* ptr; |
| |
| /* Id of the process that initialized libc instance, in which this free |
| * is called. This field is used by the emulator to report errors in |
| * the course of TRACE_DEV_REG_FREE_PTR event handling. In case of an |
| * error, emulator sets this field to zero (invalid value for a process ID). |
| */ |
| uint32_t libc_pid; |
| |
| /* Process ID in context of which memory is being freed. */ |
| uint32_t free_pid; |
| }; |
| |
| // ============================================================================= |
| // Communication events |
| // ============================================================================= |
| |
| /* Notifies the emulator that libc has been initialized for a process. |
| * Event's value parameter is PID for the process in context of which libc has |
| * been initialized. |
| */ |
| #define TRACE_DEV_REG_LIBC_INIT 1536 |
| |
| /* Notifies the emulator about new memory block been allocated. |
| * Event's value parameter points to MallocDesc instance that contains |
| * allocated block information. Note that 'libc_pid' field of the descriptor |
| * is used by emulator to report failure in handling this event. In case |
| * of a failure emulator will zero that field before completing this event. |
| */ |
| #define TRACE_DEV_REG_MALLOC 1537 |
| |
| /* Notifies the emulator about memory block being freed. |
| * Event's value parameter points to MallocFree descriptor that contains |
| * information about block that's being freed. Note that 'libc_pid' field |
| * of the descriptor is used by emulator to report failure in handling this |
| * event. In case of a failure emulator will zero that field before completing |
| * this event. |
| */ |
| #define TRACE_DEV_REG_FREE_PTR 1538 |
| |
| /* Queries the emulator about allocated memory block information. |
| * Event's value parameter points to MallocDescQuery descriptor that contains |
| * query parameters. Note that 'libc_pid' field of the descriptor is used by |
| * emulator to report failure in handling this event. In case of a failure |
| * emulator will zero that field before completing this event. |
| */ |
| #define TRACE_DEV_REG_QUERY_MALLOC 1539 |
| |
| /* Queries the emulator to print a string to its stdout. |
| * Event's value parameter points to a zero-terminated string to be printed. |
| */ |
| #define TRACE_DEV_REG_PRINT_USER_STR 1540 |
| |
| static void notify_qemu_string(const char* str); |
| static void qemu_log(int prio, const char* fmt, ...); |
| static void dump_malloc_descriptor(char* str, |
| size_t str_buf_size, |
| const MallocDesc* desc); |
| |
| // ============================================================================= |
| // Macros |
| // ============================================================================= |
| |
| /* Defines default size of allocation prefix. |
| * Note that we make prefix area quite large in order to increase chances of |
| * catching buffer overflow. */ |
| #define DEFAULT_PREFIX_SIZE (malloc_alignment * 4) |
| |
| /* Defines default size of allocation suffix. |
| * Note that we make suffix area quite large in order to increase chances of |
| * catching buffer overflow. */ |
| #define DEFAULT_SUFFIX_SIZE (malloc_alignment * 4) |
| |
| /* Debug tracing has been enabled by the emulator. */ |
| #define DEBUG_TRACING_ENABLED 0x00000001 |
| /* Error tracing has been enabled by the emulator. */ |
| #define ERROR_TRACING_ENABLED 0x00000002 |
| /* Info tracing has been enabled by the emulator. */ |
| #define INFO_TRACING_ENABLED 0x00000004 |
| /* All tracing flags combined. */ |
| #define ALL_TRACING_ENABLED (DEBUG_TRACING_ENABLED | \ |
| ERROR_TRACING_ENABLED | \ |
| INFO_TRACING_ENABLED) |
| |
| /* Prints a string to the emulator's stdout. |
| * In early stages of system loading, logging messages to logcat |
| * is not available, because ADB API has not been |
| * hooked up yet. So, in order to see such messages we need to print them to |
| * the emulator's stdout. |
| * Parameters passed to this macro are the same as parameters for printf |
| * routine. |
| */ |
| #define TR(...) \ |
| do { \ |
| char tr_str[4096]; \ |
| snprintf(tr_str, sizeof(tr_str), __VA_ARGS__); \ |
| tr_str[sizeof(tr_str) - 1] = '\0'; \ |
| notify_qemu_string(&tr_str[0]); \ |
| } while (0) |
| |
| // ============================================================================= |
| // Logging macros. Note that we simultaneously log messages to ADB and emulator. |
| // ============================================================================= |
| |
| /* |
| * Helper macros for checking if particular trace level is enabled. |
| */ |
| #define debug_LOG_ENABLED ((tracing_flags & DEBUG_TRACING_ENABLED) != 0) |
| #define error_LOG_ENABLED ((tracing_flags & ERROR_TRACING_ENABLED) != 0) |
| #define info_LOG_ENABLED ((tracing_flags & INFO_TRACING_ENABLED) != 0) |
| #define tracing_enabled(type) (type##_LOG_ENABLED) |
| |
| /* |
| * Logging helper macros. |
| */ |
| #define qemu_debug_log(format, ...) \ |
| do { \ |
| __libc_format_log(ANDROID_LOG_DEBUG, "memcheck", (format), ##__VA_ARGS__); \ |
| if (tracing_flags & DEBUG_TRACING_ENABLED) { \ |
| qemu_log(ANDROID_LOG_DEBUG, (format), ##__VA_ARGS__); \ |
| } \ |
| } while (0) |
| |
| #define qemu_error_log(format, ...) \ |
| do { \ |
| __libc_format_log(ANDROID_LOG_ERROR, "memcheck", (format), ##__VA_ARGS__); \ |
| if (tracing_flags & ERROR_TRACING_ENABLED) { \ |
| qemu_log(ANDROID_LOG_ERROR, (format), ##__VA_ARGS__); \ |
| } \ |
| } while (0) |
| |
| #define qemu_info_log(format, ...) \ |
| do { \ |
| __libc_format_log(ANDROID_LOG_INFO, "memcheck", (format), ##__VA_ARGS__); \ |
| if (tracing_flags & INFO_TRACING_ENABLED) { \ |
| qemu_log(ANDROID_LOG_INFO, (format), ##__VA_ARGS__); \ |
| } \ |
| } while (0) |
| |
| /* Logs message dumping MallocDesc instance at the end of the message. |
| * Param: |
| * type - Message type: debug, error, or info |
| * desc - MallocDesc instance to dump. |
| * fmt + rest - Formats message preceding dumped descriptor. |
| */ |
| #define log_mdesc(type, desc, fmt, ...) \ |
| do { \ |
| if (tracing_enabled(type)) { \ |
| char log_str[4096]; \ |
| __libc_format_buffer(log_str, sizeof(log_str), fmt, ##__VA_ARGS__); \ |
| log_str[sizeof(log_str) - 1] = '\0'; \ |
| size_t str_len = strlen(log_str); \ |
| dump_malloc_descriptor(log_str + str_len, \ |
| sizeof(log_str) - str_len, \ |
| (desc)); \ |
| type##_log("%s", log_str); \ |
| } \ |
| } while (0) |
| |
| // ============================================================================= |
| // Static data |
| // ============================================================================= |
| |
| // The underlying malloc implementation to use to get memory. |
| static const MallocDebug* g_malloc_dispatch = NULL; |
| |
| /* Emulator's magic page address. |
| * This page (mapped on /dev/qemu_trace device) is used to fire up events |
| * in the emulator. */ |
| static volatile void* qtrace = NULL; |
| |
| /* Cached PID of the process in context of which this libc instance |
| * has been initialized. */ |
| static uint32_t malloc_pid = 0; |
| |
| /* Memory allocation alignment that is used in the malloc implementation. |
| * This variable is updated by memcheck_initialize routine. */ |
| static uint32_t malloc_alignment = 8; |
| |
| /* Tracing flags. These flags control which types of logging messages are |
| * enabled by the emulator. See XXX_TRACING_ENABLED for the values of flags |
| * stored in this variable. This variable is updated by memcheck_initialize |
| * routine. */ |
| static uint32_t tracing_flags = 0; |
| |
| // ============================================================================= |
| // Static routines |
| // ============================================================================= |
| |
| /* Gets pointer, returned to malloc caller for the given allocation decriptor. |
| * Param: |
| * desc - Allocation descriptor. |
| * Return: |
| * Pointer to the allocated memory returned to the malloc caller. |
| */ |
| static inline void* mallocdesc_user_ptr(const MallocDesc* desc) { |
| return static_cast<char*>(desc->ptr) + desc->prefix_size; |
| } |
| |
| /* Gets size of memory block actually allocated from the heap for the given |
| * allocation decriptor. |
| * Param: |
| * desc - Allocation descriptor. |
| * Return: |
| * Size of memory block actually allocated from the heap. |
| */ |
| static inline uint32_t mallocdesc_alloc_size(const MallocDesc* desc) { |
| return desc->prefix_size + desc->requested_bytes + desc->suffix_size; |
| } |
| |
| /* Gets pointer to the end of the allocated block for the given descriptor. |
| * Param: |
| * desc - Descriptor for the memory block, allocated in malloc handler. |
| * Return: |
| * Pointer to the end of (one byte past) the allocated block. |
| */ |
| static inline void* mallocdesc_alloc_end(const MallocDesc* desc) { |
| return static_cast<char*>(desc->ptr) + mallocdesc_alloc_size(desc); |
| } |
| |
| /* Fires up an event in the emulator. |
| * Param: |
| * code - Event code (one of the TRACE_DEV_XXX). |
| * val - Event's value parameter. |
| */ |
| static inline void notify_qemu(uint32_t code, uintptr_t val) { |
| if (NULL != qtrace) { |
| *(volatile uintptr_t*)((uintptr_t)qtrace + ((code - 1024) << 2)) = val; |
| } |
| } |
| |
| /* Prints a zero-terminated string to the emulator's stdout (fires up |
| * TRACE_DEV_REG_PRINT_USER_STR event in the emulator). |
| * Param: |
| * str - Zero-terminated string to print. |
| */ |
| static void notify_qemu_string(const char* str) { |
| if (str != NULL) { |
| notify_qemu(TRACE_DEV_REG_PRINT_USER_STR, reinterpret_cast<uintptr_t>(str)); |
| } |
| } |
| |
| /* Fires up TRACE_DEV_REG_LIBC_INIT event in the emulator. |
| * Param: |
| * pid - ID of the process that initialized libc. |
| */ |
| static void notify_qemu_libc_initialized(uint32_t pid) { |
| notify_qemu(TRACE_DEV_REG_LIBC_INIT, pid); |
| } |
| |
| /* Fires up TRACE_DEV_REG_MALLOC event in the emulator. |
| * Param: |
| * desc - Pointer to MallocDesc instance containing allocated block |
| * information. |
| * Return: |
| * Zero on success, or -1 on failure. Note that on failure libc_pid field of |
| * the desc parameter passed to this routine has been zeroed out by the |
| * emulator. |
| */ |
| static inline int notify_qemu_malloc(volatile MallocDesc* desc) { |
| desc->libc_pid = malloc_pid; |
| desc->allocator_pid = getpid(); |
| desc->av_count = 0; |
| notify_qemu(TRACE_DEV_REG_MALLOC, reinterpret_cast<uintptr_t>(desc)); |
| |
| /* Emulator reports failure by zeroing libc_pid field of the |
| * descriptor. */ |
| return desc->libc_pid != 0 ? 0 : -1; |
| } |
| |
| /* Fires up TRACE_DEV_REG_FREE_PTR event in the emulator. |
| * Param: |
| * ptr - Pointer to the memory block that's being freed. |
| * Return: |
| * Zero on success, or -1 on failure. |
| */ |
| static inline int notify_qemu_free(void* ptr_to_free) { |
| volatile MallocFree free_desc; |
| |
| free_desc.ptr = ptr_to_free; |
| free_desc.libc_pid = malloc_pid; |
| free_desc.free_pid = getpid(); |
| notify_qemu(TRACE_DEV_REG_FREE_PTR, reinterpret_cast<uintptr_t>(&free_desc)); |
| |
| /* Emulator reports failure by zeroing libc_pid field of the |
| * descriptor. */ |
| return free_desc.libc_pid != 0 ? 0 : -1; |
| } |
| |
| /* Fires up TRACE_DEV_REG_QUERY_MALLOC event in the emulator. |
| * Param: |
| * ptr - Pointer to request allocation information for. |
| * desc - Pointer to MallocDesc instance that will receive allocation |
| * information. |
| * routine - Code of the allocation routine, in context of which query is made: |
| * 1 - free |
| * 2 - realloc |
| * Return: |
| * Zero on success, or -1 on failure. |
| */ |
| static inline int query_qemu_malloc_info(const void* ptr, MallocDesc* desc, uint32_t routine) { |
| volatile MallocDescQuery query; |
| |
| query.ptr = ptr; |
| query.libc_pid = malloc_pid; |
| query.query_pid = getpid(); |
| query.routine = routine; |
| query.desc = desc; |
| notify_qemu(TRACE_DEV_REG_QUERY_MALLOC, reinterpret_cast<uintptr_t>(&query)); |
| |
| /* Emulator reports failure by zeroing libc_pid field of the |
| * descriptor. */ |
| return query.libc_pid != 0 ? 0 : -1; |
| } |
| |
| /* Logs a message to emulator's stdout. |
| * Param: |
| * prio - Message priority (debug, info, or error) |
| * fmt + rest - Message format and parameters. |
| */ |
| static void qemu_log(int prio, const char* fmt, ...) { |
| va_list ap; |
| char buf[4096]; |
| const char* prefix; |
| |
| /* Choose message prefix depending on the priority value. */ |
| switch (prio) { |
| case ANDROID_LOG_ERROR: |
| if (!tracing_enabled(error)) { |
| return; |
| } |
| prefix = "E"; |
| break; |
| case ANDROID_LOG_INFO: |
| if (!tracing_enabled(info)) { |
| return; |
| } |
| prefix = "I"; |
| break; |
| case ANDROID_LOG_DEBUG: |
| default: |
| if (!tracing_enabled(debug)) { |
| return; |
| } |
| prefix = "D"; |
| break; |
| } |
| |
| va_start(ap, fmt); |
| vsnprintf(buf, sizeof(buf), fmt, ap); |
| va_end(ap); |
| buf[sizeof(buf) - 1] = '\0'; |
| |
| TR("%s/memcheck: %s\n", prefix, buf); |
| } |
| |
| /* Dumps content of memory allocation descriptor to a string. |
| * Param: |
| * str - String to dump descriptor to. |
| * str_buf_size - Size of string's buffer. |
| * desc - Descriptor to dump. |
| */ |
| static void dump_malloc_descriptor(char* str, size_t str_buf_size, const MallocDesc* desc) { |
| if (str_buf_size) { |
| snprintf(str, str_buf_size, |
| "MDesc: %p: %p <-> %p [%u + %u + %u] by pid=%03u in libc_pid=%03u", |
| mallocdesc_user_ptr(desc), desc->ptr, |
| mallocdesc_alloc_end(desc), desc->prefix_size, |
| desc->requested_bytes, desc->suffix_size, desc->allocator_pid, |
| desc->libc_pid); |
| str[str_buf_size - 1] = '\0'; |
| } |
| } |
| |
| #if TEST_ACCESS_VIOLATIONS |
| /* Causes an access violation on allocation descriptor, and verifies that |
| * violation has been detected by memory checker in the emulator. |
| */ |
| static void test_access_violation(const MallocDesc* desc) { |
| MallocDesc desc_chk; |
| char ch; |
| volatile char* prefix = (volatile char*)desc->ptr; |
| volatile char* suffix = (volatile char*)mallocdesc_user_ptr(desc) + |
| desc->requested_bytes; |
| /* We're causing AV by reading from the prefix and suffix areas of the |
| * allocated block. This should produce two access violations, so when we |
| * get allocation descriptor from QEMU, av_counter should be bigger than |
| * av_counter of the original descriptor by 2. */ |
| ch = *prefix; |
| ch = *suffix; |
| if (!query_qemu_malloc_info(mallocdesc_user_ptr(desc), &desc_chk, 2) && |
| desc_chk.av_count != (desc->av_count + 2)) { |
| log_mdesc(error, &desc_chk, |
| "<libc_pid=%03u, pid=%03u>: malloc: Access violation test failed:\n" |
| "Expected violations count %u is not equal to the actually reported %u", |
| malloc_pid, getpid(), desc->av_count + 2, |
| desc_chk.av_count); |
| } |
| } |
| #endif // TEST_ACCESS_VIOLATIONS |
| |
| // ============================================================================= |
| // API routines |
| // ============================================================================= |
| |
| extern "C" void* qemu_instrumented_calloc(size_t, size_t); |
| extern "C" void qemu_instrumented_free(void*); |
| extern "C" struct mallinfo qemu_instrumented_mallinfo(); |
| extern "C" void* qemu_instrumented_malloc(size_t); |
| extern "C" size_t qemu_instrumented_malloc_usable_size(const void*); |
| extern "C" void* qemu_instrumented_memalign(size_t, size_t); |
| extern "C" int qemu_instrumented_posix_memalign(void**, size_t, size_t); |
| extern "C" void* qemu_instrumented_pvalloc(size_t); |
| extern "C" void* qemu_instrumented_realloc(void*, size_t); |
| extern "C" void* qemu_instrumented_valloc(size_t); |
| |
| /* Initializes malloc debugging instrumentation for the emulator. |
| * This routine is called from malloc_init_impl routine implemented in |
| * bionic/libc/bionic/malloc_debug_common.c when malloc debugging gets |
| * initialized for a process. The way malloc debugging implementation is |
| * done, it is guaranteed that this routine will be called just once per |
| * process. |
| * Return: |
| * 0 on success, or -1 on failure. |
| */ |
| extern "C" bool malloc_debug_initialize(HashTable*, const MallocDebug* malloc_dispatch) { |
| g_malloc_dispatch = malloc_dispatch; |
| |
| /* We will be using emulator's magic page to report memory allocation |
| * activities. In essence, what magic page does, it translates writes to |
| * the memory mapped spaces into writes to an I/O port that emulator |
| * "listens to" on the other end. Note that until we open and map that |
| * device, logging to emulator's stdout will not be available. */ |
| int fd = open("/dev/qemu_trace", O_RDWR); |
| if (fd < 0) { |
| error_log("Unable to open /dev/qemu_trace"); |
| return false; |
| } else { |
| qtrace = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| close(fd); |
| |
| if (qtrace == MAP_FAILED) { |
| qtrace = NULL; |
| error_log("Unable to mmap /dev/qemu_trace"); |
| return false; |
| } |
| } |
| |
| /* Cache pid of the process this library has been initialized for. */ |
| malloc_pid = getpid(); |
| return true; |
| } |
| |
| /* Completes malloc debugging instrumentation for the emulator. |
| * Note that this routine is called after successful return from |
| * malloc_debug_initialize, which means that connection to the emulator via |
| * "magic page" has been established. |
| * Param: |
| * alignment - Alignment requirement set for memiry allocations. |
| * memcheck_param - Emulator's -memcheck option parameters. This string |
| * contains abbreviation for guest events that are enabled for tracing. |
| * Return: |
| * 0 on success, or -1 on failure. |
| */ |
| extern "C" int memcheck_initialize(int alignment, const char* memcheck_param) { |
| malloc_alignment = alignment; |
| |
| /* Parse -memcheck parameter for the guest tracing flags. */ |
| while (*memcheck_param != '\0') { |
| switch (*memcheck_param) { |
| case 'a': |
| // Enable all messages from the guest. |
| tracing_flags |= ALL_TRACING_ENABLED; |
| break; |
| case 'd': |
| // Enable debug messages from the guest. |
| tracing_flags |= DEBUG_TRACING_ENABLED; |
| break; |
| case 'e': |
| // Enable error messages from the guest. |
| tracing_flags |= ERROR_TRACING_ENABLED; |
| break; |
| case 'i': |
| // Enable info messages from the guest. |
| tracing_flags |= INFO_TRACING_ENABLED; |
| break; |
| default: |
| break; |
| } |
| if (tracing_flags == ALL_TRACING_ENABLED) { |
| break; |
| } |
| memcheck_param++; |
| } |
| |
| notify_qemu_libc_initialized(malloc_pid); |
| |
| qemu_debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p", |
| malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free, |
| qemu_instrumented_calloc, qemu_instrumented_realloc, |
| qemu_instrumented_memalign); |
| |
| return 0; |
| } |
| |
| /* This routine serves as entry point for 'malloc'. |
| * Primary responsibility of this routine is to allocate requested number of |
| * bytes (plus prefix, and suffix guards), and report allocation to the |
| * emulator. |
| */ |
| extern "C" void* qemu_instrumented_malloc(size_t bytes) { |
| MallocDesc desc; |
| |
| /* Initialize block descriptor and allocate memory. Note that malloc |
| * returns a valid pointer on zero allocation. Lets mimic this behavior. */ |
| desc.prefix_size = DEFAULT_PREFIX_SIZE; |
| desc.requested_bytes = bytes; |
| desc.suffix_size = DEFAULT_SUFFIX_SIZE; |
| size_t size = mallocdesc_alloc_size(&desc); |
| if (size < bytes) { // Overflow |
| qemu_error_log("<libc_pid=%03u, pid=%03u> malloc: malloc(%zu) overflow caused failure.", |
| malloc_pid, getpid(), bytes); |
| errno = ENOMEM; |
| return NULL; |
| } |
| desc.ptr = g_malloc_dispatch->malloc(size); |
| if (desc.ptr == NULL) { |
| qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%zu): malloc(%zu) failed.", |
| malloc_pid, getpid(), bytes, size); |
| return NULL; |
| } |
| |
| // Fire up event in the emulator. |
| if (notify_qemu_malloc(&desc)) { |
| log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: malloc: notify_malloc failed for ", |
| malloc_pid, getpid()); |
| g_malloc_dispatch->free(desc.ptr); |
| errno = ENOMEM; |
| return NULL; |
| } else { |
| #if TEST_ACCESS_VIOLATIONS |
| test_access_violation(&desc); |
| #endif // TEST_ACCESS_VIOLATIONS |
| log_mdesc(info, &desc, "+++ <libc_pid=%03u, pid=%03u> malloc(%zu) -> ", |
| malloc_pid, getpid(), bytes); |
| return mallocdesc_user_ptr(&desc); |
| } |
| } |
| |
| /* This routine serves as entry point for 'malloc'. |
| * Primary responsibility of this routine is to free requested memory, and |
| * report free block to the emulator. |
| */ |
| extern "C" void qemu_instrumented_free(void* mem) { |
| MallocDesc desc; |
| |
| if (mem == NULL) { |
| // Just let go NULL free |
| g_malloc_dispatch->free(mem); |
| return; |
| } |
| |
| // Query emulator for the freeing block information. |
| if (query_qemu_malloc_info(mem, &desc, 1)) { |
| error_log("<libc_pid=%03u, pid=%03u>: free(%p) query_info failed.", |
| malloc_pid, getpid(), mem); |
| return; |
| } |
| |
| #if TEST_ACCESS_VIOLATIONS |
| test_access_violation(&desc); |
| #endif // TEST_ACCESS_VIOLATIONS |
| |
| /* Make sure that pointer that's being freed matches what we expect |
| * for this memory block. Note that this violation should be already |
| * caught in the emulator. */ |
| if (mem != mallocdesc_user_ptr(&desc)) { |
| log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) is invalid for ", |
| malloc_pid, getpid(), mem); |
| return; |
| } |
| |
| // Fire up event in the emulator and free block that was actually allocated. |
| if (notify_qemu_free(mem)) { |
| log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) notify_free failed for ", |
| malloc_pid, getpid(), mem); |
| } else { |
| log_mdesc(info, &desc, "--- <libc_pid=%03u, pid=%03u> free(%p) -> ", |
| malloc_pid, getpid(), mem); |
| g_malloc_dispatch->free(desc.ptr); |
| } |
| } |
| |
| /* This routine serves as entry point for 'calloc'. |
| * This routine behaves similarly to qemu_instrumented_malloc. |
| */ |
| extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) { |
| if (n_elements == 0 || elem_size == 0) { |
| // Just let go zero bytes allocation. |
| qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc", |
| malloc_pid, getpid()); |
| return qemu_instrumented_malloc(0); |
| } |
| |
| // Fail on overflow - just to be safe even though this code runs only |
| // within the debugging C library, not the production one. |
| if (n_elements && SIZE_MAX / n_elements < elem_size) { |
| qemu_error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu, %zu) overflow caused failure.", |
| malloc_pid, getpid(), n_elements, elem_size); |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| MallocDesc desc; |
| |
| /* Calculating prefix size. The trick here is to make sure that |
| * first element (returned to the caller) is properly aligned. */ |
| if (DEFAULT_PREFIX_SIZE >= elem_size) { |
| /* If default alignment is bigger than element size, we will |
| * set our prefix size to the default alignment size. */ |
| desc.prefix_size = DEFAULT_PREFIX_SIZE; |
| /* For the suffix we will use whatever bytes remain from the prefix |
| * allocation size, aligned to the size of an element, plus the usual |
| * default suffix size. */ |
| desc.suffix_size = (DEFAULT_PREFIX_SIZE % elem_size) + |
| DEFAULT_SUFFIX_SIZE; |
| } else { |
| /* Make sure that prefix, and suffix sizes is at least elem_size, |
| * and first element returned to the caller is properly aligned. */ |
| desc.prefix_size = elem_size + DEFAULT_PREFIX_SIZE - 1; |
| desc.prefix_size &= ~(malloc_alignment - 1); |
| desc.suffix_size = DEFAULT_SUFFIX_SIZE; |
| } |
| desc.requested_bytes = n_elements * elem_size; |
| size_t total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_size; |
| if (total_size < desc.requested_bytes) { // Overflow |
| qemu_error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu, %zu) overflow caused failure.", |
| malloc_pid, getpid(), n_elements, elem_size); |
| errno = ENOMEM; |
| return NULL; |
| } |
| size_t total_elements = total_size / elem_size; |
| total_size %= elem_size; |
| if (total_size != 0) { |
| // Add extra to the suffix area. |
| total_elements++; |
| desc.suffix_size += (elem_size - total_size); |
| } |
| desc.ptr = g_malloc_dispatch->calloc(total_elements, elem_size); |
| if (desc.ptr == NULL) { |
| error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu(%zu), %zu) (prx=%u, sfx=%u) failed.", |
| malloc_pid, getpid(), n_elements, total_elements, elem_size, |
| desc.prefix_size, desc.suffix_size); |
| return NULL; |
| } |
| |
| if (notify_qemu_malloc(&desc)) { |
| log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: calloc(%zu(%zu), %zu): notify_malloc failed for ", |
| malloc_pid, getpid(), n_elements, total_elements, elem_size); |
| g_malloc_dispatch->free(desc.ptr); |
| errno = ENOMEM; |
| return NULL; |
| } else { |
| #if TEST_ACCESS_VIOLATIONS |
| test_access_violation(&desc); |
| #endif // TEST_ACCESS_VIOLATIONS |
| log_mdesc(info, &desc, "### <libc_pid=%03u, pid=%03u> calloc(%zu(%zu), %zu) -> ", |
| malloc_pid, getpid(), n_elements, total_elements, elem_size); |
| return mallocdesc_user_ptr(&desc); |
| } |
| } |
| |
| /* This routine serves as entry point for 'realloc'. |
| * This routine behaves similarly to qemu_instrumented_free + |
| * qemu_instrumented_malloc. Note that this modifies behavior of "shrinking" an |
| * allocation, but overall it doesn't seem to matter, as caller of realloc |
| * should not expect that pointer returned after shrinking will remain the same. |
| */ |
| extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) { |
| if (mem == NULL) { |
| // Nothing to realloc. just do regular malloc. |
| qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to malloc", |
| malloc_pid, getpid(), mem, bytes); |
| return qemu_instrumented_malloc(bytes); |
| } |
| |
| if (bytes == 0) { |
| // This is a "free" condition. |
| qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to free and malloc", |
| malloc_pid, getpid(), mem, bytes); |
| qemu_instrumented_free(mem); |
| |
| // This is what realloc does for a "free" realloc. |
| return NULL; |
| } |
| |
| // Query emulator for the reallocating block information. |
| MallocDesc cur_desc; |
| if (query_qemu_malloc_info(mem, &cur_desc, 2)) { |
| // Note that this violation should be already caught in the emulator. |
| error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) query_info failed.", |
| malloc_pid, getpid(), mem, bytes); |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| #if TEST_ACCESS_VIOLATIONS |
| test_access_violation(&cur_desc); |
| #endif // TEST_ACCESS_VIOLATIONS |
| |
| /* Make sure that reallocating pointer value is what we would expect |
| * for this memory block. Note that this violation should be already caught |
| * in the emulator.*/ |
| if (mem != mallocdesc_user_ptr(&cur_desc)) { |
| log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) is invalid for ", |
| malloc_pid, getpid(), mem, bytes); |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| /* TODO: We're a bit inefficient here, always allocating new block from |
| * the heap. If this realloc shrinks current buffer, we can just do the |
| * shrinking "in place", adjusting suffix_size in the allocation descriptor |
| * for this block that is stored in the emulator. */ |
| |
| // Initialize descriptor for the new block. |
| MallocDesc new_desc; |
| new_desc.prefix_size = DEFAULT_PREFIX_SIZE; |
| new_desc.requested_bytes = bytes; |
| new_desc.suffix_size = DEFAULT_SUFFIX_SIZE; |
| size_t new_size = mallocdesc_alloc_size(&new_desc); |
| if (new_size < bytes) { // Overflow |
| qemu_error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%zu) failed due to overflow", |
| malloc_pid, getpid(), mem, bytes, new_size); |
| errno = ENOMEM; |
| return NULL; |
| } |
| new_desc.ptr = g_malloc_dispatch->malloc(new_size); |
| if (new_desc.ptr == NULL) { |
| log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%zu) failed on ", |
| malloc_pid, getpid(), mem, bytes, new_size); |
| return NULL; |
| } |
| void* new_mem = mallocdesc_user_ptr(&new_desc); |
| |
| // Copy user data from old block to the new one. |
| size_t to_copy = bytes < cur_desc.requested_bytes ? bytes : cur_desc.requested_bytes; |
| if (to_copy != 0) { |
| memcpy(new_mem, mallocdesc_user_ptr(&cur_desc), to_copy); |
| } |
| |
| // Register new block with emulator. |
| if (notify_qemu_malloc(&new_desc)) { |
| log_mdesc(error, &new_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) notify_malloc failed -> ", |
| malloc_pid, getpid(), mem, bytes); |
| log_mdesc(error, &cur_desc, " <- "); |
| g_malloc_dispatch->free(new_desc.ptr); |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| #if TEST_ACCESS_VIOLATIONS |
| test_access_violation(&new_desc); |
| #endif // TEST_ACCESS_VIOLATIONS |
| |
| // Free old block. |
| if (notify_qemu_free(mem)) { |
| log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): notify_free failed for ", |
| malloc_pid, getpid(), mem, bytes); |
| /* Since we registered new decriptor with the emulator, we need |
| * to unregister it before freeing newly allocated block. */ |
| notify_qemu_free(mallocdesc_user_ptr(&new_desc)); |
| g_malloc_dispatch->free(new_desc.ptr); |
| errno = ENOMEM; |
| return NULL; |
| } |
| g_malloc_dispatch->free(cur_desc.ptr); |
| |
| log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) -> ", |
| malloc_pid, getpid(), mem, bytes); |
| log_mdesc(info, &cur_desc, " <- "); |
| |
| return new_mem; |
| } |
| |
| /* This routine serves as entry point for 'memalign'. |
| * This routine behaves similarly to qemu_instrumented_malloc. |
| */ |
| extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes) { |
| MallocDesc desc; |
| |
| if (bytes == 0) { |
| // Just let go zero bytes allocation. |
| qemu_info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%zx, %zu) redir to malloc", |
| malloc_pid, getpid(), alignment, bytes); |
| return qemu_instrumented_malloc(0); |
| } |
| |
| // Prefix size for aligned allocation must be equal to the alignment used |
| // for allocation in order to ensure proper alignment of the returned |
| // pointer. in case that alignment requirement is greater than prefix |
| // size. |
| if (alignment < DEFAULT_PREFIX_SIZE) { |
| alignment = DEFAULT_PREFIX_SIZE; |
| } else if (!powerof2(alignment)) { |
| alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment); |
| } |
| desc.prefix_size = alignment; |
| desc.requested_bytes = bytes; |
| desc.suffix_size = DEFAULT_SUFFIX_SIZE; |
| size_t size = mallocdesc_alloc_size(&desc); |
| if (size < bytes) { // Overflow |
| qemu_error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%zu) failed due to overflow.", |
| malloc_pid, getpid(), alignment, bytes, size); |
| |
| return NULL; |
| } |
| desc.ptr = g_malloc_dispatch->memalign(desc.prefix_size, size); |
| if (desc.ptr == NULL) { |
| error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%zu) failed.", |
| malloc_pid, getpid(), alignment, bytes, size); |
| return NULL; |
| } |
| if (notify_qemu_malloc(&desc)) { |
| log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%zx, %zu): notify_malloc failed for ", |
| malloc_pid, getpid(), alignment, bytes); |
| g_malloc_dispatch->free(desc.ptr); |
| return NULL; |
| } |
| |
| #if TEST_ACCESS_VIOLATIONS |
| test_access_violation(&desc); |
| #endif // TEST_ACCESS_VIOLATIONS |
| |
| log_mdesc(info, &desc, "@@@ <libc_pid=%03u, pid=%03u> memalign(%zx, %zu) -> ", |
| malloc_pid, getpid(), alignment, bytes); |
| return mallocdesc_user_ptr(&desc); |
| } |
| |
| extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem) { |
| MallocDesc cur_desc; |
| |
| // Query emulator for the reallocating block information. |
| if (query_qemu_malloc_info(mem, &cur_desc, 2)) { |
| // Note that this violation should be already caught in the emulator. |
| error_log("<libc_pid=%03u, pid=%03u>: malloc_usable_size(%p) query_info failed.", |
| malloc_pid, getpid(), mem); |
| return 0; |
| } |
| |
| /* Make sure that reallocating pointer value is what we would expect |
| * for this memory block. Note that this violation should be already caught |
| * in the emulator.*/ |
| if (mem != mallocdesc_user_ptr(&cur_desc)) { |
| log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: malloc_usable_size(%p) is invalid for ", |
| malloc_pid, getpid(), mem); |
| return 0; |
| } |
| |
| /* during instrumentation, we can't really report anything more than requested_bytes */ |
| return cur_desc.requested_bytes; |
| } |
| |
| extern "C" struct mallinfo qemu_instrumented_mallinfo() { |
| return g_malloc_dispatch->mallinfo(); |
| } |
| |
| extern "C" int qemu_instrumented_posix_memalign(void** memptr, size_t alignment, size_t size) { |
| if ((alignment & (alignment - 1)) != 0) { |
| qemu_error_log("<libc_pid=%03u, pid=%03u> posix_memalign(%p, %zu, %zu): invalid alignment.", |
| malloc_pid, getpid(), memptr, alignment, size); |
| return EINVAL; |
| } |
| int saved_errno = errno; |
| *memptr = qemu_instrumented_memalign(alignment, size); |
| errno = saved_errno; |
| return (*memptr != NULL) ? 0 : ENOMEM; |
| } |
| |
| extern "C" void* qemu_instrumented_pvalloc(size_t bytes) { |
| size_t pagesize = sysconf(_SC_PAGESIZE); |
| size_t size = BIONIC_ALIGN(bytes, pagesize); |
| if (size < bytes) { // Overflow |
| qemu_error_log("<libc_pid=%03u, pid=%03u> pvalloc(%zu): overflow (%zu).", |
| malloc_pid, getpid(), bytes, size); |
| return NULL; |
| } |
| return qemu_instrumented_memalign(pagesize, size); |
| } |
| |
| extern "C" void* qemu_instrumented_valloc(size_t size) { |
| return qemu_instrumented_memalign(sysconf(_SC_PAGESIZE), size); |
| } |