Add descriptive names to sanitizer entries in /proc/self/maps. Helps debugging.

This is done by creating a named shared memory region, unlinking it
and setting up a private (i.e. copy-on-write) mapping of that instead
of a regular anonymous mapping. I've experimented with regular
(sparse) files, but they can not be scaled to the size of MSan shadow
mapping, at least on Linux/X86_64 and ext3 fs.

Controlled by a common flag, decorate_proc_maps, disabled by default.

This patch has a few shortcomings:
* not all mappings are annotated, especially in TSan.
* our handling of memset() of shadow via mmap() puts small anonymous
  mappings inside larger named mappings, which looks ugly and can, in
  theory, hit the mapping number limit.

llvm-svn: 238621
This commit is contained in:
Evgeniy Stepanov 2015-05-29 22:31:28 +00:00
parent 2cfd9d574d
commit 8e9c70be7f
20 changed files with 166 additions and 54 deletions

View File

@ -62,6 +62,7 @@ check_symbol_exists(__func__ "" COMPILER_RT_HAS_FUNC_SYMBOL)
# Libraries.
check_library_exists(c printf "" COMPILER_RT_HAS_LIBC)
check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL)
check_library_exists(rt shm_open "" COMPILER_RT_HAS_LIBRT)
check_library_exists(m pow "" COMPILER_RT_HAS_LIBM)
check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD)
check_library_exists(stdc++ __cxa_throw "" COMPILER_RT_HAS_LIBSTDCXX)

View File

@ -66,6 +66,7 @@ append_list_if(MSVC /DEBUG ASAN_DYNAMIC_CFLAGS)
append_list_if(COMPILER_RT_HAS_LIBC c ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBRT rt ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBM m ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBSTDCXX stdc++ ASAN_DYNAMIC_LIBS)

View File

@ -93,7 +93,7 @@ void AppendToErrorMessageBuffer(const char *buffer);
void *AsanDlSymNext(const char *sym);
void ReserveShadowMemoryRange(uptr beg, uptr end);
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
// Platform-specific options.
#if SANITIZER_MAC

View File

@ -64,7 +64,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
if (page_end != shadow_end) {
REAL(memset)((void *)page_end, 0, shadow_end - page_end);
}
ReserveShadowMemoryRange(page_beg, page_end - 1);
ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
}
}
}

View File

@ -89,12 +89,12 @@ void ShowStatsAndAbort() {
// ---------------------- mmap -------------------- {{{1
// Reserve memory range [beg, end].
// We need to use inclusive range because end+1 may not be representable.
void ReserveShadowMemoryRange(uptr beg, uptr end) {
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
CHECK_EQ((beg % GetPageSizeCached()), 0);
CHECK_EQ(((end + 1) % GetPageSizeCached()), 0);
uptr size = end - beg + 1;
DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
void *res = MmapFixedNoReserve(beg, size);
void *res = MmapFixedNoReserve(beg, size, name);
if (res != (void*)beg) {
Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
"Perhaps you're using ulimit -v\n", size);
@ -298,7 +298,7 @@ static void InitializeHighMemEnd() {
}
static void ProtectGap(uptr addr, uptr size) {
void *res = MmapNoAccess(addr, size);
void *res = MmapNoAccess(addr, size, "shadow gap");
if (addr == (uptr)res)
return;
Report("ERROR: Failed to protect the shadow gap. "
@ -422,9 +422,9 @@ static void AsanInitInternal() {
if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
if (kLowShadowBeg)
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gap.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
@ -433,11 +433,11 @@ static void AsanInitInternal() {
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
CHECK(kLowShadowBeg != kLowShadowEnd);
// mmap the low shadow plus at least one page at the left.
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the mid shadow.
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd);
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gaps.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);

View File

@ -99,6 +99,7 @@ append_list_if(ANDROID atomic ASAN_UNITTEST_INSTRUMENTED_LIBS)
set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS})
append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread
ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)

View File

@ -53,16 +53,16 @@ static bool CheckMemoryRangeAvailability(uptr beg, uptr size) {
return true;
}
static bool ProtectMemoryRange(uptr beg, uptr size) {
static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {
if (size > 0) {
void *addr = MmapNoAccess(beg, size);
void *addr = MmapNoAccess(beg, size, name);
if (beg == 0 && addr != 0) {
// Depending on the kernel configuration, we may not be able to protect
// the page at address zero.
uptr gap = 16 * GetPageSizeCached();
beg += gap;
size -= gap;
addr = MmapNoAccess(beg, size);
addr = MmapNoAccess(beg, size, name);
}
if ((uptr)addr != beg) {
uptr end = beg + size - 1;
@ -135,7 +135,7 @@ bool InitShadow(bool init_origins) {
if (map) {
if (!CheckMemoryRangeAvailability(start, size))
return false;
if ((uptr)MmapFixedNoReserve(start, size) != start)
if ((uptr)MmapFixedNoReserve(start, size, kMemoryLayout[i].name) != start)
return false;
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(start, size);
@ -143,7 +143,7 @@ bool InitShadow(bool init_origins) {
if (protect) {
if (!CheckMemoryRangeAvailability(start, size))
return false;
if (!ProtectMemoryRange(start, size))
if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name))
return false;
}
}

View File

@ -69,10 +69,11 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
// Memory management
void *MmapOrDie(uptr size, const char *mem_type);
void UnmapOrDie(void *addr, uptr size);
void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
const char *name = nullptr);
void *MmapNoReserveOrDie(uptr size, const char *mem_type);
void *MmapFixedOrDie(uptr fixed_addr, uptr size);
void *MmapNoAccess(uptr fixed_addr, uptr size);
void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
// Map aligned chunk of address space; size and alignment are powers of two.
void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
// Disallow access to a memory range. Use MmapNoAccess to allocate an

View File

@ -163,3 +163,6 @@ COMMON_FLAG(bool, intercept_strspn, true,
COMMON_FLAG(bool, intercept_strpbrk, true,
"If set, uses custom wrappers for strpbrk function "
"to find more errors.")
COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer "
"mappings in /proc/self/maps with "
"user-readable names")

View File

@ -165,22 +165,6 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
return (void *)p;
}
void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
uptr PageSize = GetPageSizeCached();
uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
RoundUpTo(size, PageSize),
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
-1, 0);
int reserrno;
if (internal_iserror(p, &reserrno))
Report("ERROR: %s failed to "
"allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
SanitizerToolName, size, size, fixed_addr, reserrno);
IncreaseTotalMmap(size);
return (void *)p;
}
void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
uptr PageSize = GetPageSizeCached();
uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
@ -199,13 +183,6 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
return (void *)p;
}
void *MmapNoAccess(uptr fixed_addr, uptr size) {
return (void *)internal_mmap((void*)fixed_addr, size,
PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED |
MAP_NORESERVE, -1, 0);
}
bool MprotectNoAccess(uptr addr, uptr size) {
return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
}

