MapShadow: implement GetExecutableRegionSize
This will be used for accurate guest-exec mappings splitting in
/proc/self/maps emulation.
It can also be used to fix long-standing problem with executability
checks in TranslateRegion.
Flag: EXEMPT NDK
Bug: 382709531
Test: added unit test, tree-hugger
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6c52f9c807e12af004fe661422f318840be2d056)
Merged-In: If2ce6c6073add687e75498c548a9a99f65ef6f7a
Change-Id: If2ce6c6073add687e75498c548a9a99f65ef6f7a
diff --git a/guest_os_primitives/guest_map_shadow.cc b/guest_os_primitives/guest_map_shadow.cc
index f5b4594..4fecbc5 100644
--- a/guest_os_primitives/guest_map_shadow.cc
+++ b/guest_os_primitives/guest_map_shadow.cc
@@ -18,7 +18,9 @@
#include <sys/mman.h>
#include <climits> // CHAR_BIT
+#include <cstddef>
#include <mutex>
+#include <tuple>
#include "berberis/base/bit_util.h"
#include "berberis/base/forever_alloc.h"
@@ -124,17 +126,24 @@
MunmapOrDie(shadow_, kShadowSize);
}
-BitValue GuestMapShadow::GetExecutable(GuestAddr start, size_t size) const {
+std::tuple<bool, size_t> GuestMapShadow::GetExecutableRegionSize(GuestAddr start,
+ size_t scan_size) const {
GuestAddr pc = AlignDownGuestPageSize(start);
- GuestAddr end = AlignUpGuestPageSize(start + size);
+ GuestAddr scan_end_pc = AlignUpGuestPageSize(start + scan_size);
bool is_exec = IsExecAddr(pc);
- pc += kGuestPageSize;
- while (pc < end) {
+ for (pc += kGuestPageSize; pc < scan_end_pc; pc += kGuestPageSize) {
if (is_exec != IsExecAddr(pc)) {
- return kBitMixed;
+ break;
}
- pc += kGuestPageSize;
+ }
+ return {is_exec, pc - start};
+}
+
+BitValue GuestMapShadow::GetExecutable(GuestAddr start, size_t scan_size) const {
+ auto [is_exec, region_size] = GetExecutableRegionSize(start, scan_size);
+ if (region_size < scan_size) {
+ return kBitMixed;
}
return is_exec ? kBitSet : kBitUnset;
}
diff --git a/guest_os_primitives/guest_map_shadow_test.cc b/guest_os_primitives/guest_map_shadow_test.cc
index 5475d71..a4a6798 100644
--- a/guest_os_primitives/guest_map_shadow_test.cc
+++ b/guest_os_primitives/guest_map_shadow_test.cc
@@ -32,12 +32,21 @@
::testing::Test::SetUp();
InitLargeMmap();
}
+
+ template <bool kExpectedExec, size_t kExpectedSize>
+ void ExpectExecRegionSize(GuestAddr start, size_t test_size) {
+ auto [is_exec, size] = shadow_.GetExecutableRegionSize(start, test_size);
+ EXPECT_EQ(is_exec, kExpectedExec);
+ EXPECT_EQ(size, kExpectedSize);
+ }
+
+ GuestMapShadow shadow_;
};
constexpr GuestAddr kGuestAddr{0x7f018000};
constexpr size_t kGuestRegionSize{0x00020000};
-TEST_F(GuestMapShadowTest, smoke) {
+TEST_F(GuestMapShadowTest, Basic) {
auto shadow = std::make_unique<GuestMapShadow>();
ASSERT_EQ(kBitUnset, shadow->GetExecutable(kGuestAddr, kGuestRegionSize));
@@ -65,7 +74,7 @@
ASSERT_TRUE(!shadow->IsExecutable(kGuestAddr, kGuestRegionSize));
}
-TEST_F(GuestMapShadowTest, remap) {
+TEST_F(GuestMapShadowTest, Remap) {
constexpr GuestAddr kRemapAddr = 0x00107000;
constexpr size_t kRemapRegionSize1 = kGuestRegionSize / 2;
constexpr size_t kRemapRegionSize2 = kGuestRegionSize * 2;
@@ -157,6 +166,21 @@
#endif
+TEST_F(GuestMapShadowTest, GetExecutableRegionSize) {
+ shadow_.SetExecutable(kGuestAddr, kGuestRegionSize);
+
+ ExpectExecRegionSize<false, kGuestRegionSize>(kGuestAddr - kGuestRegionSize, kGuestRegionSize);
+ ExpectExecRegionSize<true, kGuestRegionSize>(kGuestAddr, kGuestRegionSize);
+ ExpectExecRegionSize<false, kGuestRegionSize>(kGuestAddr + kGuestRegionSize, kGuestRegionSize);
+
+ // Cases where region size is shorter than the tested size.
+ ExpectExecRegionSize<false, kGuestRegionSize / 2>(kGuestAddr - kGuestRegionSize / 2,
+ kGuestRegionSize);
+ ExpectExecRegionSize<true, kGuestRegionSize / 2>(kGuestAddr + kGuestRegionSize / 2,
+ kGuestRegionSize);
+ ExpectExecRegionSize<true, kGuestRegionSize>(kGuestAddr, kGuestRegionSize * 2);
+}
+
} // namespace
} // namespace berberis
diff --git a/guest_os_primitives/include/berberis/guest_os_primitives/guest_map_shadow.h b/guest_os_primitives/include/berberis/guest_os_primitives/guest_map_shadow.h
index 5cf3009..e9774d7 100644
--- a/guest_os_primitives/include/berberis/guest_os_primitives/guest_map_shadow.h
+++ b/guest_os_primitives/include/berberis/guest_os_primitives/guest_map_shadow.h
@@ -38,6 +38,8 @@
GuestMapShadow();
~GuestMapShadow();
+ [[nodiscard]] std::tuple<bool, size_t> GetExecutableRegionSize(GuestAddr start,
+ size_t max_size) const;
[[nodiscard]] BitValue GetExecutable(GuestAddr start, size_t size) const;
// Check if region start..start+size is fully executable.