diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp @@ -266,15 +266,46 @@ #endif // SANITIZER_GO +namespace { +struct ScopedPipe { + ScopedPipe(const ScopedPipe &) = delete; + ScopedPipe(const ScopedPipe &&) = delete; + ScopedPipe() = default; + ~ScopedPipe() { + if (pair_[0] != -1) + internal_close(pair_[0]); + if (pair_[1] != -1) + internal_close(pair_[1]); + UnmapOrDie(buffer_, bufferSize()); + } + bool initialize() { + if (pair_[0] == -1 && pipe(pair_) == -1) + return false; + if (!buffer_) + buffer_ = (char *)MmapOrDie(bufferSize(), __func__); + return true; + } + int get(size_t i) const { return pair_[i]; } + char *buffer() const { return buffer_; } + + static size_t bufferSize() { return GetPageSizeCached() * 10; } + + private: + char *buffer_ = nullptr; + int pair_[2] = {-1, -1}; +}; + +thread_local ScopedPipe memory_range_pipe; +} // namespace + 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; + CHECK_LT(size, ScopedPipe::bufferSize()); + // If pipe is not available, ignore access check to avoid false-positive. + if (!memory_range_pipe.initialize()) + return true; uptr bytes_written = - internal_write(sock_pair[1], reinterpret_cast(beg), size); + internal_write(memory_range_pipe.get(1), reinterpret_cast(beg), size); int write_errno; bool result; if (internal_iserror(bytes_written, &write_errno)) { @@ -282,9 +313,8 @@ result = false; } else { result = (bytes_written == size); + internal_read(memory_range_pipe.get(1), memory_range_pipe.buffer(), bytes_written); } - internal_close(sock_pair[0]); - internal_close(sock_pair[1]); return result; } diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp @@ -13,11 +13,13 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_POSIX -#include "sanitizer_common/sanitizer_common.h" -#include "gtest/gtest.h" +# include +# include +# include +# include -#include -#include +# include "gtest/gtest.h" +# include "sanitizer_common/sanitizer_common.h" namespace __sanitizer { @@ -78,6 +80,40 @@ EXPECT_TRUE(IsAccessibleMemoryRange(mem + 2 * page_size, page_size)); EXPECT_FALSE(IsAccessibleMemoryRange(mem, 3 * page_size)); EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2)); + + // No stuck even if pipe capaciity exceeds. + for (size_t i = 0; i < 32 << 10; ++i) EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2)); +} + +TEST(SanitizerCommon, IsAccessibleMemoryRange_NOFILE) { + 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_FALSE(IsAccessibleMemoryRange(mem, page_size + 1)); + + // Use ASSERT_DEATH to change RLIMIT_NOFILE in child + ASSERT_DEATH( + { + int fd = open("/dev/null", O_WRONLY); + close(fd); + + rlimit rlim{}; + rlim.rlim_cur = rlim.rlim_max = fd; + int rc = setrlimit(RLIMIT_NOFILE, &rlim); + ASSERT_EQ(rc, 0); + + fd = open("/dev/null", O_WRONLY); + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EMFILE); + + ASSERT_TRUE(IsAccessibleMemoryRange(mem, page_size - 1)); + ASSERT_FALSE(IsAccessibleMemoryRange(mem, page_size + 1)); + + _Exit(1); + }, + ".*" /* failed ASSERT will raise "illegal return in test statement." anyway */); } } // namespace __sanitizer