View File

@ -24,6 +24,7 @@
#include "sanitizer_symbolizer.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
@ -31,6 +32,7 @@
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace __sanitizer {
@ -217,6 +219,55 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
#endif
}
#if SANITIZER_ANDROID
int GetNamedMappingFd(const char *name, uptr size) {
return -1;
}
#else
int GetNamedMappingFd(const char *name, uptr size) {
if (!common_flags()->decorate_proc_maps)
return -1;
char shmname[200];
CHECK(internal_strlen(name) < sizeof(shmname) - 10);
internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(),
name);
int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
CHECK_GE(fd, 0);
int res = internal_ftruncate(fd, size);
CHECK_EQ(0, res);
res = shm_unlink(shmname);
CHECK_EQ(0, res);
return fd;
}
#endif
void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
int fd = name ? GetNamedMappingFd(name, size) : -1;
unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
if (fd == -1) flags |= MAP_ANON;
uptr PageSize = GetPageSizeCached();
uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)),
RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE,
flags, fd, 0);
int reserrno;
if (internal_iserror(p, &reserrno))
Report("ERROR: %s failed to "
"allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
SanitizerToolName, size, size, fixed_addr, reserrno);
IncreaseTotalMmap(size);
return (void *)p;
}
void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
int fd = name ? GetNamedMappingFd(name, size) : -1;
unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
if (fd == -1) flags |= MAP_ANON;
return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
0);
}
} // namespace __sanitizer
#endif // SANITIZER_POSIX

View File

