From c8dda336bbb7edd22df9dfd257d64b7e68514c1f Mon Sep 17 00:00:00 2001 From: Marcin Koscielnicki Date: Thu, 14 Apr 2016 12:56:24 +0000 Subject: [PATCH] [sanitizer] [SystemZ] Abort if the kernel might be vulnerable to CVE-2016-2143. In short, CVE-2016-2143 will crash the machine if a process uses both >4TB virtual addresses and fork(). ASan, TSan, and MSan will, by necessity, map a sizable chunk of virtual address space, which is much larger than 4TB. Even worse, sanitizers will always use fork() for llvm-symbolizer when a bug is detected. Disable all three by aborting on process initialization if the running kernel version is not known to contain a fix. Unfortunately, there's no reliable way to detect the fix without crashing the kernel. So, we rely on whitelisting - I've included a list of upstream kernel versions that will work. In case someone uses a distribution kernel or applied the fix themselves, an override switch is also included. Differential Revision: http://reviews.llvm.org/D18915 llvm-svn: 266297 --- compiler-rt/lib/asan/asan_rtl.cc | 7 +++ compiler-rt/lib/msan/msan.cc | 7 +++ .../lib/sanitizer_common/sanitizer_linux.cc | 67 ++++++++++++++++++++++ compiler-rt/lib/sanitizer_common/sanitizer_linux.h | 5 ++ compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc | 3 + 5 files changed, 89 insertions(+) diff --git a/compiler-rt/lib/asan/asan_rtl.cc b/compiler-rt/lib/asan/asan_rtl.cc index ae8b822..b6d124c 100644 --- a/compiler-rt/lib/asan/asan_rtl.cc +++ b/compiler-rt/lib/asan/asan_rtl.cc @@ -27,6 +27,10 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" +#if defined(__s390x__) && defined(__linux__) +// For AvoidCVE_2016_2143. +#include "sanitizer_common/sanitizer_linux.h" +#endif #include "sanitizer_common/sanitizer_symbolizer.h" #include "lsan/lsan_common.h" #include "ubsan/ubsan_init.h" @@ -415,6 +419,9 @@ static void AsanInitInternal() { AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); +#if defined(__s390x__) && defined(__linux__) + AvoidCVE_2016_2143(); +#endif SetCanPoisonMemory(flags()->poison_heap); SetMallocContextSize(common_flags()->malloc_context_size); diff --git a/compiler-rt/lib/msan/msan.cc b/compiler-rt/lib/msan/msan.cc index cecbfe0..c65e999 100644 --- a/compiler-rt/lib/msan/msan.cc +++ b/compiler-rt/lib/msan/msan.cc @@ -22,6 +22,10 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" +#if defined(__s390x__) && defined(__linux__) +// For AvoidCVE_2016_2143. +#include "sanitizer_common/sanitizer_linux.h" +#endif #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_symbolizer.h" @@ -375,6 +379,9 @@ void __msan_init() { msan_init_is_running = 1; SanitizerToolName = "MemorySanitizer"; +#if defined(__s390x__) && defined(__linux__) + AvoidCVE_2016_2143(); +#endif InitTlsSize(); CacheBinaryName(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index b4f46bd..6c1c8f7 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -1346,6 +1347,72 @@ void MaybeReexec() { // No need to re-exec on Linux. } +#ifdef __s390x__ +static bool FixedCVE_2016_2143() { + // Try to determine if the running kernel has a fix for CVE-2016-2143, + // return false if in doubt (better safe than sorry). Distros may want to + // adjust this for their own kernels. + struct utsname buf; + unsigned int major, minor, patch = 0; + // This should never fail, but just in case... + if (uname(&buf)) + return false; + char *ptr = buf.release; + major = internal_simple_strtoll(ptr, &ptr, 10); + // At least first 2 should be matched. + if (ptr[0] != '.') + return false; + minor = internal_simple_strtoll(ptr+1, &ptr, 10); + // Third is optional. + if (ptr[0] == '.') + patch = internal_simple_strtoll(ptr+1, &ptr, 10); + if (major < 3) { + // <3.0 is bad. + return false; + } else if (major == 3) { + // 3.2.79+ is OK. + if (minor == 2 && patch >= 79) + return true; + // Otherwise, bad. + return false; + } else if (major == 4) { + // 4.1.21+ is OK. + if (minor == 1 && patch >= 21) + return true; + // 4.4.6+ is OK. + if (minor == 4 && patch >= 6) + return true; + // Otherwise, OK if 4.5+. + return minor >= 5; + } else { + // Linux 5 and up are fine. + return true; + } +} + +void AvoidCVE_2016_2143() { + // Older kernels are affected by CVE-2016-2143 - they will crash hard + // if someone uses 4-level page tables (ie. virtual addresses >= 4TB) + // and fork() in the same process. Unfortunately, sanitizers tend to + // require such addresses. Since this is very likely to crash the whole + // machine (sanitizers themselves use fork() for llvm-symbolizer, for one), + // abort the process at initialization instead. + if (FixedCVE_2016_2143()) + return; + if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143")) + return; + Report( + "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n" + "MSan or TSan with such kernel can and will crash your machine, or worse.\n" + "\n" + "If you are certain your kernel is not vulnerable (you have compiled it\n" + "yourself, are are using an unrecognized distribution kernel), you can\n" + "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n" + "with any value.\n"); + Die(); +} +#endif + } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index a61198f..43fc6d6 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -83,6 +83,11 @@ bool LibraryNameIs(const char *full_name, const char *base_name); // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)); + +#ifdef __s390x__ +// Aborts the process if running on a kernel without a fix for CVE-2016-2143. +void AvoidCVE_2016_2143(); +#endif } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc index c10fce5..e5c1ee0 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc @@ -247,6 +247,9 @@ static void InitDataSeg() { #endif // #ifndef SANITIZER_GO void InitializePlatformEarly() { +#ifdef __s390x__ + AvoidCVE_2016_2143(); +#endif #ifdef TSAN_RUNTIME_VMA vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); -- 2.7.4