From 0caf70e98e8cf109d0116c774147c568b369fd27 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 23 Jul 2014 11:38:38 -0700 Subject: [PATCH 1/6] Fix belated review comments on syslog change. Bug: 14292866 (cherry picked from commit afe6360627ef3f0e9bc8f45535fbfae3354f3ae0) Change-Id: I8e3cc6b37b2539e51a27261ffb5d6e58266ce11d --- libc/bionic/libc_logging.cpp | 3 +- libc/bionic/syslog.cpp | 6 +-- libc/include/syslog.h | 77 +++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp index 88d979091..cdbf7236c 100644 --- a/libc/bionic/libc_logging.cpp +++ b/libc/bionic/libc_logging.cpp @@ -230,6 +230,7 @@ static void SendRepeat(Out& o, char ch, int count) { /* Perform formatted output to an output target 'o' */ template static void out_vformat(Out& o, const char* format, va_list args) { + int caller_errno = errno; int nn = 0; for (;;) { @@ -380,7 +381,7 @@ static void out_vformat(Out& o, const char* format, va_list args) { buffer[1] = '\0'; } else if (c == 'm') { // syslog-like %m for strerror(errno). - str = strerror(errno); + str = strerror(caller_errno); } else { __assert(__FILE__, __LINE__, "conversion specifier unsupported"); } diff --git a/libc/bionic/syslog.cpp b/libc/bionic/syslog.cpp index 7e153eb16..29f892a20 100644 --- a/libc/bionic/syslog.cpp +++ b/libc/bionic/syslog.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ -#include - #include +#include #include "private/libc_logging.h" @@ -24,6 +23,7 @@ static const char* syslog_log_tag = NULL; static int syslog_priority_mask = 0xff; void closelog() { + syslog_log_tag = NULL; } void openlog(const char* log_tag, int /*options*/, int /*facility*/) { @@ -61,7 +61,7 @@ void vsyslog(int priority, const char* fmt, va_list args) { // What's our Android log priority? priority &= LOG_PRIMASK; int android_log_priority; - if (priority < LOG_ERR) { + if (priority <= LOG_ERR) { android_log_priority = ANDROID_LOG_ERROR; } else if (priority == LOG_WARNING) { android_log_priority = ANDROID_LOG_WARN; diff --git a/libc/include/syslog.h b/libc/include/syslog.h index f396feca7..cbceab287 100644 --- a/libc/include/syslog.h +++ b/libc/include/syslog.h @@ -35,45 +35,48 @@ __BEGIN_DECLS -#define LOG_EMERG 0 -#define LOG_ALERT 1 -#define LOG_CRIT 2 -#define LOG_ERR 3 -#define LOG_WARNING 4 -#define LOG_NOTICE 5 -#define LOG_INFO 6 -#define LOG_DEBUG 7 +/* Priorities are translated to Android log priorities as shown. */ +#define LOG_EMERG 0 /* ERROR */ +#define LOG_ALERT 1 /* ERROR */ +#define LOG_CRIT 2 /* ERROR */ +#define LOG_ERR 3 /* ERROR */ +#define LOG_WARNING 4 /* WARN */ +#define LOG_NOTICE 5 /* INFO */ +#define LOG_INFO 6 /* INFO */ +#define LOG_DEBUG 7 /* DEBUG */ -#define LOG_PRIMASK 7 -#define LOG_PRI(x) ((x) & LOG_PRIMASK) +#define LOG_PRIMASK 7 +#define LOG_PRI(x) ((x) & LOG_PRIMASK) -#define LOG_KERN 0000 -#define LOG_USER 0010 -#define LOG_MAIL 0020 -#define LOG_DAEMON 0030 -#define LOG_AUTH 0040 -#define LOG_SYSLOG 0050 -#define LOG_LPR 0060 -#define LOG_NEWS 0070 -#define LOG_UUCP 0100 -#define LOG_CRON 0110 -#define LOG_AUTHPRIV 0120 -#define LOG_FTP 0130 -#define LOG_LOCAL0 0200 -#define LOG_LOCAL1 0210 -#define LOG_LOCAL2 0220 -#define LOG_LOCAL3 0230 -#define LOG_LOCAL4 0240 -#define LOG_LOCAL5 0250 -#define LOG_LOCAL6 0260 -#define LOG_LOCAL7 0270 +/* Facilities are currently ignored on Android. */ +#define LOG_KERN 0000 +#define LOG_USER 0010 +#define LOG_MAIL 0020 +#define LOG_DAEMON 0030 +#define LOG_AUTH 0040 +#define LOG_SYSLOG 0050 +#define LOG_LPR 0060 +#define LOG_NEWS 0070 +#define LOG_UUCP 0100 +#define LOG_CRON 0110 +#define LOG_AUTHPRIV 0120 +#define LOG_FTP 0130 +#define LOG_LOCAL0 0200 +#define LOG_LOCAL1 0210 +#define LOG_LOCAL2 0220 +#define LOG_LOCAL3 0230 +#define LOG_LOCAL4 0240 +#define LOG_LOCAL5 0250 +#define LOG_LOCAL6 0260 +#define LOG_LOCAL7 0270 -#define LOG_FACMASK 01770 -#define LOG_FAC(x) (((x) >> 3) & (LOG_FACMASK >> 3)) +#define LOG_FACMASK 01770 +#define LOG_FAC(x) (((x) >> 3) & (LOG_FACMASK >> 3)) #define LOG_MASK(pri) (1 << (pri)) #define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) +/* openlog(3) flags are currently ignored on Android. */ #define LOG_PID 0x01 #define LOG_CONS 0x02 #define LOG_ODELAY 0x04 @@ -81,11 +84,11 @@ __BEGIN_DECLS #define LOG_NOWAIT 0x10 #define LOG_PERROR 0x20 -extern void closelog(void); -extern void openlog(const char*, int, int); -extern int setlogmask(int); -extern void syslog(int, const char*, ...) __printflike(2, 3); -extern void vsyslog(int, const char*, va_list) __printflike(2, 0); +void closelog(void); +void openlog(const char*, int, int); +int setlogmask(int); +void syslog(int, const char*, ...) __printflike(2, 3); +void vsyslog(int, const char*, va_list) __printflike(2, 0); __END_DECLS From 4514aa630c8cace57a71268eabff687fcdf13ca0 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 23 Jul 2014 14:43:44 -0700 Subject: [PATCH 2/6] HACK: remove %m support from printf. The change that added this support causes a cpu hard lock on one device. This code clearly isn't at fault, but disabling it to unblock until we can find a real fix. Bug: 16484311 Change-Id: I33834dc49d959ae403b10d2c7cad12ae2950f772 --- libc/bionic/libc_logging.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp index cdbf7236c..1fb8a84ab 100644 --- a/libc/bionic/libc_logging.cpp +++ b/libc/bionic/libc_logging.cpp @@ -230,7 +230,9 @@ static void SendRepeat(Out& o, char ch, int count) { /* Perform formatted output to an output target 'o' */ template static void out_vformat(Out& o, const char* format, va_list args) { +#if 0 int caller_errno = errno; +#endif int nn = 0; for (;;) { @@ -379,9 +381,11 @@ static void out_vformat(Out& o, const char* format, va_list args) { } else if (c == '%') { buffer[0] = '%'; buffer[1] = '\0'; +#if 0 } else if (c == 'm') { // syslog-like %m for strerror(errno). str = strerror(caller_errno); +#endif } else { __assert(__FILE__, __LINE__, "conversion specifier unsupported"); } From 11837629693e520452672e0eae28d3ce71f80ed6 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 24 Jul 2014 17:52:23 -0700 Subject: [PATCH 3/6] Use libunwindbacktrace for debug malloc code. Create a method of disabling the debug allocation code paths so that it's possible to use the libunwindbacktrace library without any modifications. Use this path to create and destroy the maps for the process. It's not stricly necessary in the init code since the symbols are not modified until after the initialize calls. Also, remove the debug_XXX source files that doesn't need to be in libc.so. Fix the maps reading code since it was completely broken for 64 bit. Bug: 16408686 (cherry picked from commit 861c0ef37bcfcae56d88572cb01c18bcfe1faded) Change-Id: I04445f0cf9a1e85172b64d57df92eb7939ce2332 --- libc/Android.mk | 8 ++-- libc/bionic/debug_mapinfo.cpp | 53 +++++++++++++++---------- libc/bionic/debug_mapinfo.h | 4 +- libc/bionic/debug_stacktrace.cpp | 13 +++++- libc/bionic/malloc_debug_check.cpp | 43 ++++++++++++++++++++ libc/bionic/malloc_debug_disable.h | 64 ++++++++++++++++++++++++++++++ libc/bionic/malloc_debug_leak.cpp | 41 +++++++++++++++++++ 7 files changed, 198 insertions(+), 28 deletions(-) create mode 100644 libc/bionic/malloc_debug_disable.h diff --git a/libc/Android.mk b/libc/Android.mk index 1fb5e84ae..8326dbbc8 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -982,8 +982,6 @@ LOCAL_SRC_FILES := \ $(libc_arch_dynamic_src_files) \ $(libc_static_common_src_files) \ bionic/malloc_debug_common.cpp \ - bionic/debug_mapinfo.cpp \ - bionic/debug_stacktrace.cpp \ bionic/libc_init_dynamic.cpp \ bionic/NetdClient.cpp \ @@ -1049,7 +1047,10 @@ LOCAL_CFLAGS := \ LOCAL_CONLYFLAGS := $(libc_common_conlyflags) LOCAL_CPPFLAGS := $(libc_common_cppflags) -LOCAL_C_INCLUDES := $(libc_common_c_includes) +# Make sure that unwind.h comes from libunwind. +LOCAL_C_INCLUDES := \ + external/libunwind/include \ + $(libc_common_c_includes) \ LOCAL_SRC_FILES := \ bionic/debug_mapinfo.cpp \ @@ -1064,6 +1065,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) LOCAL_SHARED_LIBRARIES := libc libdl LOCAL_SYSTEM_SHARED_LIBRARIES := +LOCAL_WHOLE_STATIC_LIBRARIES := libunwindbacktrace LOCAL_ALLOW_UNDEFINED_SYMBOLS := true # Don't install on release build diff --git a/libc/bionic/debug_mapinfo.cpp b/libc/bionic/debug_mapinfo.cpp index 17276ce40..d83799afa 100644 --- a/libc/bionic/debug_mapinfo.cpp +++ b/libc/bionic/debug_mapinfo.cpp @@ -26,39 +26,48 @@ * SUCH DAMAGE. */ +#include +#include #include #include #include -#include #include "debug_mapinfo.h" +#include "malloc_debug_disable.h" -// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so -// 012345678901234567890123456789012345678901234567890123456789 -// 0 1 2 3 4 5 - +// Format of /proc//maps: +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so static mapinfo_t* parse_maps_line(char* line) { - int len = strlen(line); + uintptr_t start; + uintptr_t end; + int name_pos; + if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %*4s %*x %*x:%*x %*d%n", &start, + &end, &name_pos) < 2) { + return NULL; + } - if (len < 1) return 0; - line[--len] = 0; - - if (len < 50) return 0; - if (line[20] != 'x') return 0; - - mapinfo_t* mi = static_cast( - mmap(NULL, sizeof(mapinfo_t) + (len - 47), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)); - if (mi == MAP_FAILED) return 0; - - mi->start = strtoul(line, 0, 16); - mi->end = strtoul(line + 9, 0, 16); - mi->next = 0; - strcpy(mi->name, line + 49); + while (isspace(line[name_pos])) { + name_pos += 1; + } + const char* name = line + name_pos; + size_t name_len = strlen(name); + if (name_len && name[name_len - 1] == '\n') { + name_len -= 1; + } + mapinfo_t* mi = reinterpret_cast(calloc(1, sizeof(mapinfo_t) + name_len + 1)); + if (mi) { + mi->start = start; + mi->end = end; + memcpy(mi->name, name, name_len); + mi->name[name_len] = '\0'; + } return mi; } __LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) { + ScopedDisableDebugCalls disable; + struct mapinfo_t* milist = NULL; char data[1024]; // Used to read lines as well as to construct the filename. snprintf(data, sizeof(data), "/proc/%d/maps", pid); @@ -77,10 +86,12 @@ __LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) { } __LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) { + ScopedDisableDebugCalls disable; + while (mi != NULL) { mapinfo_t* del = mi; mi = mi->next; - munmap(del, sizeof(mapinfo_t) + strlen(del->name) + 2); + free(del); } } diff --git a/libc/bionic/debug_mapinfo.h b/libc/bionic/debug_mapinfo.h index cccd2e374..926b37762 100644 --- a/libc/bionic/debug_mapinfo.h +++ b/libc/bionic/debug_mapinfo.h @@ -33,8 +33,8 @@ struct mapinfo_t { struct mapinfo_t* next; - unsigned start; - unsigned end; + uintptr_t start; + uintptr_t end; char name[]; }; diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp index 713e76113..b86e2afdc 100644 --- a/libc/bionic/debug_stacktrace.cpp +++ b/libc/bionic/debug_stacktrace.cpp @@ -35,6 +35,7 @@ #include #include "debug_mapinfo.h" +#include "malloc_debug_disable.h" #include "private/libc_logging.h" #if defined(__LP64__) @@ -56,6 +57,8 @@ typedef char* (*DemanglerFn)(const char*, char*, size_t*, int*); static DemanglerFn g_demangler_fn = NULL; __LIBC_HIDDEN__ void backtrace_startup() { + ScopedDisableDebugCalls disable; + g_map_info = mapinfo_create(getpid()); g_demangler = dlopen("libgccdemangle.so", RTLD_NOW); if (g_demangler != NULL) { @@ -65,6 +68,8 @@ __LIBC_HIDDEN__ void backtrace_startup() { } __LIBC_HIDDEN__ void backtrace_shutdown() { + ScopedDisableDebugCalls disable; + mapinfo_destroy(g_map_info); dlclose(g_demangler); } @@ -98,7 +103,7 @@ static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg) return _URC_NO_REASON; } -#ifdef __arm__ +#if defined(__arm__) /* * The instruction pointer is pointing at the instruction after the bl(x), and * the _Unwind_Backtrace routine already masks the Thumb mode indicator (LSB @@ -121,12 +126,16 @@ static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg) } __LIBC_HIDDEN__ int get_backtrace(uintptr_t* frames, size_t max_depth) { + ScopedDisableDebugCalls disable; + stack_crawl_state_t state(frames, max_depth); _Unwind_Backtrace(trace_function, &state); return state.frame_count; } __LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) { + ScopedDisableDebugCalls disable; + uintptr_t self_bt[16]; if (frames == NULL) { frame_count = get_backtrace(self_bt, 16); @@ -146,7 +155,7 @@ __LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) { symbol = info.dli_sname; } - uintptr_t rel_pc; + uintptr_t rel_pc = offset; const mapinfo_t* mi = (g_map_info != NULL) ? mapinfo_find(g_map_info, frames[i], &rel_pc) : NULL; const char* soname = (mi != NULL) ? mi->name : info.dli_fname; if (soname == NULL) { diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp index 1c63d4dcf..94ba6f5ec 100644 --- a/libc/bionic/malloc_debug_check.cpp +++ b/libc/bionic/malloc_debug_check.cpp @@ -49,6 +49,7 @@ #include "debug_mapinfo.h" #include "debug_stacktrace.h" #include "malloc_debug_common.h" +#include "malloc_debug_disable.h" #include "private/bionic_macros.h" #include "private/libc_logging.h" #include "private/ScopedPthreadMutexLocker.h" @@ -331,6 +332,9 @@ static inline void add_to_backlog(hdr_t* hdr) { extern "C" void* chk_malloc(size_t bytes) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); + if (DebugCallsDisabled()) { + return g_malloc_dispatch->malloc(bytes); + } size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t); if (size < bytes) { // Overflow @@ -348,6 +352,10 @@ extern "C" void* chk_malloc(size_t bytes) { } extern "C" void* chk_memalign(size_t alignment, size_t bytes) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->memalign(alignment, bytes); + } + if (alignment <= MALLOC_ALIGNMENT) { return chk_malloc(bytes); } @@ -386,6 +394,9 @@ extern "C" void* chk_memalign(size_t alignment, size_t bytes) { extern "C" void chk_free(void* ptr) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); + if (DebugCallsDisabled()) { + return g_malloc_dispatch->free(ptr); + } if (!ptr) /* ignore free(NULL) */ return; @@ -421,6 +432,9 @@ extern "C" void chk_free(void* ptr) { extern "C" void* chk_realloc(void* ptr, size_t bytes) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); + if (DebugCallsDisabled()) { + return g_malloc_dispatch->realloc(ptr, bytes); + } if (!ptr) { return chk_malloc(bytes); @@ -496,6 +510,10 @@ extern "C" void* chk_realloc(void* ptr, size_t bytes) { extern "C" void* chk_calloc(size_t nmemb, size_t bytes) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); + if (DebugCallsDisabled()) { + return g_malloc_dispatch->calloc(nmemb, bytes); + } + size_t total_bytes = nmemb * bytes; size_t size = sizeof(hdr_t) + total_bytes + sizeof(ftr_t); if (size < total_bytes || (nmemb && SIZE_MAX / nmemb < bytes)) { // Overflow @@ -513,6 +531,10 @@ extern "C" void* chk_calloc(size_t nmemb, size_t bytes) { } extern "C" size_t chk_malloc_usable_size(const void* ptr) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->malloc_usable_size(ptr); + } + // malloc_usable_size returns 0 for NULL and unknown blocks. if (ptr == NULL) return 0; @@ -529,6 +551,10 @@ extern "C" struct mallinfo chk_mallinfo() { } extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->posix_memalign(memptr, alignment, size); + } + if (!powerof2(alignment)) { return EINVAL; } @@ -538,7 +564,12 @@ extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size) return (*memptr != NULL) ? 0 : ENOMEM; } +#if defined(HAVE_DEPRECATED_MALLOC_FUNCS) extern "C" void* chk_pvalloc(size_t bytes) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->pvalloc(bytes); + } + size_t pagesize = getpagesize(); size_t size = BIONIC_ALIGN(bytes, pagesize); if (size < bytes) { // Overflow @@ -548,10 +579,16 @@ extern "C" void* chk_pvalloc(size_t bytes) { } extern "C" void* chk_valloc(size_t size) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->valloc(size); + } return chk_memalign(getpagesize(), size); } +#endif static void ReportMemoryLeaks() { + ScopedDisableDebugCalls disable; + // Use /proc/self/exe link to obtain the program name for logging // purposes. If it's not available, we set it to "". char exe[PATH_MAX]; @@ -585,10 +622,14 @@ static void ReportMemoryLeaks() { } } +pthread_key_t g_debug_calls_disabled; + extern "C" bool malloc_debug_initialize(HashTable* hash_table, const MallocDebug* malloc_dispatch) { g_hash_table = hash_table; g_malloc_dispatch = malloc_dispatch; + pthread_key_create(&g_debug_calls_disabled, NULL); + char debug_backlog[PROP_VALUE_MAX]; if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) { g_malloc_debug_backlog = atoi(debug_backlog); @@ -605,4 +646,6 @@ extern "C" void malloc_debug_finalize(int malloc_debug_level) { ReportMemoryLeaks(); } backtrace_shutdown(); + + pthread_setspecific(g_debug_calls_disabled, NULL); } diff --git a/libc/bionic/malloc_debug_disable.h b/libc/bionic/malloc_debug_disable.h new file mode 100644 index 000000000..9503128c4 --- /dev/null +++ b/libc/bionic/malloc_debug_disable.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef MALLOC_DEBUG_DISABLE_H +#define MALLOC_DEBUG_DISABLE_H + +#include + +#include "private/bionic_macros.h" + +// ============================================================================= +// Used to disable the debug allocation calls. +// ============================================================================= +extern pthread_key_t g_debug_calls_disabled; + +static inline bool DebugCallsDisabled() { + return pthread_getspecific(g_debug_calls_disabled) != NULL; +} + +class ScopedDisableDebugCalls { + public: + ScopedDisableDebugCalls() : disabled_(DebugCallsDisabled()) { + if (!disabled_) { + pthread_setspecific(g_debug_calls_disabled, reinterpret_cast(1)); + } + } + ~ScopedDisableDebugCalls() { + if (!disabled_) { + pthread_setspecific(g_debug_calls_disabled, NULL); + } + } + + private: + bool disabled_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDisableDebugCalls); +}; + +#endif // MALLOC_DEBUG_DISABLE_H diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp index d9824f09f..7926a1f27 100644 --- a/libc/bionic/malloc_debug_leak.cpp +++ b/libc/bionic/malloc_debug_leak.cpp @@ -48,6 +48,7 @@ #include "debug_stacktrace.h" #include "malloc_debug_common.h" +#include "malloc_debug_disable.h" #include "private/bionic_macros.h" #include "private/libc_logging.h" @@ -267,6 +268,7 @@ extern "C" int fill_posix_memalign(void** memptr, size_t alignment, size_t size) return (*memptr != NULL) ? 0 : ENOMEM; } +#if defined(HAVE_DEPRECATED_MALLOC_FUNCS) extern "C" void* fill_pvalloc(size_t bytes) { size_t pagesize = getpagesize(); size_t size = BIONIC_ALIGN(bytes, pagesize); @@ -279,6 +281,7 @@ extern "C" void* fill_pvalloc(size_t bytes) { extern "C" void* fill_valloc(size_t size) { return fill_memalign(getpagesize(), size); } +#endif // ============================================================================= // malloc leak functions @@ -287,6 +290,10 @@ extern "C" void* fill_valloc(size_t size) { static uint32_t MEMALIGN_GUARD = 0xA1A41520; extern "C" void* leak_malloc(size_t bytes) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->malloc(bytes); + } + // allocate enough space infront of the allocation to store the pointer for // the alloc structure. This will making free'ing the structer really fast! @@ -319,6 +326,10 @@ extern "C" void* leak_malloc(size_t bytes) { } extern "C" void leak_free(void* mem) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->free(mem); + } + if (mem == NULL) { return; } @@ -355,6 +366,10 @@ extern "C" void leak_free(void* mem) { } extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->calloc(n_elements, elem_size); + } + // 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) { @@ -370,6 +385,10 @@ extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) { } extern "C" void* leak_realloc(void* oldMem, size_t bytes) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->realloc(oldMem, bytes); + } + if (oldMem == NULL) { return leak_malloc(bytes); } @@ -398,6 +417,10 @@ extern "C" void* leak_realloc(void* oldMem, size_t bytes) { } extern "C" void* leak_memalign(size_t alignment, size_t bytes) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->memalign(alignment, bytes); + } + // we can just use malloc if (alignment <= MALLOC_ALIGNMENT) { return leak_malloc(bytes); @@ -439,6 +462,10 @@ extern "C" void* leak_memalign(size_t alignment, size_t bytes) { } extern "C" size_t leak_malloc_usable_size(const void* mem) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->malloc_usable_size(mem); + } + if (mem != NULL) { // Check the guard to make sure it is valid. const AllocationEntry* header = const_to_header((void*)mem); @@ -467,6 +494,10 @@ extern "C" struct mallinfo leak_mallinfo() { } extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->posix_memalign(memptr, alignment, size); + } + if (!powerof2(alignment)) { return EINVAL; } @@ -476,7 +507,12 @@ extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size) return (*memptr != NULL) ? 0 : ENOMEM; } +#if defined(HAVE_DEPRECATED_MALLOC_FUNCS) extern "C" void* leak_pvalloc(size_t bytes) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->pvalloc(bytes); + } + size_t pagesize = getpagesize(); size_t size = BIONIC_ALIGN(bytes, pagesize); if (size < bytes) { // Overflow @@ -486,5 +522,10 @@ extern "C" void* leak_pvalloc(size_t bytes) { } extern "C" void* leak_valloc(size_t size) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->valloc(size); + } + return leak_memalign(getpagesize(), size); } +#endif From b5e08542840d5722defae3e750d49a7d5ce6ccc9 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 7 Aug 2014 16:21:21 -0700 Subject: [PATCH 4/6] Add a way to disable backtracing in malloc debug. The property libc.debug.malloc.nobacktrace set to non-zero disables getting backtracing when using mode 1 or mode 10. Bug: 16874447 Change-Id: I7650ba9f4385b5110b743cab01e877fc69545b3c --- libc/bionic/malloc_debug_backtrace.h | 37 +++++++++++ libc/bionic/malloc_debug_check.cpp | 93 ++++++++++++++++++---------- libc/bionic/malloc_debug_leak.cpp | 3 +- 3 files changed, 98 insertions(+), 35 deletions(-) create mode 100644 libc/bionic/malloc_debug_backtrace.h diff --git a/libc/bionic/malloc_debug_backtrace.h b/libc/bionic/malloc_debug_backtrace.h new file mode 100644 index 000000000..774548b59 --- /dev/null +++ b/libc/bionic/malloc_debug_backtrace.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef MALLOC_DEBUG_BACKTRACE_H +#define MALLOC_DEBUG_BACKTRACE_H + +extern bool g_backtrace_enabled; + +#define GET_BACKTRACE(bt, depth) \ + (g_backtrace_enabled ? get_backtrace(bt, depth) : 0) + +#endif // MALLOC_DEBUG_BACKTRACE_H diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp index 94ba6f5ec..dee03fae1 100644 --- a/libc/bionic/malloc_debug_check.cpp +++ b/libc/bionic/malloc_debug_check.cpp @@ -48,6 +48,7 @@ #include "debug_mapinfo.h" #include "debug_stacktrace.h" +#include "malloc_debug_backtrace.h" #include "malloc_debug_common.h" #include "malloc_debug_disable.h" #include "private/bionic_macros.h" @@ -123,6 +124,10 @@ static pthread_mutex_t backlog_lock = PTHREAD_MUTEX_INITIALIZER; // It determines the size of the backlog we use to detect multiple frees. static unsigned g_malloc_debug_backlog = 100; +// This variable is set to false if the property libc.debug.malloc.nobacktrace +// is set to non-zero. +__LIBC_HIDDEN__ bool g_backtrace_enabled = true; + __LIBC_HIDDEN__ HashTable* g_hash_table; __LIBC_HIDDEN__ const MallocDebug* g_malloc_dispatch; @@ -273,7 +278,7 @@ static inline int check_allocation_locked(hdr_t* hdr, int* safe) { valid = check_guards(hdr, safe); } - if (!valid && *safe) { + if (!valid && *safe && g_backtrace_enabled) { log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->bt, hdr->bt_depth); @@ -344,7 +349,7 @@ extern "C" void* chk_malloc(size_t bytes) { hdr_t* hdr = static_cast(g_malloc_dispatch->malloc(size)); if (hdr) { hdr->base = hdr; - hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); + hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, bytes); return user(hdr); } @@ -385,7 +390,7 @@ extern "C" void* chk_memalign(size_t alignment, size_t bytes) { hdr_t* hdr = meta(reinterpret_cast(ptr)); hdr->base = base; - hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); + hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, bytes); return user(hdr); } @@ -405,27 +410,31 @@ extern "C" void chk_free(void* ptr) { if (del(hdr) < 0) { uintptr_t bt[MAX_BACKTRACE_DEPTH]; - int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); + int depth = GET_BACKTRACE(bt, MAX_BACKTRACE_DEPTH); if (hdr->tag == BACKLOG_TAG) { log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n", user(hdr), hdr->size); - log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", - user(hdr), hdr->size); - log_backtrace(hdr->bt, hdr->bt_depth); - /* hdr->freed_bt_depth should be nonzero here */ - log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", - user(hdr), hdr->size); - log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); - log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n", - user(hdr), hdr->size); - log_backtrace(bt, depth); + if (g_backtrace_enabled) { + log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", + user(hdr), hdr->size); + log_backtrace(hdr->bt, hdr->bt_depth); + /* hdr->freed_bt_depth should be nonzero here */ + log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", + user(hdr), hdr->size); + log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); + log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n", + user(hdr), hdr->size); + log_backtrace(bt, depth); + } } else { log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr)); - log_backtrace(bt, depth); + if (g_backtrace_enabled) { + log_backtrace(bt, depth); + } } } else { - hdr->freed_bt_depth = get_backtrace(hdr->freed_bt, MAX_BACKTRACE_DEPTH); + hdr->freed_bt_depth = GET_BACKTRACE(hdr->freed_bt, MAX_BACKTRACE_DEPTH); add_to_backlog(hdr); } } @@ -451,22 +460,24 @@ extern "C" void* chk_realloc(void* ptr, size_t bytes) { if (del(hdr) < 0) { uintptr_t bt[MAX_BACKTRACE_DEPTH]; - int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); + int depth = GET_BACKTRACE(bt, MAX_BACKTRACE_DEPTH); if (hdr->tag == BACKLOG_TAG) { log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n", user(hdr), bytes, hdr->size); - log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", - user(hdr), hdr->size); - log_backtrace(hdr->bt, hdr->bt_depth); - /* hdr->freed_bt_depth should be nonzero here */ - log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", - user(hdr), hdr->size); - log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); - log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n", - user(hdr), hdr->size); - log_backtrace(bt, depth); + if (g_backtrace_enabled) { + log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", + user(hdr), hdr->size); + log_backtrace(hdr->bt, hdr->bt_depth); + /* hdr->freed_bt_depth should be nonzero here */ + log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", + user(hdr), hdr->size); + log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); + log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n", + user(hdr), hdr->size); + log_backtrace(bt, depth); + } - /* We take the memory out of the backlog and fall through so the + /* We take the memory out of the backlog and fall through so the * reallocation below succeeds. Since we didn't really free it, we * can default to this behavior. */ @@ -474,7 +485,9 @@ extern "C" void* chk_realloc(void* ptr, size_t bytes) { } else { log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr), bytes); - log_backtrace(bt, depth); + if (g_backtrace_enabled) { + log_backtrace(bt, depth); + } // just get a whole new allocation and leak the old one return g_malloc_dispatch->realloc(0, bytes); // return realloc(user(hdr), bytes); // assuming it was allocated externally @@ -501,7 +514,7 @@ extern "C" void* chk_realloc(void* ptr, size_t bytes) { } if (hdr) { hdr->base = hdr; - hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); + hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, bytes); return user(hdr); } @@ -523,7 +536,7 @@ extern "C" void* chk_calloc(size_t nmemb, size_t bytes) { hdr_t* hdr = static_cast(g_malloc_dispatch->calloc(1, size)); if (hdr) { hdr->base = hdr; - hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); + hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, total_bytes); return user(hdr); } @@ -611,7 +624,7 @@ static void ReportMemoryLeaks() { hdr_t* block = head; log_message("+++ %s leaked block of size %d at %p (leak %d of %d)", exe, block->size, user(block), index++, total); - if (del_leak(block, &safe)) { + if (del_leak(block, &safe) && g_backtrace_enabled) { /* safe == 1, because the allocation is valid */ log_backtrace(block->bt, block->bt_depth); } @@ -636,7 +649,17 @@ extern "C" bool malloc_debug_initialize(HashTable* hash_table, const MallocDebug info_log("%s: setting backlog length to %d\n", getprogname(), g_malloc_debug_backlog); } - backtrace_startup(); + // Check if backtracing should be disabled. + char env[PROP_VALUE_MAX]; + if (__system_property_get("libc.debug.malloc.nobacktrace", env) && atoi(env) != 0) { + g_backtrace_enabled = false; + __libc_format_log(ANDROID_LOG_INFO, "libc", "not gathering backtrace information\n"); + } + + if (g_backtrace_enabled) { + backtrace_startup(); + } + return true; } @@ -645,7 +668,9 @@ extern "C" void malloc_debug_finalize(int malloc_debug_level) { if (malloc_debug_level == 10) { ReportMemoryLeaks(); } - backtrace_shutdown(); + if (g_backtrace_enabled) { + backtrace_shutdown(); + } pthread_setspecific(g_debug_calls_disabled, NULL); } diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp index 7926a1f27..837dccc5a 100644 --- a/libc/bionic/malloc_debug_leak.cpp +++ b/libc/bionic/malloc_debug_leak.cpp @@ -47,6 +47,7 @@ #include #include "debug_stacktrace.h" +#include "malloc_debug_backtrace.h" #include "malloc_debug_common.h" #include "malloc_debug_disable.h" @@ -311,7 +312,7 @@ extern "C" void* leak_malloc(size_t bytes) { ScopedPthreadMutexLocker locker(&g_hash_table->lock); uintptr_t backtrace[BACKTRACE_SIZE]; - size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE); + size_t numEntries = GET_BACKTRACE(backtrace, BACKTRACE_SIZE); AllocationEntry* header = reinterpret_cast(base); header->entry = record_backtrace(backtrace, numEntries, bytes); From a0108accb212fd916fb776e6675cc6261d1b1433 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Fri, 15 Aug 2014 18:42:58 -0700 Subject: [PATCH 5/6] Fix leak_realloc, copy entire allocation. Bug: 16874447 Change-Id: Ie54a73fd75529961195fa5173d9116d0ae897b03 --- libc/bionic/malloc_debug_leak.cpp | 64 +++++++++++++++---------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp index 27b671432..df0f997e5 100644 --- a/libc/bionic/malloc_debug_leak.cpp +++ b/libc/bionic/malloc_debug_leak.cpp @@ -385,6 +385,36 @@ extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) { return ptr; } +extern "C" size_t leak_malloc_usable_size(const void* mem) { + if (DebugCallsDisabled()) { + return g_malloc_dispatch->malloc_usable_size(mem); + } + + if (mem == NULL) { + return 0; + } + + // Check the guard to make sure it is valid. + const AllocationEntry* header = const_to_header(mem); + + if (header->guard == MEMALIGN_GUARD) { + // If this is a memalign'd pointer, then grab the header from + // entry. + header = const_to_header(header->entry); + } else if (header->guard != GUARD) { + debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n", + header->guard, header->entry); + return 0; + } + + size_t ret = g_malloc_dispatch->malloc_usable_size(header); + if (ret != 0) { + // The usable area starts at 'mem' and stops at 'header+ret'. + return reinterpret_cast(header) + ret - reinterpret_cast(mem); + } + return 0; +} + extern "C" void* leak_realloc(void* oldMem, size_t bytes) { if (DebugCallsDisabled()) { return g_malloc_dispatch->realloc(oldMem, bytes); @@ -408,7 +438,7 @@ extern "C" void* leak_realloc(void* oldMem, size_t bytes) { newMem = leak_malloc(bytes); if (newMem != NULL) { - size_t oldSize = header->entry->size & ~SIZE_FLAG_MASK; + size_t oldSize = leak_malloc_usable_size(oldMem); size_t copySize = (oldSize <= bytes) ? oldSize : bytes; memcpy(newMem, oldMem, copySize); leak_free(oldMem); @@ -462,38 +492,6 @@ extern "C" void* leak_memalign(size_t alignment, size_t bytes) { return base; } -extern "C" size_t leak_malloc_usable_size(const void* mem) { - if (DebugCallsDisabled()) { - return g_malloc_dispatch->malloc_usable_size(mem); - } - - if (mem != NULL) { - // Check the guard to make sure it is valid. - const AllocationEntry* header = const_to_header((void*)mem); - - if (header->guard == MEMALIGN_GUARD) { - // If this is a memalign'd pointer, then grab the header from - // entry. - header = const_to_header(header->entry); - } else if (header->guard != GUARD) { - debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n", - header->guard, header->entry); - return 0; - } - - // TODO: Temporary workaround to avoid a crash b/16874447. - return header->entry->size & ~SIZE_FLAG_MASK; -#if 0 - size_t ret = g_malloc_dispatch->malloc_usable_size(header); - if (ret != 0) { - // The usable area starts at 'mem' and stops at 'header+ret'. - return reinterpret_cast(header) + ret - reinterpret_cast(mem); - } -#endif - } - return 0; -} - extern "C" struct mallinfo leak_mallinfo() { return g_malloc_dispatch->mallinfo(); } From 20dc3f8fa4b192d902d58f496ae15ff33faa78ac Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 26 Aug 2014 20:48:11 -0700 Subject: [PATCH 6/6] Replace snprintf calls in linker. When enabling debug malloc, the snprintf calls in the linker fails to update the buffer. The problem is that snprintf makes a call to pthread_getspecific that returns a valid pointer, but the data it points to is zero. This should never happen and causes the snprintf to stop and do nothing. Temporarily replace snprintf with a different implementation to work around this issue. Bug: 16874447 Bug: 17302493 Change-Id: I7a500f28adf153150cf2812fae745ff41f1c48d3 --- linker/debugger.cpp | 4 ++-- linker/linker.cpp | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/linker/debugger.cpp b/linker/debugger.cpp index 079682cab..c31615171 100644 --- a/linker/debugger.cpp +++ b/linker/debugger.cpp @@ -170,9 +170,9 @@ static void log_signal_summary(int signum, const siginfo_t* info) { if (info != NULL) { // For a rethrown signal, this si_code will be right and the one debuggerd shows will // always be SI_TKILL. - snprintf(code_desc, sizeof(code_desc), ", code %d", info->si_code); + __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code); if (has_address) { - snprintf(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr); + __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr); } } __libc_format_log(ANDROID_LOG_FATAL, "libc", diff --git a/linker/linker.cpp b/linker/linker.cpp index 52eb56ab8..2d8e07eb4 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -866,7 +866,21 @@ static void soinfo_unload(soinfo* si) { } void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { - snprintf(buffer, buffer_size, "%s:%s", kDefaultLdPaths[0], kDefaultLdPaths[1]); + // Use basic string manipulation calls to avoid snprintf. + // snprintf indirectly calls pthread_getspecific to get the size of a buffer. + // When debug malloc is enabled, this call returns 0. This in turn causes + // snprintf to do nothing, which causes libraries to fail to load. + // See b/17302493 for further details. + // Once the above bug is fixed, this code can be modified to use + // snprintf again. + size_t required_len = strlen(kDefaultLdPaths[0]) + strlen(kDefaultLdPaths[1]) + 2; + if (buffer_size < required_len) { + __libc_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: buffer len %zu, required len %zu", + buffer_size, required_len); + } + char* end = stpcpy(buffer, kDefaultLdPaths[0]); + *end = ':'; + strcpy(end + 1, kDefaultLdPaths[1]); } void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path) {