From 51458256a2e1beda59e2e7c11c5059ad2ca2b5cf Mon Sep 17 00:00:00 2001 From: Sagar Thakur Date: Thu, 6 Oct 2016 09:58:11 +0000 Subject: [PATCH] [ESan][MIPS] Adds support for MIPS64 With this patch 12 out of 13 tests are passing. Reviewed by zhaoqin. Differential: D23799 llvm-svn: 283435 --- compiler-rt/cmake/config-ix.cmake | 2 +- compiler-rt/lib/esan/esan.cpp | 8 ++ compiler-rt/lib/esan/esan.h | 1 + compiler-rt/lib/esan/esan_linux.cpp | 2 +- compiler-rt/lib/esan/esan_shadow.h | 131 +++++++++++++++--- .../lib/sanitizer_common/CMakeLists.txt | 4 + .../lib/sanitizer_common/sanitizer_linux.cc | 4 +- .../lib/sanitizer_common/sanitizer_linux.h | 2 +- .../sanitizer_common/sanitizer_linux_mips64.S | 23 +++ .../sanitizer_platform_limits_posix.h | 12 ++ .../esan/TestCases/mmap-shadow-conflict.c | 24 +++- .../test/esan/TestCases/verbose-simple.c | 18 ++- compiler-rt/test/esan/lit.cfg | 2 +- compiler-rt/test/lit.common.cfg | 3 + 14 files changed, 197 insertions(+), 39 deletions(-) create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_linux_mips64.S diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index 2cefff5774d5..cb9f74b061ad 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -159,7 +159,7 @@ set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X}) set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64}) set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64}) -set(ALL_ESAN_SUPPORTED_ARCH ${X86_64}) +set(ALL_ESAN_SUPPORTED_ARCH ${X86_64} ${MIPS64}) set(ALL_SCUDO_SUPPORTED_ARCH ${X86_64}) set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32}) diff --git a/compiler-rt/lib/esan/esan.cpp b/compiler-rt/lib/esan/esan.cpp index 2fb77894d4fb..09b530b6645f 100644 --- a/compiler-rt/lib/esan/esan.cpp +++ b/compiler-rt/lib/esan/esan.cpp @@ -141,9 +141,17 @@ static bool verifyShadowScheme() { } #endif +uptr VmaSize; + static void initializeShadow() { verifyAddressSpace(); + // This is based on the assumption that the intial stack is always allocated + // in the topmost segment of the user address space and the assumption + // holds true on all the platforms currently supported. + VmaSize = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); + DCHECK(verifyShadowScheme()); Mapping.initialize(ShadowScale[__esan_which_tool]); diff --git a/compiler-rt/lib/esan/esan.h b/compiler-rt/lib/esan/esan.h index 5a0dde627888..e73b21e56ff1 100644 --- a/compiler-rt/lib/esan/esan.h +++ b/compiler-rt/lib/esan/esan.h @@ -34,6 +34,7 @@ namespace __esan { extern bool EsanIsInitialized; extern bool EsanDuringInit; +extern uptr VmaSize; void initializeLibrary(ToolType Tool); int finalizeLibrary(); diff --git a/compiler-rt/lib/esan/esan_linux.cpp b/compiler-rt/lib/esan/esan_linux.cpp index aa961b66116b..014205ce01eb 100644 --- a/compiler-rt/lib/esan/esan_linux.cpp +++ b/compiler-rt/lib/esan/esan_linux.cpp @@ -25,7 +25,7 @@ namespace __esan { void verifyAddressSpace() { -#if SANITIZER_LINUX && defined(__x86_64__) +#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_MIPS64) // The kernel determines its mmap base from the stack size limit. // Our Linux 64-bit shadow mapping assumes the stack limit is less than a // terabyte, which keeps the mmap region above 0x7e00'. diff --git a/compiler-rt/lib/esan/esan_shadow.h b/compiler-rt/lib/esan/esan_shadow.h index f8f154ef7cca..72a919a190d8 100644 --- a/compiler-rt/lib/esan/esan_shadow.h +++ b/compiler-rt/lib/esan/esan_shadow.h @@ -15,6 +15,7 @@ #ifndef ESAN_SHADOW_H #define ESAN_SHADOW_H +#include "esan.h" #include #if SANITIZER_WORDSIZE != 64 @@ -23,6 +24,12 @@ namespace __esan { +struct ApplicationRegion { + uptr Start; + uptr End; + bool ShadowMergedWithPrev; +}; + #if SANITIZER_LINUX && defined(__x86_64__) // Linux x86_64 // @@ -89,12 +96,6 @@ namespace __esan { // [0x000015ff'ff601000, 0x00001600'00000000] // [0x000015ff'ff600000, 0x000015ff'ff601000] -struct ApplicationRegion { - uptr Start; - uptr End; - bool ShadowMergedWithPrev; -}; - static const struct ApplicationRegion AppRegions[] = { {0x0000000000000000ull, 0x0000010000000000u, false}, {0x0000550000000000u, 0x0000570000000000u, false}, @@ -105,6 +106,52 @@ static const struct ApplicationRegion AppRegions[] = { {0x00007fffff601000u, 0x0000800000000000u, true}, {0xffffffffff600000u, 0xffffffffff601000u, true}, }; + +#elif SANITIZER_LINUX && SANITIZER_MIPS64 + +// Application memory falls into these 3 regions +// +// [0x00000001'00000000, 0x00000002'00000000) non-PIE + heap +// [0x000000aa'00000000, 0x000000ab'00000000) PIE +// [0x000000ff'00000000, 0x000000ff'ffffffff) libraries + stack +// +// This formula translates from application memory to shadow memory: +// +// shadow(app) = ((app & 0x00000f'ffffffff) + offset) >> scale +// +// Where the offset for 1:1 is 0x000013'00000000. For other scales, the +// offset is shifted left by the scale, except for scales of 1 and 2 where +// it must be tweaked in order to pass the double-shadow test +// (see the "shadow(shadow)" comments below): +// scale == 0: 0x000013'00000000 +// scale == 1: 0x000022'00000000 +// scale == 2: 0x000044'00000000 +// scale >= 3: (0x000013'00000000 << scale) +// +// The resulting shadow memory regions for a 0 scaling are: +// +// [0x00000014'00000000, 0x00000015'00000000) +// [0x0000001d'00000000, 0x0000001e'00000000) +// [0x00000022'00000000, 0x00000022'ffffffff) +// +// We also want to ensure that a wild access by the application into the shadow +// regions will not corrupt our own shadow memory. shadow(shadow) ends up +// disjoint from shadow(app): +// +// [0x00000017'00000000, 0x00000018'00000000) +// [0x00000020'00000000, 0x00000021'00000000) +// [0x00000015'00000000, 0x00000015'ffffffff] + +static const struct ApplicationRegion AppRegions[] = { + {0x0100000000u, 0x0200000000u, false}, + {0xaa00000000u, 0xab00000000u, false}, + {0xff00000000u, 0xffffffffffu, false}, +}; + +#else +#error Platform not supported +#endif + static const u32 NumAppRegions = sizeof(AppRegions)/sizeof(AppRegions[0]); // See the comment above: we do not currently support a stack size rlimit @@ -113,29 +160,59 @@ static const uptr MaxStackSize = (1ULL << 40) - 4096; class ShadowMapping { public: - static const uptr Mask = 0x00000fffffffffffu; + // The scale and offset vary by tool. uptr Scale; uptr Offset; + + // TODO(sagar.thakur): Try to hardcode the mask as done in the compiler + // instrumentation to reduce the runtime cost of appToShadow. + struct ShadowMemoryMask40 { + static const uptr Mask = 0x0000000fffffffffu; + }; + + struct ShadowMemoryMask47 { + static const uptr Mask = 0x00000fffffffffffu; + }; + void initialize(uptr ShadowScale) { - static const uptr OffsetArray[3] = { - 0x0000130000000000u, - 0x0000220000000000u, - 0x0000440000000000u, + + const uptr OffsetArray40[3] = { + 0x0000001300000000u, + 0x0000002200000000u, + 0x0000004400000000u, }; + + const uptr OffsetArray47[3] = { + 0x0000130000000000u, + 0x0000220000000000u, + 0x0000440000000000u, + }; + Scale = ShadowScale; - if (Scale <= 2) - Offset = OffsetArray[Scale]; - else - Offset = OffsetArray[0] << Scale; + switch (VmaSize) { + case 40: { + if (Scale <= 2) + Offset = OffsetArray40[Scale]; + else + Offset = OffsetArray40[0] << Scale; + } + break; + case 47: { + if (Scale <= 2) + Offset = OffsetArray47[Scale]; + else + Offset = OffsetArray47[0] << Scale; + } + break; + default: { + Printf("ERROR: %d-bit virtual memory address size not supported\n", VmaSize); + Die(); + } + } } }; extern ShadowMapping Mapping; -#else -// We'll want to use templatized functions over the ShadowMapping once -// we support more platforms. -#error Platform not supported -#endif static inline bool getAppRegion(u32 i, uptr *Start, uptr *End) { if (i >= NumAppRegions) @@ -154,9 +231,21 @@ bool isAppMem(uptr Mem) { return false; } +template +uptr appToShadowImpl(uptr App) { + return (((App & Params::Mask) + Mapping.Offset) >> Mapping.Scale); +} + ALWAYS_INLINE uptr appToShadow(uptr App) { - return (((App & ShadowMapping::Mask) + Mapping.Offset) >> Mapping.Scale); + switch (VmaSize) { + case 40: return appToShadowImpl(App); + case 47: return appToShadowImpl(App); + default: { + Printf("ERROR: %d-bit virtual memory address size not supported\n", VmaSize); + Die(); + } + } } static inline bool getShadowRegion(u32 i, uptr *Start, uptr *End) { diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt index 59a6b3511d9c..007d93cc1687 100644 --- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt @@ -37,6 +37,8 @@ set(SANITIZER_SOURCES_NOTERMINATION if(UNIX AND NOT APPLE) list(APPEND SANITIZER_SOURCES_NOTERMINATION sanitizer_linux_x86_64.S) + list(APPEND SANITIZER_SOURCES_NOTERMINATION + sanitizer_linux_mips64.S) endif() set(SANITIZER_SOURCES @@ -147,6 +149,8 @@ if (LLVM_ENABLE_PEDANTIC AND UNIX AND NOT APPLE) # CMAKE_C*_FLAGS and re-add as a source property to all the non-.S files). set_source_files_properties(sanitizer_linux_x86_64.S PROPERTIES COMPILE_FLAGS "-w") + set_source_files_properties(sanitizer_linux_mips64.S + PROPERTIES COMPILE_FLAGS "-w") endif () if(APPLE) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index b2d469ea391b..5784fec9b2d6 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -99,7 +99,7 @@ const int FUTEX_WAKE = 1; # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 #endif -#if defined(__x86_64__) +#if defined(__x86_64__) || SANITIZER_MIPS64 extern "C" { extern void internal_sigreturn(); } @@ -671,7 +671,7 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { // Invokes sigaction via a raw syscall with a restorer, but does not support // all platforms yet. // We disable for Go simply because we have not yet added to buildgo.sh. -#if defined(__x86_64__) && !SANITIZER_GO +#if (defined(__x86_64__) || SANITIZER_MIPS64) && !SANITIZER_GO int internal_sigaction_syscall(int signum, const void *act, void *oldact) { if (act == nullptr) return internal_sigaction_norestorer(signum, act, oldact); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index 526fa4426e34..d4d0f47eed02 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -42,7 +42,7 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); // (like the process-wide error reporting SEGV handler) must use // internal_sigaction instead. int internal_sigaction_norestorer(int signum, const void *act, void *oldact); -#if defined(__x86_64__) && !SANITIZER_GO +#if (defined(__x86_64__) || SANITIZER_MIPS64) && !SANITIZER_GO // Uses a raw system call to avoid interceptors. int internal_sigaction_syscall(int signum, const void *act, void *oldact); #endif diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux_mips64.S b/compiler-rt/lib/sanitizer_common/sanitizer_linux_mips64.S new file mode 100644 index 000000000000..8729642aa654 --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux_mips64.S @@ -0,0 +1,23 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +// Avoid being marked as needing an executable stack: +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +// Further contents are mips64 only: +#if defined(__linux__) && defined(__mips64) + +.section .text +.set noreorder +.globl internal_sigreturn +.type internal_sigreturn, @function +internal_sigreturn: + + li $v0,5211 // #5211 is for SYS_rt_sigreturn + syscall + +.size internal_sigreturn, .-internal_sigreturn + +#endif // defined(__linux__) && defined(__mips64) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h index 0a7a5e71ebbe..659fa17be5fc 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -616,6 +616,17 @@ namespace __sanitizer { #endif // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. +#if SANITIZER_MIPS + struct __sanitizer_kernel_sigaction_t { + unsigned int sa_flags; + union { + void (*handler)(int signo); + void (*sigaction)(int signo, void *info, void *ctx); + }; + __sanitizer_kernel_sigset_t sa_mask; + void (*sa_restorer)(void); + }; +#else struct __sanitizer_kernel_sigaction_t { union { void (*handler)(int signo); @@ -625,6 +636,7 @@ namespace __sanitizer { void (*sa_restorer)(void); __sanitizer_kernel_sigset_t sa_mask; }; +#endif extern uptr sig_ign; extern uptr sig_dfl; diff --git a/compiler-rt/test/esan/TestCases/mmap-shadow-conflict.c b/compiler-rt/test/esan/TestCases/mmap-shadow-conflict.c index 4b3c58b06825..8e86bba4adb1 100644 --- a/compiler-rt/test/esan/TestCases/mmap-shadow-conflict.c +++ b/compiler-rt/test/esan/TestCases/mmap-shadow-conflict.c @@ -1,26 +1,40 @@ // RUN: %clang_esan_frag -O0 %s -o %t 2>&1 -// RUN: %env_esan_opts=verbosity=1 %run %t 2>&1 | FileCheck %s +// RUN: %env_esan_opts=verbosity=1 %run %t 2>&1 | FileCheck --check-prefix=%arch --check-prefix=CHECK %s #include #include #include int main(int argc, char **argv) { +#if defined(__mips64) + void *Map = mmap((void *)0x0000001600000000ULL, 0x1000, PROT_READ, + MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0); +#else void *Map = mmap((void *)0x0000016000000000ULL, 0x1000, PROT_READ, MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0); +#endif if (Map == (void *)-1) fprintf(stderr, "map failed\n"); else fprintf(stderr, "mapped %p\n", Map); +#if defined(__mips64) + Map = mmap((void *)0x0000001600000000ULL, 0x1000, PROT_READ, + MAP_ANON|MAP_PRIVATE, -1, 0); +#else Map = mmap((void *)0x0000016000000000ULL, 0x1000, PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0); +#endif fprintf(stderr, "mapped %p\n", Map); // CHECK: in esan::initializeLibrary // (There can be a re-exec for stack limit here.) - // CHECK: Shadow scale=2 offset=0x440000000000 - // CHECK-NEXT: Shadow #0: [110000000000-114000000000) (256GB) - // CHECK-NEXT: Shadow #1: [124000000000-12c000000000) (512GB) - // CHECK-NEXT: Shadow #2: [148000000000-150000000000) (512GB) + // x86_64: Shadow scale=2 offset=0x440000000000 + // x86_64-NEXT: Shadow #0: [110000000000-114000000000) (256GB) + // x86_64-NEXT: Shadow #1: [124000000000-12c000000000) (512GB) + // x86_64-NEXT: Shadow #2: [148000000000-150000000000) (512GB) + // mips64: Shadow scale=2 offset=0x4400000000 + // mips64-NEXT: Shadow #0: [1140000000-1180000000) (1GB) + // mips64-NEXT: Shadow #1: [1380000000-13c0000000) (1GB) + // mips64-NEXT: Shadow #2: [14c0000000-1500000000) (1GB) // CHECK-NEXT: mmap conflict: {{.*}} // CHECK-NEXT: map failed // CHECK-NEXT: mmap conflict: {{.*}} diff --git a/compiler-rt/test/esan/TestCases/verbose-simple.c b/compiler-rt/test/esan/TestCases/verbose-simple.c index 0d867bf55d9e..5ac37e159295 100644 --- a/compiler-rt/test/esan/TestCases/verbose-simple.c +++ b/compiler-rt/test/esan/TestCases/verbose-simple.c @@ -1,14 +1,18 @@ // RUN: %clang_esan_frag -O0 %s -o %t 2>&1 -// RUN: %env_esan_opts="verbosity=1 log_exe_name=1" %run %t 2>&1 | FileCheck %s +// RUN: %env_esan_opts="verbosity=1 log_exe_name=1" %run %t 2>&1 | FileCheck --check-prefix=%arch --check-prefix=CHECK %s int main(int argc, char **argv) { // CHECK: in esan::initializeLibrary // (There can be a re-exec for stack limit here.) - // CHECK: Shadow scale=2 offset=0x440000000000 - // CHECK-NEXT: Shadow #0: [110000000000-114000000000) (256GB) - // CHECK-NEXT: Shadow #1: [124000000000-12c000000000) (512GB) - // CHECK-NEXT: Shadow #2: [148000000000-150000000000) (512GB) - // CHECK-NEXT: in esan::finalizeLibrary - // CHECK-NEXT: ==verbose-simple{{.*}}EfficiencySanitizer: total struct field access count = 0 + // x86_64: Shadow scale=2 offset=0x440000000000 + // x86_64-NEXT: Shadow #0: [110000000000-114000000000) (256GB) + // x86_64-NEXT: Shadow #1: [124000000000-12c000000000) (512GB) + // x86_64-NEXT: Shadow #2: [148000000000-150000000000) (512GB) + // mips64: Shadow scale=2 offset=0x4400000000 + // mips64-NEXT: Shadow #0: [1140000000-1180000000) (1GB) + // mips64-NEXT: Shadow #1: [1380000000-13c0000000) (1GB) + // mips64-NEXT: Shadow #2: [14c0000000-1500000000) (1GB) + // CHECK: in esan::finalizeLibrary + // CHECK: ==verbose-simple{{.*}}EfficiencySanitizer: total struct field access count = 0 return 0; } diff --git a/compiler-rt/test/esan/lit.cfg b/compiler-rt/test/esan/lit.cfg index cf16a6b5df44..8b8457d66e00 100644 --- a/compiler-rt/test/esan/lit.cfg +++ b/compiler-rt/test/esan/lit.cfg @@ -40,5 +40,5 @@ config.substitutions.append(('%env_esan_opts=', config.suffixes = ['.c', '.cpp'] # EfficiencySanitizer tests are currently supported on Linux x86-64 only. -if config.host_os not in ['Linux'] or config.target_arch != 'x86_64': +if config.host_os not in ['Linux'] or config.target_arch not in ['x86_64', 'mips64'] : config.unsupported = True diff --git a/compiler-rt/test/lit.common.cfg b/compiler-rt/test/lit.common.cfg index d4e7eb31062e..ed7a205a5e0e 100644 --- a/compiler-rt/test/lit.common.cfg +++ b/compiler-rt/test/lit.common.cfg @@ -88,6 +88,9 @@ config.substitutions.append( ('%run', config.emulator) ) # Define CHECK-%os to check for OS-dependent output. config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) +# Define %arch to check for architecture-dependent output. +config.substitutions.append( ('%arch', (config.host_arch))) + if config.host_os == 'Windows': # FIXME: This isn't quite right. Specifically, it will succeed if the program # does not crash but exits with a non-zero exit code. We ought to merge