POSIX: don’t be overly restrictive about mmap() region length

ScopedMmap was asserting that the length of a mapped region must be an
exact number of pages, but this is not required or useful.

Change-Id: I6141712a1980a217565e31ddcd4c23cf6a32503c
Reviewed-on: https://chromium-review.googlesource.com/c/1480440
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Mark Mentovai 2019-02-22 18:08:01 -05:00 committed by Commit Bot
parent b1739cc67f
commit 9d26012e9c
2 changed files with 93 additions and 1 deletions

View File

@ -19,6 +19,7 @@
#include <algorithm>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
namespace {
@ -54,9 +55,12 @@ bool ScopedMmap::ResetAddrLen(void* addr, size_t 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_EQ(len % getpagesize(), 0u);
DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (len - 1)).IsValid());
}

View File

@ -294,6 +294,94 @@ TEST(ScopedMmapDeathTest, ResetMmap) {
EXPECT_DEATH_CRASH(cookie.Check(), "");
}
TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
ScopedMmap mapping;
EXPECT_FALSE(mapping.is_valid());
EXPECT_EQ(mapping.addr(), MAP_FAILED);
EXPECT_EQ(mapping.len(), 0u);
ASSERT_TRUE(mapping.Reset());
EXPECT_FALSE(mapping.is_valid());
// Establishing a half-page mapping actually establishes a single page.
const size_t kPageSize = base::checked_cast<size_t>(getpagesize());
const size_t kHalfPageSize = kPageSize / 2;
ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kHalfPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_NE(mapping.addr(), MAP_FAILED);
EXPECT_EQ(mapping.len(), kPageSize);
TestCookie cookie;
cookie.SetUp(mapping.addr_as<uint64_t*>());
// Shrinking a one-page mapping to a half page is a no-op.
void* orig_addr = mapping.addr();
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(cookie.Observed(), cookie.Expected());
// Same thing shrinking it to a single byte, or one byte less than a whole
// page.
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(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(cookie.Observed(), cookie.Expected());
// Shrinking a two-page mapping to a half page frees the second page but
// leaves the first alone.
ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_NE(mapping.addr(), MAP_FAILED);
EXPECT_EQ(mapping.len(), 2 * kPageSize);
TestCookie two_cookies[2];
for (size_t index = 0; index < base::size(two_cookies); ++index) {
two_cookies[index].SetUp(reinterpret_cast<uint64_t*>(
mapping.addr_as<uintptr_t>() + index * kPageSize));
}
orig_addr = mapping.addr();
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(two_cookies[0].Observed(), two_cookies[0].Expected());
EXPECT_DEATH_CRASH(two_cookies[1].Check(), "");
// Shrinking a two-page mapping to a page and a half is a no-op.
ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));
EXPECT_TRUE(mapping.is_valid());
EXPECT_NE(mapping.addr(), MAP_FAILED);
EXPECT_EQ(mapping.len(), 2 * kPageSize);
for (size_t index = 0; index < base::size(two_cookies); ++index) {
two_cookies[index].SetUp(reinterpret_cast<uint64_t*>(
mapping.addr_as<uintptr_t>() + index * kPageSize));
}
orig_addr = mapping.addr();
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(two_cookies[0].Observed(), two_cookies[0].Expected());
EXPECT_EQ(two_cookies[1].Observed(), two_cookies[1].Expected());
}
TEST(ScopedMmapDeathTest, Mprotect) {
ScopedMmap mapping;