x86_64-*-linux* | i?86-*-linux*)
if test x$ac_cv_sizeof_void_p = x8; then
TSAN_SUPPORTED=yes
- LSAN_SUPPORTED=yes
TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_amd64.lo
fi
+ LSAN_SUPPORTED=yes
;;
- powerpc*-*-linux*)
+ powerpc*le-*-linux*)
+ UNSUPPORTED=1
;;
sparc*-*-linux*)
;;
arm*-*-linux*)
+ LSAN_SUPPORTED=yes
;;
aarch64*-*-linux*)
if test x$ac_cv_sizeof_void_p = x8; then
return false;
}
+#if LSAN_DYNAMIC
+// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
+// (and thus normal initializers from .preinit_array or modules haven't run).
+
+class LsanInitializer {
+ public: // NOLINT
+ LsanInitializer() {
+ __lsan_init();
+ }
+};
+
+static LsanInitializer lsan_initializer;
+#endif // LSAN_DYNAMIC
+
} // namespace __lsan
using namespace __lsan; // NOLINT
cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
cf.malloc_context_size = 30;
cf.detect_leaks = true;
- cf.exitcode = 23;
+ cf.exitcode = 0;
OverrideCommonFlags(cf);
}
extern "C" void __lsan_init() {
CHECK(!lsan_init_is_running);
- if (lsan_inited)
+ if (LIKELY(lsan_inited))
return;
+ MaybeMountProcFS();
lsan_init_is_running = true;
SanitizerToolName = "LeakSanitizer";
CacheBinaryName();
GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
common_flags()->fast_unwind_on_malloc)
+#ifndef LSAN_DYNAMIC
+# ifdef PIC
+# define LSAN_DYNAMIC 1
+# else
+# define LSAN_DYNAMIC 0
+# endif
+#endif
+
namespace __lsan {
void InitializeInterceptors();
struct ChunkMetadata {
u8 allocated : 8; // Must be first.
ChunkTag tag : 2;
+#if SANITIZER_WORDSIZE == 64
uptr requested_size : 54;
+#else
+ uptr requested_size : 32;
+ uptr padding : 22;
+#endif
u32 stack_trace_id;
};
+#if SANITIZER_WORDSIZE == 64
#if defined(__mips64) || defined(__aarch64__)
static const uptr kMaxAllowedMallocSize = 4UL << 30;
static const uptr kRegionSizeLog = 20;
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize,
sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator;
#endif
+#else // SANITIZER_WORDSIZE == 32
+typedef CompactSizeClassMap SizeClassMap;
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef FlatByteMap<kNumRegions> ByteMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE,
+ sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap>
+ PrimaryAllocator;
+static const uptr kMaxAllowedMallocSize = 1UL << 30;
+#endif
+
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
if (addr < chunk) return 0;
ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
CHECK(m);
- if (!m->allocated)
+ if (!allocator.PointsIntoChunk(p))
+ return 0;
+ if (m->allocated != 1)
return 0;
if (addr < chunk + m->requested_size)
return chunk;
ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
}
+// TODO: support static TLS for ARM and x86.
+#if !defined(__arm__) && !defined(__i386__)
if (flags()->use_tls) {
LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
if (cache_begin == cache_end) {
ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
}
}
+#endif
}
}
// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks
// which are reachable from it as indirectly leaked.
static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
+// On 32-bit archs it's hard to distinguish between direct and indirect leaks.
+#if SANITIZER_WORDSIZE == 32
+ return;
+#endif
chunk = GetUserBegin(chunk);
LsanMetadata m(chunk);
if (m.allocated() && m.tag() != kReachable) {
#if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) \
&& (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__))
#define CAN_SANITIZE_LEAKS 1
+#elif defined(__arm__) || defined(__i386__)
+#define CAN_SANITIZE_LEAKS 1
#else
#define CAN_SANITIZE_LEAKS 0
#endif
#include "lsan.h"
#include "lsan_allocator.h"
#include "lsan_thread.h"
+#include <stddef.h>
using namespace __lsan;
return Allocate(stack, size, 1, kAlwaysClearMemory);
INTERCEPTOR_ATTRIBUTE
-void *operator new(uptr size) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size) { OPERATOR_NEW_BODY; }
INTERCEPTOR_ATTRIBUTE
-void *operator new[](uptr size) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
INTERCEPTOR_ATTRIBUTE
-void *operator new(uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
INTERCEPTOR_ATTRIBUTE
-void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
#define OPERATOR_DELETE_BODY \
ENSURE_LSAN_INITED; \
(1 + chunk_idx) * kMetadataSize);
}
+ bool PointsIntoChunk(const void *p) {
+ CHECK(PointerIsMine(p));
+ uptr mem = reinterpret_cast<uptr>(p);
+ uptr class_id = GetSizeClass(p);
+ uptr size = SizeClassMap::Size(class_id);
+ uptr beg = kSpaceBeg + kRegionSize * class_id;
+ uptr n_chunks = kRegionSize / (size + kMetadataSize);
+ uptr gap_start = beg + n_chunks * size + 1;
+ return (beg <= mem) && (mem < gap_start);
+ }
+
uptr TotalMemoryUsed() {
uptr res = 0;
for (uptr i = 0; i < kNumClasses; i++)
return reinterpret_cast<void*>(meta);
}
+ bool PointsIntoChunk(const void *p) {
+ CHECK(PointerIsMine(p));
+ uptr mem = reinterpret_cast<uptr>(p);
+ uptr beg = ComputeRegionBeg(mem);
+ uptr size = SizeClassMap::Size(GetSizeClass(p));
+ uptr n_chunks = kRegionSize / (size + kMetadataSize);
+ uptr gap_start = beg + n_chunks * size + 1;
+ return (beg <= mem) && (mem < gap_start);
+ }
+
NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c,
uptr class_id) {
CHECK_LT(class_id, kNumClasses);
primary_.ForceUnlock();
}
+ bool PointsIntoChunk(const void *p) {
+ if (primary_.PointerIsMine(p))
+ return primary_.PointsIntoChunk(p);
+ return true;
+ }
+
// Iterate over all existing chunks.
// The allocator must be locked when calling this function.
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
: "x30", "memory");
return res;
}
+#elif defined(__arm__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ unsigned int res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
+ ((unsigned int *)child_stack)[0] = (uptr)fn;
+ ((unsigned int *)child_stack)[1] = (uptr)arg;
+ register int r0 __asm__("r0") = flags;
+ register void *r1 __asm__("r1") = child_stack;
+ register int *r2 __asm__("r2") = parent_tidptr;
+ register void *r3 __asm__("r3") = newtls;
+ register int *r4 __asm__("r4") = child_tidptr;
+ register int r7 __asm__("r7") = __NR_clone;
+ __asm__ __volatile__("push {r4, r7}\n"
+ "swi 0x0\n"
+ "cmp r0, #0\n"
+ "bne 1f\n"
+ "ldr r0, [sp, #4]\n"
+#if defined(__ARM_ARCH_4T__) && defined(__THUMB_INTERWORK__)
+ "ldr ip, [sp], #8\n"
+ "mov lr, pc\n"
+ "bx ip\n"
+#else
+ "ldr lr, [sp], #8\n"
+ "blx lr\n"
+#endif
+ "mov r7, %7\n"
+ "swi 0x0\n"
+ "1:\n"
+ "pop {r4, r7}\n"
+ "mov %0, r0\n"
+ : "=r"(res)
+ : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
+ "i"(__NR_exit)
+ : "memory");
+ return res;
+}
+#elif defined(__i386__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ int res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 7 * sizeof(unsigned int);
+ ((unsigned int *)child_stack)[0] = (uptr)flags;
+ ((unsigned int *)child_stack)[1] = (uptr)0;
+ ((unsigned int *)child_stack)[2] = (uptr)fn;
+ ((unsigned int *)child_stack)[3] = (uptr)arg;
+ __asm__ __volatile__(
+ /* %eax = syscall(%eax = SYSCALL(clone),
+ * %ebx = flags,
+ * %ecx = child_stack,
+ * %edx = parent_tidptr,
+ * %esi = new_tls,
+ * %edi = child_tidptr)
+ */
+
+ /* Obtain flags */
+ "movl (%%ecx), %%ebx\n"
+ /* Do the system call */
+ "pushl %%ebx\n"
+ "pushl %%esi\n"
+ "pushl %%edi\n"
+ /* Remember the flag value. */
+ "movl %%ebx, (%%ecx)\n"
+ "int $0x80\n"
+ "popl %%edi\n"
+ "popl %%esi\n"
+ "popl %%ebx\n"
+
+ /* if (%eax != 0)
+ * return;
+ */
+
+ "test %%eax,%%eax\n"
+ "jnz 1f\n"
+
+ /* terminate the stack frame */
+ "xorl %%ebp,%%ebp\n"
+ /* Call FN. */
+ "call *%%ebx\n"
+#ifdef PIC
+ "call here\n"
+ "here:\n"
+ "popl %%ebx\n"
+ "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
+#endif
+ /* Call exit */
+ "movl %%eax, %%ebx\n"
+ "movl %2, %%eax\n"
+ "int $0x80\n"
+ "1:\n"
+ : "=a" (res)
+ : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
+ "c"(child_stack),
+ "d"(parent_tidptr),
+ "S"(newtls),
+ "D"(child_tidptr)
+ : "memory");
+ return res;
+}
#endif // defined(__x86_64__) && SANITIZER_LINUX
#if SANITIZER_ANDROID
// internal_sigaction instead.
int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
-#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
+#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || defined(__arm__) || defined(__i386__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
#endif
#include "sanitizer_platform.h"
#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
- defined(__aarch64__))
+ defined(__aarch64__) || defined(__arm__) || \
+ defined(__i386__))
#include "sanitizer_stoptheworld.h"
} // namespace __sanitizer
#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
- // || defined(__aarch64__)
+ // || defined(__aarch64__) || defined(__arm__))