[sanitizer] Implement MprotectReadOnly and MprotectNoAccess

MprotectReadOnly for Win and Fuchsia
MprotectNoAccess for Fuchsia

GitOrigin-RevId: e599aa80c085559a5449bbd61a96da63d481aa6c
diff --git a/lib/sanitizer_common/sanitizer_fuchsia.cpp b/lib/sanitizer_common/sanitizer_fuchsia.cpp
index c7b30d9..9b5f6f1 100644
--- a/lib/sanitizer_common/sanitizer_fuchsia.cpp
+++ b/lib/sanitizer_common/sanitizer_fuchsia.cpp
@@ -274,6 +274,15 @@
   UNIMPLEMENTED();
 }
 
+bool MprotectNoAccess(uptr addr, uptr size) {
+  return _zx_vmar_protect(_zx_vmar_root_self(), 0, Addr, Size) == ZX_OK;
+}
+
+bool MprotectReadOnly(uptr addr, uptr size) {
+  return _zx_vmar_protect(_zx_vmar_root_self(), ZX_VM_PERM_READ, Addr, Size) ==
+         ZX_OK;
+}
+
 void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
                                    const char *mem_type) {
   CHECK_GE(size, GetPageSize());
diff --git a/lib/sanitizer_common/sanitizer_win.cpp b/lib/sanitizer_common/sanitizer_win.cpp
index c3607db..1a31ce0 100644
--- a/lib/sanitizer_common/sanitizer_win.cpp
+++ b/lib/sanitizer_common/sanitizer_win.cpp
@@ -337,6 +337,11 @@
   return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
 }
 
+bool MprotectReadOnly(uptr addr, uptr size) {
+  DWORD old_protection;
+  return VirtualProtect((LPVOID)addr, size, PAGE_READONLY, &old_protection);
+}
+
 void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
   uptr beg_aligned = RoundDownTo(beg, GetPageSizeCached()),
        end_aligned = RoundDownTo(end, GetPageSizeCached());
diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cpp b/lib/sanitizer_common/tests/sanitizer_common_test.cpp
index 9008a65..c76644f 100644
--- a/lib/sanitizer_common/tests/sanitizer_common_test.cpp
+++ b/lib/sanitizer_common/tests/sanitizer_common_test.cpp
@@ -87,6 +87,24 @@
   }
 }
 
+TEST(SanitizerCommon, Mprotect) {
+  uptr PageSize = GetPageSizeCached();
+  u8 *mem = reinterpret_cast<u8 *>(MmapOrDie(PageSize, "MprotectTest"));
+  for (u8 *p = mem; p < mem + PageSize; ++p) ++(*p);
+
+  MprotectReadOnly(reinterpret_cast<uptr>(mem), PageSize);
+  for (u8 *p = mem; p < mem + PageSize; ++p) EXPECT_EQ(1u, *p);
+  EXPECT_DEATH(++mem[0], "");
+  EXPECT_DEATH(++mem[PageSize / 2], "");
+  EXPECT_DEATH(++mem[PageSize - 1], "");
+
+  MprotectNoAccess(reinterpret_cast<uptr>(mem), PageSize);
+  volatile u8 t;
+  EXPECT_DEATH(t = mem[0], "");
+  EXPECT_DEATH(t = mem[PageSize / 2], "");
+  EXPECT_DEATH(t = mem[PageSize - 1], "");
+}
+
 TEST(SanitizerCommon, InternalMmapVectorRoundUpCapacity) {
   InternalMmapVector<uptr> v;
   v.reserve(1);