POSIX: ScopedMmap length refinement

9d26012e9c73 relaxed the requirement on ScopedMmap such that the length
of the region supervised no longer needed to be provided as a round
number of pages. This was accomplished by internally rounding up the
provided length to a page length. Unfortunately, this made

ScopedMmap::len() return something other than the passed-in length,
which is undesirable. This change makes ScopedMmap store the passed-in
length internally, making it available unmodified via the accessor, and
rounding it up to page length at internal points of use.

Change-Id: I827925af68e38f33bfa3cee535db0f098884fc6b
Reviewed-on: https://chromium-review.googlesource.com/c/1492774
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Mark Mentovai 2019-02-27 17:40:16 -05:00 committed by Commit Bot
parent 3332ae3546
commit 708367f5ba
3 changed files with 26 additions and 17 deletions

View File

@ -33,6 +33,11 @@ bool Munmap(uintptr_t addr, size_t len) {
return true;
}
size_t RoundPage(size_t size) {
const size_t kPageMask = base::checked_cast<size_t>(getpagesize()) - 1;
return (size + kPageMask) & ~kPageMask;
}
} // namespace
namespace crashpad {
@ -41,7 +46,7 @@ ScopedMmap::ScopedMmap() {}
ScopedMmap::~ScopedMmap() {
if (is_valid()) {
Munmap(reinterpret_cast<uintptr_t>(addr_), len_);
Munmap(reinterpret_cast<uintptr_t>(addr_), RoundPage(len_));
}
}
@ -51,29 +56,28 @@ bool ScopedMmap::Reset() {
bool ScopedMmap::ResetAddrLen(void* addr, size_t len) {
const uintptr_t new_addr = reinterpret_cast<uintptr_t>(addr);
const size_t new_len_round = RoundPage(len);
if (addr == MAP_FAILED) {
DCHECK_EQ(len, 0u);
} else {
// Round |len| up to the next page.
const size_t kPageMask = base::checked_cast<size_t>(getpagesize()) - 1;
len = (len + kPageMask) & ~kPageMask;
DCHECK_NE(len, 0u);
DCHECK_EQ(new_addr % getpagesize(), 0u);
DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (len - 1)).IsValid());
DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (new_len_round - 1))
.IsValid());
}
bool result = true;
if (is_valid()) {
const uintptr_t old_addr = reinterpret_cast<uintptr_t>(addr_);
const size_t old_len_round = RoundPage(len_);
if (old_addr < new_addr) {
result &= Munmap(old_addr, std::min(len_, new_addr - old_addr));
result &= Munmap(old_addr, std::min(old_len_round, new_addr - old_addr));
}
if (old_addr + len_ > new_addr + len) {
uintptr_t unmap_start = std::max(old_addr, new_addr + len);
result &= Munmap(unmap_start, old_addr + len_ - unmap_start);
if (old_addr + old_len_round > new_addr + new_len_round) {
uintptr_t unmap_start = std::max(old_addr, new_addr + new_len_round);
result &= Munmap(unmap_start, old_addr + old_len_round - unmap_start);
}
}
@ -108,7 +112,7 @@ bool ScopedMmap::ResetMmap(void* addr,
}
bool ScopedMmap::Mprotect(int prot) {
if (mprotect(addr_, len_, prot) < 0) {
if (mprotect(addr_, RoundPage(len_), prot) < 0) {
PLOG(ERROR) << "mprotect";
return false;
}

View File

@ -91,6 +91,11 @@ class ScopedMmap {
}
//! \brief Returns the size of the memory-mapped region.
//!
//! This is the value originally passed to ResetAddrLen() or ResetMmap(), or
//! after Reset(), `0`. It may not be a round number of pages. Providing the
//! passed-in value is intended to ease tracking the intended lengths of
//! memory-mapped regions backed by files whose sizes are not whole pages.
size_t len() const { return len_; }
private:

View File

@ -309,7 +309,7 @@ TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kHalfPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_NE(mapping.addr(), MAP_FAILED);
EXPECT_EQ(mapping.len(), kPageSize);
EXPECT_EQ(mapping.len(), kHalfPageSize);
TestCookie cookie;
cookie.SetUp(mapping.addr_as<uint64_t*>());
@ -319,7 +319,7 @@ TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kHalfPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_EQ(mapping.addr(), orig_addr);
EXPECT_EQ(mapping.len(), kPageSize);
EXPECT_EQ(mapping.len(), kHalfPageSize);
EXPECT_EQ(cookie.Observed(), cookie.Expected());
@ -328,14 +328,14 @@ TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, 1));
EXPECT_TRUE(mapping.is_valid());
EXPECT_EQ(mapping.addr(), orig_addr);
EXPECT_EQ(mapping.len(), kPageSize);
EXPECT_EQ(mapping.len(), 1u);
EXPECT_EQ(cookie.Observed(), cookie.Expected());
ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kPageSize - 1));
EXPECT_TRUE(mapping.is_valid());
EXPECT_EQ(mapping.addr(), orig_addr);
EXPECT_EQ(mapping.len(), kPageSize);
EXPECT_EQ(mapping.len(), kPageSize - 1);
EXPECT_EQ(cookie.Observed(), cookie.Expected());
@ -356,7 +356,7 @@ TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kHalfPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_EQ(mapping.addr(), orig_addr);
EXPECT_EQ(mapping.len(), kPageSize);
EXPECT_EQ(mapping.len(), kHalfPageSize);
EXPECT_EQ(two_cookies[0].Observed(), two_cookies[0].Expected());
EXPECT_DEATH_CRASH(two_cookies[1].Check(), "");
@ -376,7 +376,7 @@ TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kPageSize + kHalfPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_EQ(mapping.addr(), orig_addr);
EXPECT_EQ(mapping.len(), 2 * kPageSize);
EXPECT_EQ(mapping.len(), kPageSize + kHalfPageSize);
EXPECT_EQ(two_cookies[0].Observed(), two_cookies[0].Expected());
EXPECT_EQ(two_cookies[1].Observed(), two_cookies[1].Expected());