PR20721: Don't let UBSan print inaccessible memory

Summary:
UBSan needs to check if memory snippet it's going to print resides
in addressable memory. Similar check might be helpful in ASan with
dump_instruction_bytes option (see http://reviews.llvm.org/D5167).

Instead of scanning /proc/self/maps manually, delegate this check to
the OS kernel: try to write this memory in a syscall and assume that
memory is inaccessible if the syscall failed (e.g. with EFAULT).

Fixes PR20721.

Test Plan: compiler-rt test suite

Reviewers: eugenis, glider

Reviewed By: glider

Subscribers: emaste, ygribov, llvm-commits, glider, rsmith

Differential Revision: http://reviews.llvm.org/D5253

llvm-svn: 217971
This commit is contained in:
Alexey Samsonov
2014-09-17 17:56:15 +00:00
parent d122211e60
commit 1947bf9921
6 changed files with 63 additions and 1 deletions

View File

@@ -167,6 +167,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
void *MapFileToMemory(const char *file_name, uptr *buff_size);
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
bool IsAccessibleMemoryRange(uptr beg, uptr size);
// Error report formatting.
const char *StripPathPrefix(const char *filepath,
const char *strip_file_prefix);

View File

@@ -166,6 +166,28 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
}
#endif // SANITIZER_GO
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
uptr page_size = GetPageSizeCached();
// Checking too large memory ranges is slow.
CHECK_LT(size, page_size * 10);
int sock_pair[2];
if (pipe(sock_pair))
return false;
uptr bytes_written =
internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
int write_errno;
bool result;
if (internal_iserror(bytes_written, &write_errno)) {
CHECK_EQ(EFAULT, write_errno);
result = false;
} else {
result = (bytes_written == size);
}
internal_close(sock_pair[0]);
internal_close(sock_pair[1]);
return result;
}
} // namespace __sanitizer
#endif // SANITIZER_POSIX

View File

@@ -522,6 +522,11 @@ bool IsDeadlySignal(int signum) {
return false;
}
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
// FIXME: Actually implement this function.
return true;
}
} // namespace __sanitizer
#endif // _WIN32

View File

@@ -18,6 +18,7 @@
#include "gtest/gtest.h"
#include <pthread.h>
#include <sys/mman.h>
namespace __sanitizer {
@@ -57,6 +58,23 @@ TEST(SanitizerCommon, PthreadDestructorIterations) {
EXPECT_FALSE(destructor_executed);
}
TEST(SanitizerCommon, IsAccessibleMemoryRange) {
const int page_size = GetPageSize();
uptr mem = (uptr)mmap(0, 3 * page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
// Protect the middle page.
mprotect((void *)(mem + page_size), page_size, PROT_NONE);
EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size - 1));
EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size));
EXPECT_FALSE(IsAccessibleMemoryRange(mem, page_size + 1));
EXPECT_TRUE(IsAccessibleMemoryRange(mem + page_size - 1, 1));
EXPECT_FALSE(IsAccessibleMemoryRange(mem + page_size - 1, 2));
EXPECT_FALSE(IsAccessibleMemoryRange(mem + 2 * page_size - 1, 1));
EXPECT_TRUE(IsAccessibleMemoryRange(mem + 2 * page_size, page_size));
EXPECT_FALSE(IsAccessibleMemoryRange(mem, 3 * page_size));
EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2));
}
} // namespace __sanitizer
#endif // SANITIZER_POSIX

View File

@@ -222,9 +222,13 @@ static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
Max = addNoOverflow(Min, BytesToShow);
if (!IsAccessibleMemoryRange(Min, Max - Min)) {
Printf("<memory cannot be printed>\n");
return;
}
// Emit data.
for (uptr P = Min; P != Max; ++P) {
// FIXME: Check that the address is readable before printing it.
unsigned char C = *reinterpret_cast<const unsigned char*>(P);
Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
}

View File

@@ -8,6 +8,9 @@
// RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD
// RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover %s -O3 -o %t
// RUN: not %run %t w1 2>&1 | FileCheck %s --check-prefix=CHECK-WILD
#include <new>
struct S {
@@ -23,6 +26,8 @@ int main(int, char **argv) {
int *p = (int*)&c[4 + argv[1][1] - '0'];
S *s = (S*)p;
void *wild = reinterpret_cast<void *>(0x123L);
(void)*p; // ok!
switch (argv[1][0]) {
@@ -74,5 +79,11 @@ int main(int, char **argv) {
// CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}}
// CHECK-NEW-NEXT: {{^ \^}}
return (new (s) S)->k && 0;
case 'w':
// CHECK-WILD: misaligned.cpp:[[@LINE+3]]:35: runtime error: member access within misaligned address 0x000000000123 for type 'S', which requires 4 byte alignment
// CHECK-WILD-NEXT: 0x000000000123: note: pointer points here
// CHECK-WILD-NEXT: <memory cannot be printed>
return static_cast<S*>(wild)->k;
}
}