Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -556,6 +556,12 @@ SanitizerToolName = "AddressSanitizer"; CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; + + // Initialize flags. This must be done early, because most of the + // initialization steps look at flags(). + const char *options = GetEnv("ASAN_OPTIONS"); + InitializeFlags(flags(), options); + InitializeHighMemEnd(); // Make sure we are not statically linked. @@ -566,11 +572,6 @@ SetCheckFailedCallback(AsanCheckFailed); SetPrintfAndReportCallback(AppendToErrorMessageBuffer); - // Initialize flags. This must be done early, because most of the - // initialization steps look at flags(). - const char *options = GetEnv("ASAN_OPTIONS"); - InitializeFlags(flags(), options); - if (!flags()->start_deactivated) ParseExtraActivationFlags(); Index: lib/sanitizer_common/sanitizer_flags.h =================================================================== --- lib/sanitizer_common/sanitizer_flags.h +++ lib/sanitizer_common/sanitizer_flags.h @@ -54,6 +54,7 @@ bool help; uptr mmap_limit_mb; bool coverage; + bool full_address_space; }; inline CommonFlags *common_flags() { Index: lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- lib/sanitizer_common/sanitizer_flags.cc +++ lib/sanitizer_common/sanitizer_flags.cc @@ -55,6 +55,7 @@ f->legacy_pthread_cond = false; f->intercept_tls_get_addr = false; f->coverage = false; + f->full_address_space = false; } void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { @@ -126,6 +127,9 @@ ParseFlag(str, &f->coverage, "coverage", "If set, coverage information will be dumped at program shutdown (if the " "coverage instrumentation was enabled at compile time)."); + ParseFlag(str, &f->full_address_space, "full_address_space", + "Sanitize complete address space; " + "by default kernel area on 32-bit platforms will not be sanitized"); // Do a sanity check for certain flags. if (f->malloc_context_size < 1) Index: lib/sanitizer_common/sanitizer_posix.cc =================================================================== --- lib/sanitizer_common/sanitizer_posix.cc +++ lib/sanitizer_common/sanitizer_posix.cc @@ -24,6 +24,7 @@ #if SANITIZER_LINUX #include +#include #endif namespace __sanitizer { @@ -34,16 +35,36 @@ } #if SANITIZER_WORDSIZE == 32 -// Take care of unusable kernel area in top gigabyte -static uptr GetKernelStartAddress() { -#if 0 // SANITIZER_LINUX - // FIXME: this code is too naive. We have a situation where the machine is a - // true x8_64, but under schroot uname returns i686. - // 64-bit Linux provides 32-bit apps with full address space +// Take care of unusable kernel area in top gigabyte. +static uptr GetKernelAreaSize() { +#if SANITIZER_LINUX + const uptr gbyte = 1UL << 30; + + // Firstly check if there are writable segments + // mapped to top gigabyte (e.g. stack). + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr end, prot; + while (proc_maps.Next(/*start*/0, &end, + /*offset*/0, /*filename*/0, + /*filename_size*/0, &prot)) { + if ((end >= 3 * gbyte) + && (prot & MemoryMappingLayout::kProtectionWrite) != 0) + return 0; + } + + // Even if nothing is mapped, top Gb may still be accessible + // if we are running on 64-bit kernel. + // Uname may report misleading results if personality type + // is modified (e.g. under schroot) so check this as well. struct utsname uname_info; - return 0 == uname(&uname_info) && !internal_strstr(uname_info.machine, "64") - ? 1ULL << 30 - : 0; + int pers = personality(0xffffffffUL); + if (!(pers & PER_MASK) + && uname(&uname_info) == 0 + && internal_strstr(uname_info.machine, "64")) + return 0; + + // Top gigabyte is reserved for kernel. + return gbyte; #else return 0; #endif // SANITIZER_LINUX @@ -66,7 +87,8 @@ # endif #else // SANITIZER_WORDSIZE == 32 uptr res = (1ULL << 32) - 1; // 0xffffffff; - res -= GetKernelStartAddress(); + if (!common_flags()->full_address_space) + res -= GetKernelAreaSize(); CHECK_LT(reinterpret_cast(&res), res); return res; #endif // SANITIZER_WORDSIZE Index: test/asan/TestCases/Linux/kernel-area.cc =================================================================== --- test/asan/TestCases/Linux/kernel-area.cc +++ test/asan/TestCases/Linux/kernel-area.cc @@ -2,6 +2,8 @@ // // RUN: %clangxx_asan %s -o %t // RUN: ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits +// RUN: ASAN_OPTIONS=verbosity=1:full_address_space=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits +// RUN: ASAN_OPTIONS=verbosity=1:full_address_space=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-kernel-64-bits // // CHECK-kernel-32-bits: || `[0x38000000, 0xbfffffff]` || HighMem || // CHECK-kernel-32-bits: || `[0x27000000, 0x37ffffff]` || HighShadow ||