summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2010-02-17 13:43:15 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-02-17 13:43:15 -0800
commitb55462328f7f7468bb8c0cb44ad1069af30415a3 (patch)
tree291cf73e304ef6840de39e4732d0d8488955f22b
parentf31fceaae66dcc1f71f930ed8f2a777b80fe415f (diff)
parent75fba6888a1e5738f8255f3511c4ad40cbcc0eda (diff)
downloadbionic-b55462328f7f7468bb8c0cb44ad1069af30415a3.zip
bionic-b55462328f7f7468bb8c0cb44ad1069af30415a3.tar.gz
bionic-b55462328f7f7468bb8c0cb44ad1069af30415a3.tar.bz2
Merge "Merge memory checking functionality from sandbox"
-rw-r--r--.gitignore1
-rw-r--r--libc/bionic/malloc_debug_common.c122
-rw-r--r--libc/bionic/malloc_debug_common.h15
-rw-r--r--libc/bionic/malloc_debug_leak.c9
-rw-r--r--libc/bionic/malloc_debug_qemu.c975
5 files changed, 1068 insertions, 54 deletions
diff --git a/.gitignore b/.gitignore
index 0db5b03..5a5ea87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.pyc
+*.*~
libc/kernel/original
diff --git a/libc/bionic/malloc_debug_common.c b/libc/bionic/malloc_debug_common.c
index 4210915..a86255a 100644
--- a/libc/bionic/malloc_debug_common.c
+++ b/libc/bionic/malloc_debug_common.c
@@ -27,8 +27,8 @@
*/
/*
- * Contains definition of global variables and implementation of routines
- * that are used by malloc leak detection code and other components in
+ * Contains definition of structures, global variables, and implementation of
+ * routines that are used by malloc leak detection code and other components in
* the system. The trick is that some components expect these data and
* routines to be defined / implemented in libc.so library, regardless
* whether or not MALLOC_LEAK_CHECK macro is defined. To make things even
@@ -237,11 +237,11 @@ void* memalign(size_t alignment, size_t bytes) {
// =============================================================================
#define debug_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ )
+ __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ )
#define error_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
+ __libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
#define info_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
+ __libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
/* Table for dispatching malloc calls, depending on environment. */
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
@@ -259,43 +259,64 @@ extern char* __progname;
* CHK_SENTINEL_VALUE, and CHK_FILL_FREE macros.
* 10 - For adding pre-, and post- allocation stubs in order to detect
* buffer overruns.
- * 20 - For enabling emulator memory allocation instrumentation detecting
- * memory leaks and buffer overruns.
+ * Note that emulator's memory allocation instrumentation is not controlled by
+ * libc.debug.malloc value, but rather by emulator, started with -memcheck
+ * option. Note also, that if emulator has started with -memcheck option,
+ * emulator's instrumented memory allocation will take over value saved in
+ * libc.debug.malloc. In other words, if emulator has started with -memcheck
+ * option, libc.debug.malloc value is ignored.
* Actual functionality for debug levels 1-10 is implemented in
- * libc_malloc_debug_leak.so, while functionality for debug level 20 is
- * implemented in libc_malloc_debug_qemu.so and can be run inside the
- * emulator only.
+ * libc_malloc_debug_leak.so, while functionality for emultor's instrumented
+ * allocations is implemented in libc_malloc_debug_qemu.so and can be run inside
+ * the emulator only.
*/
static void* libc_malloc_impl_handle = NULL;
+/* Make sure we have MALLOC_ALIGNMENT that matches the one that is
+ * used in dlmalloc. Emulator's memchecker needs this value to properly
+ * align its guarding zones.
+ */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)8U)
+#endif /* MALLOC_ALIGNMENT */
+
/* Initializes memory allocation framework once per process. */
static void malloc_init_impl(void)
{
const char* so_name = NULL;
- unsigned int debug_level = 0;
+ MallocDebugInit malloc_debug_initialize = NULL;
unsigned int qemu_running = 0;
- int len;
+ unsigned int debug_level = 0;
+ unsigned int memcheck_enabled = 0;
char env[PROP_VALUE_MAX];
+ char memcheck_tracing[PROP_VALUE_MAX];
+
+ /* Get custom malloc debug level. Note that emulator started with
+ * memory checking option will have priority over debug level set in
+ * libc.debug.malloc system property. */
+ if (__system_property_get("ro.kernel.qemu", env) && atoi(env)) {
+ qemu_running = 1;
+ if (__system_property_get("ro.kernel.memcheck", memcheck_tracing)) {
+ if (memcheck_tracing[0] != '0') {
+ // Emulator has started with memory tracing enabled. Enforce it.
+ debug_level = 20;
+ memcheck_enabled = 1;
+ }
+ }
+ }
- // Get custom malloc debug level.
- len = __system_property_get("libc.debug.malloc", env);
- if (len) {
+ /* If debug level has not been set by memcheck option in the emulator,
+ * lets grab it from libc.debug.malloc system property. */
+ if (!debug_level && __system_property_get("libc.debug.malloc", env)) {
debug_level = atoi(env);
}
/* Debug level 0 means that we should use dlxxx allocation
- * routines (default).
- */
+ * routines (default). */
if (!debug_level) {
return;
}
- // Get emulator running status.
- len = __system_property_get("ro.kernel.qemu", env);
- if (len) {
- qemu_running = atoi(env);
- }
-
// Lets see which .so must be loaded for the requested debug level
switch (debug_level) {
case 1:
@@ -304,17 +325,23 @@ static void malloc_init_impl(void)
so_name = "/system/lib/libc_malloc_debug_leak.so";
break;
case 20:
- // Quick check: debug level 20 can only be handled in emulator
+ // Quick check: debug level 20 can only be handled in emulator.
if (!qemu_running) {
- info_log("%s: Debug level %d can only be set in emulator\n",
- __progname, debug_level);
+ error_log("%s: Debug level %d can only be set in emulator\n",
+ __progname, debug_level);
+ return;
+ }
+ // Make sure that memory checking has been enabled in emulator.
+ if (!memcheck_enabled) {
+ error_log("%s: Memory checking is not enabled in the emulator\n",
+ __progname);
return;
}
so_name = "/system/lib/libc_malloc_debug_qemu.so";
break;
default:
- info_log("%s: Debug level %d is unknown\n",
- __progname, debug_level);
+ error_log("%s: Debug level %d is unknown\n",
+ __progname, debug_level);
return;
}
@@ -326,6 +353,36 @@ static void malloc_init_impl(void)
return;
}
+ // Initialize malloc debugging in the loaded module.
+ malloc_debug_initialize =
+ dlsym(libc_malloc_impl_handle, "malloc_debug_initialize");
+ if (malloc_debug_initialize == NULL) {
+ error_log("%s: Initialization routine is not found in %s\n",
+ __progname, so_name);
+ dlclose(libc_malloc_impl_handle);
+ return;
+ }
+ if (malloc_debug_initialize()) {
+ dlclose(libc_malloc_impl_handle);
+ return;
+ }
+
+ if (debug_level == 20) {
+ // For memory checker we need to do extra initialization.
+ int (*memcheck_initialize)(int, const char*) =
+ dlsym(libc_malloc_impl_handle, "memcheck_initialize");
+ if (memcheck_initialize == NULL) {
+ error_log("%s: memcheck_initialize routine is not found in %s\n",
+ __progname, so_name);
+ dlclose(libc_malloc_impl_handle);
+ return;
+ }
+ if (memcheck_initialize(MALLOC_ALIGNMENT, memcheck_tracing)) {
+ dlclose(libc_malloc_impl_handle);
+ return;
+ }
+ }
+
// Initialize malloc dispatch table with appropriate routines.
switch (debug_level) {
case 1:
@@ -374,8 +431,8 @@ static void malloc_init_impl(void)
break;
case 20:
__libc_android_log_print(ANDROID_LOG_INFO, "libc",
- "%s using MALLOC_DEBUG = %d (instrumented for emulator)\n",
- __progname, debug_level);
+ "%s[%u] using MALLOC_DEBUG = %d (instrumented for emulator)\n",
+ __progname, getpid(), debug_level);
gMallocUse.malloc =
dlsym(libc_malloc_impl_handle, "qemu_instrumented_malloc");
gMallocUse.free =
@@ -421,9 +478,8 @@ static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
*/
void malloc_debug_init(void)
{
- /* We need to initialize malloc iff we impelement here custom
- * malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so
- */
+ /* We need to initialize malloc iff we implement here custom
+ * malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so */
#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
error_log("Unable to initialize malloc_debug component.");
diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h
index a6301b3..87600d6 100644
--- a/libc/bionic/malloc_debug_common.h
+++ b/libc/bionic/malloc_debug_common.h
@@ -70,13 +70,28 @@ struct HashTable {
/* Entry in malloc dispatch table. */
typedef struct MallocDebug MallocDebug;
struct MallocDebug {
+ /* Address of the actual malloc routine. */
void* (*malloc)(size_t bytes);
+ /* Address of the actual free routine. */
void (*free)(void* mem);
+ /* Address of the actual calloc routine. */
void* (*calloc)(size_t n_elements, size_t elem_size);
+ /* Address of the actual realloc routine. */
void* (*realloc)(void* oldMem, size_t bytes);
+ /* Address of the actual memalign routine. */
void* (*memalign)(size_t alignment, size_t bytes);
};
+/* Malloc debugging initialization routine.
+ * This routine must be implemented in .so modules that implement malloc
+ * debugging. This routine is called once per process from malloc_init_impl
+ * routine implemented in bionic/libc/bionic/malloc_debug_common.c when malloc
+ * debugging gets initialized for the process.
+ * Return:
+ * 0 on success, -1 on failure.
+ */
+typedef int (*MallocDebugInit)(void);
+
#ifdef __cplusplus
}; /* end of extern "C" */
#endif
diff --git a/libc/bionic/malloc_debug_leak.c b/libc/bionic/malloc_debug_leak.c
index 7b8822c..e11606d 100644
--- a/libc/bionic/malloc_debug_leak.c
+++ b/libc/bionic/malloc_debug_leak.c
@@ -629,3 +629,12 @@ void* leak_memalign(size_t alignment, size_t bytes)
}
return base;
}
+
+/* Initializes malloc debugging framework.
+ * See comments on MallocDebugInit in malloc_debug_common.h
+ */
+int malloc_debug_initialize(void)
+{
+ // We don't really have anything that requires initialization here.
+ return 0;
+}
diff --git a/libc/bionic/malloc_debug_qemu.c b/libc/bionic/malloc_debug_qemu.c
index b63ddb4..4b694e9 100644
--- a/libc/bionic/malloc_debug_qemu.c
+++ b/libc/bionic/malloc_debug_qemu.c
@@ -27,55 +27,988 @@
*/
/*
- * Contains implementation of memeory allocation routines instrumented for
+ * 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 <pthread.h>
#include <unistd.h>
+#include <errno.h>
#include "dlmalloc.h"
#include "logd.h"
+#include "malloc_debug_common.h"
-// This file should be included into the build only when
-// MALLOC_QEMU_INSTRUMENT macro is defined.
+/* 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
+#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.
+ */
+typedef 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;
+} MallocDesc;
+
+/* 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.
+ */
+typedef 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.
+ */
+ 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;
+} MallocDescQuery;
+
+/* 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.
+ */
+typedef 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;
+} MallocFree;
// =============================================================================
-// log functions
+// Communication events
// =============================================================================
-#define debug_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_qemu", (format), ##__VA_ARGS__ )
-#define error_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_qemu", (format), ##__VA_ARGS__ )
-#define info_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_INFO, "malloc_qemu", (format), ##__VA_ARGS__ )
+/* 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 mesages via
+ * __libc_android_log_print API 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.
+// =============================================================================
-void* qemu_instrumented_malloc(size_t bytes)
+/*
+ * 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 debug_log(format, ...) \
+ do { \
+ __libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \
+ (format), ##__VA_ARGS__ ); \
+ if (tracing_flags & DEBUG_TRACING_ENABLED) { \
+ qemu_log(ANDROID_LOG_DEBUG, (format), ##__VA_ARGS__ ); \
+ } \
+ } while (0)
+
+#define error_log(format, ...) \
+ do { \
+ __libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \
+ (format), ##__VA_ARGS__ ); \
+ if (tracing_flags & ERROR_TRACING_ENABLED) { \
+ qemu_log(ANDROID_LOG_ERROR, (format), ##__VA_ARGS__ ); \
+ } \
+ } while (0)
+
+#define info_log(format, ...) \
+ do { \
+ __libc_android_log_print(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.
+ * frmt + rest - Formats message preceding dumped descriptor.
+*/
+#define log_mdesc(type, desc, frmt, ...) \
+ do { \
+ if (tracing_enabled(type)) { \
+ char log_str[4096]; \
+ size_t str_len; \
+ snprintf(log_str, sizeof(log_str), frmt, ##__VA_ARGS__); \
+ log_str[sizeof(log_str) - 1] = '\0'; \
+ str_len = strlen(log_str); \
+ dump_malloc_descriptor(log_str + str_len, \
+ sizeof(log_str) - str_len, \
+ (desc)); \
+ type##_log(log_str); \
+ } \
+ } while (0)
+
+// =============================================================================
+// Static data
+// =============================================================================
+
+/* 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 dlmalloc.
+ * 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 (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 dlmalloc(bytes);
+ return desc->prefix_size + desc->requested_bytes + desc->suffix_size;
}
-void qemu_instrumented_free(void* mem)
+/* 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)
{
- dlfree(mem);
+ return (char*)desc->ptr + mallocdesc_alloc_size(desc);
}
-void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
+/* 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, uint32_t val)
{
- return dlcalloc(n_elements, elem_size);
+ if (NULL != qtrace) {
+ *(volatile uint32_t*)((uint32_t)qtrace + ((code - 1024) << 2)) = val;
+ }
}
-void* qemu_instrumented_realloc(void* mem, size_t bytes)
+/* 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)
{
- return dlrealloc(mem, bytes);
+ if (str != NULL) {
+ notify_qemu(TRACE_DEV_REG_PRINT_USER_STR, (uint32_t)str);
+ }
}
-void* qemu_instrumented_memalign(size_t alignment, size_t bytes)
+/* 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)
{
- return dlmemalign(alignment, bytes);
+ desc->libc_pid = malloc_pid;
+ desc->allocator_pid = getpid();
+ desc->av_count = 0;
+ notify_qemu(TRACE_DEV_REG_MALLOC, (uint32_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, (uint32_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(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, (uint32_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: %X <-> %X [%u + %u + %u] by pid=%03u in libc_pid=%03u",
+ mallocdesc_user_ptr(desc), (uint32_t)desc->ptr,
+ (uint32_t)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
+// =============================================================================
+
+void* qemu_instrumented_malloc(size_t bytes);
+void qemu_instrumented_free(void* mem);
+void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size);
+void* qemu_instrumented_realloc(void* mem, size_t bytes);
+void* qemu_instrumented_memalign(size_t alignment, size_t bytes);
+
+/* 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.
+*/
+int
+malloc_debug_initialize(void)
+{
+ /* 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 -1;
+ } else {
+ qtrace = mmap(0, 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 -1;
+ }
+ }
+
+ /* Cache pid of the process this library has been initialized for. */
+ malloc_pid = getpid();
+
+ return 0;
+}
+
+/* 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.
+*/
+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);
+
+ 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.
+ */
+void*
+qemu_instrumented_malloc(size_t bytes)
+{
+ MallocDesc desc;
+
+ /* Initialize block descriptor and allocate memory. Note that dlmalloc
+ * 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;
+ desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc));
+ if (desc.ptr == NULL) {
+ error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.",
+ malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc));
+ 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());
+ dlfree(desc.ptr);
+ 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(%u) -> ",
+ 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.
+ */
+void
+qemu_instrumented_free(void* mem)
+{
+ MallocDesc desc;
+
+ if (mem == NULL) {
+ // Just let go NULL free
+ dlfree(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);
+ dlfree(desc.ptr);
+ }
+}
+
+/* This routine serves as entry point for 'calloc'.
+ * This routine behaves similarly to qemu_instrumented_malloc.
+ */
+void*
+qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
+{
+ MallocDesc desc;
+ void* ret;
+ size_t total_size;
+ size_t total_elements;
+
+ if (n_elements == 0 || elem_size == 0) {
+ // Just let go zero bytes allocation.
+ 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 && MAX_SIZE_T / n_elements < elem_size) {
+ return NULL;
+ }
+
+ /* 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;
+ total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_size;
+ 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 = dlcalloc(total_elements, elem_size);
+ if (desc.ptr == NULL) {
+ error_log("<libc_pid=%03u, pid=%03u> calloc: dlcalloc(%u(%u), %u) (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(%u(%u), %u): notify_malloc failed for ",
+ malloc_pid, getpid(), n_elements, total_elements, elem_size);
+ dlfree(desc.ptr);
+ 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(%u(%u), %u) -> ",
+ 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.
+ */
+void*
+qemu_instrumented_realloc(void* mem, size_t bytes)
+{
+ MallocDesc new_desc;
+ MallocDesc cur_desc;
+ size_t to_copy;
+ void* ret;
+
+ if (mem == NULL) {
+ // Nothing to realloc. just do regular malloc.
+ info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc",
+ malloc_pid, getpid(), mem, bytes);
+ return qemu_instrumented_malloc(bytes);
+ }
+
+ if (bytes == 0) {
+ // This is a "free" condition.
+ info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc",
+ malloc_pid, getpid(), mem, bytes);
+ qemu_instrumented_free(mem);
+
+ // This is what dlrealloc does for a "free" realloc.
+ return NULL;
+ }
+
+ // 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>: realloc(%p, %u) query_info failed.",
+ malloc_pid, getpid(), mem, bytes);
+ 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, %u) is invalid for ",
+ malloc_pid, getpid(), mem, bytes);
+ 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.
+ new_desc.prefix_size = DEFAULT_PREFIX_SIZE;
+ new_desc.requested_bytes = bytes;
+ new_desc.suffix_size = DEFAULT_SUFFIX_SIZE;
+ new_desc.ptr = dlmalloc(mallocdesc_alloc_size(&new_desc));
+ if (new_desc.ptr == NULL) {
+ log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u): dlmalloc(%u) failed on ",
+ malloc_pid, getpid(), mem, bytes,
+ mallocdesc_alloc_size(&new_desc));
+ return NULL;
+ }
+ ret = mallocdesc_user_ptr(&new_desc);
+
+ // Copy user data from old block to the new one.
+ to_copy = bytes < cur_desc.requested_bytes ? bytes :
+ cur_desc.requested_bytes;
+ if (to_copy != 0) {
+ memcpy(ret, 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, %u) notify_malloc failed -> ",
+ malloc_pid, getpid(), mem, bytes);
+ log_mdesc(error, &cur_desc, " <- ");
+ dlfree(new_desc.ptr);
+ 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, %u): 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));
+ dlfree(new_desc.ptr);
+ return NULL;
+ }
+ dlfree(cur_desc.ptr);
+
+ log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %u) -> ",
+ malloc_pid, getpid(), mem, bytes);
+ log_mdesc(info, &cur_desc, " <- ");
+
+ return ret;
+}
+
+/* This routine serves as entry point for 'memalign'.
+ * This routine behaves similarly to qemu_instrumented_malloc.
+ */
+void*
+qemu_instrumented_memalign(size_t alignment, size_t bytes)
+{
+ MallocDesc desc;
+
+ if (bytes == 0) {
+ // Just let go zero bytes allocation.
+ info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) 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. */
+ desc.prefix_size = alignment > DEFAULT_PREFIX_SIZE ? alignment :
+ DEFAULT_PREFIX_SIZE;
+ desc.requested_bytes = bytes;
+ desc.suffix_size = DEFAULT_SUFFIX_SIZE;
+ desc.ptr = dlmemalign(desc.prefix_size, mallocdesc_alloc_size(&desc));
+ if (desc.ptr == NULL) {
+ error_log("<libc_pid=%03u, pid=%03u> memalign(%X, %u): dlmalloc(%u) failed.",
+ malloc_pid, getpid(), alignment, bytes,
+ mallocdesc_alloc_size(&desc));
+ return NULL;
+ }
+ if (notify_qemu_malloc(&desc)) {
+ log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%X, %u): notify_malloc failed for ",
+ malloc_pid, getpid(), alignment, bytes);
+ dlfree(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(%X, %u) -> ",
+ malloc_pid, getpid(), alignment, bytes);
+ return mallocdesc_user_ptr(&desc);
}