summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulian Lettner <jlettner@apple.com>2019-10-10 17:19:58 +0000
committerJulian Lettner <jlettner@apple.com>2019-10-10 17:19:58 +0000
commit99c9d7bd6369a7505b86d7ea75a573265436e34a (patch)
tree42bc06a07f8bfe46307e337abe8cc6ea622a9b30
parentdc895a325f8df7fd10663e7cbeaaa783b2a37aa6 (diff)
downloadbcm5719-llvm-99c9d7bd6369a7505b86d7ea75a573265436e34a.tar.gz
bcm5719-llvm-99c9d7bd6369a7505b86d7ea75a573265436e34a.zip
Reland "[ASan] Do not misrepresent high value address dereferences as null dereferences"
Updated: Removed offending TODO comment. Dereferences with addresses above the 48-bit hardware addressable range produce "invalid instruction" (instead of "invalid access") hardware exceptions (there is no hardware address decoding logic for those bits), and the address provided by this exception is the address of the instruction (not the faulting address). The kernel maps the "invalid instruction" to SEGV, but fails to provide the real fault address. Because of this ASan lies and says that those cases are null dereferences. This downgrades the severity of a found bug in terms of security. In the ASan signal handler, we can not provide the real faulting address, but at least we can try not to lie. rdar://50366151 Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D68676 > llvm-svn: 374265 llvm-svn: 374384
-rw-r--r--compiler-rt/lib/asan/asan_errors.h3
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_common.h9
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp6
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp6
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp17
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_win.cpp5
-rw-r--r--compiler-rt/test/asan/TestCases/Posix/high-address-dereference.c50
7 files changed, 90 insertions, 6 deletions
diff --git a/compiler-rt/lib/asan/asan_errors.h b/compiler-rt/lib/asan/asan_errors.h
index b84f56c1853..a7fda2fd9f5 100644
--- a/compiler-rt/lib/asan/asan_errors.h
+++ b/compiler-rt/lib/asan/asan_errors.h
@@ -48,7 +48,8 @@ struct ErrorDeadlySignal : ErrorBase {
scariness.Scare(10, "stack-overflow");
} else if (!signal.is_memory_access) {
scariness.Scare(10, "signal");
- } else if (signal.addr < GetPageSizeCached()) {
+ } else if (signal.is_true_faulting_addr &&
+ signal.addr < GetPageSizeCached()) {
scariness.Scare(10, "null-deref");
} else if (signal.addr == signal.pc) {
scariness.Scare(60, "wild-jump");
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index ad056df387d..87b8f02b5b7 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -881,6 +881,11 @@ struct SignalContext {
bool is_memory_access;
enum WriteFlag { UNKNOWN, READ, WRITE } write_flag;
+ // In some cases the kernel cannot provide the true faulting address; `addr`
+ // will be zero then. This field allows to distinguish between these cases
+ // and dereferences of null.
+ bool is_true_faulting_addr;
+
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
SignalContext() = default;
@@ -893,7 +898,8 @@ struct SignalContext {
context(context),
addr(GetAddress()),
is_memory_access(IsMemoryAccess()),
- write_flag(GetWriteFlag()) {
+ write_flag(GetWriteFlag()),
+ is_true_faulting_addr(IsTrueFaultingAddress()) {
InitPcSpBp();
}
@@ -914,6 +920,7 @@ struct SignalContext {
uptr GetAddress() const;
WriteFlag GetWriteFlag() const;
bool IsMemoryAccess() const;
+ bool IsTrueFaultingAddress() const;
};
void InitializePlatformEarly();
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index d23009075c6..0b53da6c349 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -1849,6 +1849,12 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
#endif
}
+bool SignalContext::IsTrueFaultingAddress() const {
+ auto si = static_cast<const siginfo_t *>(siginfo);
+ // SIGSEGV signals without a true fault address have si_code set to 128.
+ return si->si_signo == SIGSEGV && si->si_code != 128;
+}
+
void SignalContext::DumpAllRegisters(void *context) {
// FIXME: Implement this.
}
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
index 8eb1dfbdea6..ea4bd02aa92 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
@@ -754,6 +754,12 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
#endif
}
+bool SignalContext::IsTrueFaultingAddress() const {
+ auto si = static_cast<const siginfo_t *>(siginfo);
+ // "Real" SIGSEGV codes (e.g., SEGV_MAPERR, SEGV_MAPERR) are non-zero.
+ return si->si_signo == SIGSEGV && si->si_code != 0;
+}
+
static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
ucontext_t *ucontext = (ucontext_t*)context;
# if defined(__aarch64__)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp
index d6699f3ed6f..fe9ea1a8200 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp
@@ -191,9 +191,14 @@ static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
SanitizerCommonDecorator d;
Printf("%s", d.Warning());
const char *description = sig.Describe();
- Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n",
- SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc,
- (void *)sig.bp, (void *)sig.sp, tid);
+ if (sig.is_memory_access && !sig.is_true_faulting_addr)
+ Report("ERROR: %s: %s on unknown address (pc %p bp %p sp %p T%d)\n",
+ SanitizerToolName, description, (void *)sig.pc, (void *)sig.bp,
+ (void *)sig.sp, tid);
+ else
+ Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n",
+ SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc,
+ (void *)sig.bp, (void *)sig.sp, tid);
Printf("%s", d.Default());
if (sig.pc < GetPageSizeCached())
Report("Hint: pc points to the zero page.\n");
@@ -203,7 +208,11 @@ static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
? "WRITE"
: (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
Report("The signal is caused by a %s memory access.\n", access_type);
- if (sig.addr < GetPageSizeCached())
+ if (!sig.is_true_faulting_addr)
+ Report("Hint: this fault was caused by a dereference of a high value "
+ "address (see registers below). Dissassemble the provided pc "
+ "to learn which register value was used.\n");
+ else if (sig.addr < GetPageSizeCached())
Report("Hint: address points to the zero page.\n");
}
MaybeReportNonExecRegion(sig.pc);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
index ce2a4314ab9..36dde49d870 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
@@ -945,6 +945,11 @@ bool SignalContext::IsMemoryAccess() const {
return GetWriteFlag() != SignalContext::UNKNOWN;
}
+bool SignalContext::IsTrueFaultingAddress() const {
+ // FIXME: Provide real implementation for this. See Linux and Mac variants.
+ return IsMemoryAccess();
+}
+
SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
// The contents of this array are documented at
diff --git a/compiler-rt/test/asan/TestCases/Posix/high-address-dereference.c b/compiler-rt/test/asan/TestCases/Posix/high-address-dereference.c
new file mode 100644
index 00000000000..78503302891
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Posix/high-address-dereference.c
@@ -0,0 +1,50 @@
+// On x86_64, the kernel does not provide the faulting address for dereferences
+// of addresses greater than the 48-bit hardware addressable range, i.e.,
+// `siginfo.si_addr` is zero in ASan's SEGV signal handler. This test checks
+// that ASan does not misrepresent such cases as "NULL dereferences".
+
+// REQUIRES: x86_64-target-arch
+// RUN: %clang_asan %s -o %t
+// RUN: export %env_asan_opts=print_scariness=1
+// RUN: not %run %t 0x0000000000000000 2>&1 | FileCheck %s --check-prefixes=ZERO,HINT-PAGE0
+// RUN: not %run %t 0x0000000000000FFF 2>&1 | FileCheck %s --check-prefixes=LOW1,HINT-PAGE0
+// RUN: not %run %t 0x0000000000001000 2>&1 | FileCheck %s --check-prefixes=LOW2,HINT-NONE
+// RUN: not %run %t 0x4141414141414141 2>&1 | FileCheck %s --check-prefixes=HIGH,HINT-HIGHADDR
+// RUN: not %run %t 0xFFFFFFFFFFFFFFFF 2>&1 | FileCheck %s --check-prefixes=MAX,HINT-HIGHADDR
+
+#include <stdint.h>
+#include <stdlib.h>
+
+int main(int argc, const char *argv[]) {
+ const char *hex = argv[1];
+ uint64_t *addr = (uint64_t *)strtoull(hex, NULL, 16);
+ uint64_t x = *addr; // segmentation fault
+ return x;
+}
+
+// ZERO: SEGV on unknown address 0x000000000000 (pc
+// LOW1: SEGV on unknown address 0x000000000fff (pc
+// LOW2: SEGV on unknown address 0x000000001000 (pc
+// HIGH: SEGV on unknown address (pc
+// MAX: SEGV on unknown address (pc
+
+// HINT-PAGE0-NOT: Hint: this fault was caused by a dereference of a high value address
+// HINT-PAGE0: Hint: address points to the zero page.
+
+// HINT-NONE-NOT: Hint: this fault was caused by a dereference of a high value address
+// HINT-NONE-NOT: Hint: address points to the zero page.
+
+// HINT-HIGHADDR: Hint: this fault was caused by a dereference of a high value address
+// HINT-HIGHADDR-NOT: Hint: address points to the zero page.
+
+// ZERO: SCARINESS: 10 (null-deref)
+// LOW1: SCARINESS: 10 (null-deref)
+// LOW2: SCARINESS: 20 (wild-addr-read)
+// HIGH: SCARINESS: 20 (wild-addr-read)
+// MAX: SCARINESS: 20 (wild-addr-read)
+
+// TODO: Currently, register values are only printed on Mac. Once this changes,
+// remove the 'TODO_' prefix in the following lines.
+// TODO_HIGH,TODO_MAX: Register values:
+// TODO_HIGH: = 0x4141414141414141
+// TODO_MAX: = 0xffffffffffffffff
OpenPOWER on IntegriCloud