[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
This commit is contained in:
Marcin Koscielnicki 2016-04-14 12:56:24 +00:00
parent 0ffa9eaa4a
commit c8dda336bb
5 changed files with 89 additions and 0 deletions

View File

@ -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);

View File

@ -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();

View File

@ -56,6 +56,7 @@
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <ucontext.h>
#include <unistd.h>
@ -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

View File

@ -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

View File

@ -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);