@ -104,9 +104,10 @@ void UnmapOrDie(void *addr, uptr size) {
}
}
void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
// FIXME: is this really "NoReserve"? On Win32 this does not matter much,
// but on Win64 it does.
(void)name; // unsupported
void *p = VirtualAlloc((LPVOID)fixed_addr, size,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (p == 0)
@ -125,7 +126,8 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
return MmapOrDie(size, mem_type);
}
void *MmapNoAccess(uptr fixed_addr, uptr size) {
void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
(void)name; // unsupported
void *res = VirtualAlloc((LPVOID)fixed_addr, size,
MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
if (res == 0)

View File

@ -73,6 +73,7 @@ append_list_if(ANDROID log SANITIZER_TEST_LINK_LIBS)
append_list_if(ANDROID atomic SANITIZER_TEST_LINK_LIBS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl SANITIZER_TEST_LINK_FLAGS_COMMON)
append_list_if(COMPILER_RT_HAS_LIBRT -lrt SANITIZER_TEST_LINK_FLAGS_COMMON)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SANITIZER_TEST_LINK_FLAGS_COMMON)
# x86_64 FreeBSD 9.2 additionally requires libc++ to build the tests. Also,
# 'libm' shall be specified explicitly to build i386 tests.

View File

@ -36,7 +36,7 @@ SRCS="
if [ "`uname -a | grep Linux`" != "" ]; then
SUFFIX="linux_amd64"
OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option"
OSLDFLAGS="-lpthread -fPIC -fpie"
OSLDFLAGS="-lpthread -lrt -fPIC -fpie"
SRCS="
$SRCS
../rtl/tsan_platform_linux.cc

View File

@ -202,8 +202,8 @@ static void MapRodata() {
void InitializeShadowMemory() {
// Map memory shadow.
uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
kShadowEnd - kShadowBeg);
uptr shadow =
(uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
@ -232,7 +232,8 @@ void InitializeShadowMemory() {
// Map meta shadow.
uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
uptr meta = (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size);
uptr meta =
(uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
if (meta != kMetaShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "

View File

@ -67,9 +67,12 @@ static char thread_registry_placeholder[sizeof(ThreadRegistry)];
static ThreadContextBase *CreateThreadContext(u32 tid) {
// Map thread trace when context is created.
MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event));
char name[50];
internal_snprintf(name, sizeof(name), "trace %u", tid);
MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event), name);
const uptr hdr = GetThreadTraceHeader(tid);
MapThreadTrace(hdr, sizeof(Trace));
internal_snprintf(name, sizeof(name), "trace header %u", tid);
MapThreadTrace(hdr, sizeof(Trace), name);
new((void*)hdr) Trace();
// We are going to use only a small part of the trace with the default
// value of history_size. However, the constructor writes to the whole trace.
@ -237,7 +240,7 @@ void MapShadow(uptr addr, uptr size) {
// Global data is not 64K aligned, but there are no adjacent mappings,
// so we can get away with unaligned mapping.
// CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier);
MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier, "shadow");
// Meta shadow is 2:1, so tread carefully.
static bool data_mapped = false;
@ -249,7 +252,7 @@ void MapShadow(uptr addr, uptr size) {
if (!data_mapped) {
// First call maps data+bss.
data_mapped = true;
MmapFixedNoReserve(meta_begin, meta_end - meta_begin);
MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow");
} else {
// Mapping continous heap.
// Windows wants 64K alignment.
@ -259,19 +262,19 @@ void MapShadow(uptr addr, uptr size) {
return;
if (meta_begin < mapped_meta_end)
meta_begin = mapped_meta_end;
MmapFixedNoReserve(meta_begin, meta_end - meta_begin);
MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow");
mapped_meta_end = meta_end;
}
VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n",
addr, addr+size, meta_begin, meta_end);
}
void MapThreadTrace(uptr addr, uptr size) {
void MapThreadTrace(uptr addr, uptr size, const char *name) {
DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
CHECK_GE(addr, kTraceMemBeg);
CHECK_LE(addr + size, kTraceMemEnd);
CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
uptr addr1 = (uptr)MmapFixedNoReserve(addr, size);
uptr addr1 = (uptr)MmapFixedNoReserve(addr, size, name);
if (addr1 != addr) {
Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n",
addr, size, addr1);

View File

@ -574,7 +574,7 @@ void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
}
void MapShadow(uptr addr, uptr size);
void MapThreadTrace(uptr addr, uptr size);
void MapThreadTrace(uptr addr, uptr size, const char *name);
void DontNeedShadowFor(uptr addr, uptr size);
void InitializeShadowMemory();
void InitializeInterceptors();

View File

@ -0,0 +1,60 @@
// RUN: %clangxx -g %s -o %t
// RUN: %tool_options=decorate_proc_maps=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%tool_name
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
bool CopyFdToFd(int in_fd, int out_fd) {
const size_t kBufSize = 0x10000;
static char buf[kBufSize];
while (true) {
ssize_t got = read(in_fd, buf, kBufSize);
if (got > 0) {
write(out_fd, buf, got);
} else if (got == 0) {
break;
} else if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR) {
fprintf(stderr, "error reading file, errno %d\n", errno);
return false;
}
}
return true;
}
void *ThreadFn(void *arg) {
(void)arg;
int fd = open("/proc/self/maps", O_RDONLY);
bool res = CopyFdToFd(fd, 2);
close(fd);
return (void *)!res;
}
int main(void) {
pthread_t t;
void *res;
pthread_create(&t, 0, ThreadFn, 0);
pthread_join(t, &res);
return (int)(size_t)res;
}
// CHECK-asan: rw-p {{.*}} [low shadow]
// CHECK-asan: ---p {{.*}} [shadow gap]
// CHECK-asan: rw-p {{.*}} [high shadow]
// CHECK-msan: ---p {{.*}} [invalid]
// CHECK-msan: rw-p {{.*}} [shadow]
// CHECK-msan: ---p {{.*}} [origin]
// CHECK-tsan: rw-p {{.*}} [shadow]
// CHECK-tsan: rw-p {{.*}} [meta shadow]
// CHECK-tsan: rw-p {{.*}} [trace 0]
// CHECK-tsan: rw-p {{.*}} [trace header 0]
// CHECK-tsan: rw-p {{.*}} [trace 1]
// CHECK-tsan: rw-p {{.*}} [trace header 1]
// Nothing interesting with standalone LSan.
// CHECK-lsan: decorate_proc_maps

View File

@ -0,0 +1,9 @@
def getRoot(config):
if not config.parent:
return config
return getRoot(config.parent)
root = getRoot(config)
if root.host_os in ['Windows']:
config.unsupported = True

View File

@ -30,6 +30,7 @@ def build_invocation(compile_flags):
config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) )
config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
config.substitutions.append( ("%tool_name", config.tool_name) )
config.substitutions.append( ("%tool_options", tool_options) )
config.suffixes = ['.c', '.cc', '.cpp']