[compiler-rt] 46df20a - [sanitizer] Add TryMemCpy (#112668)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 17 16:59:52 PDT 2024


Author: Vitaly Buka
Date: 2024-10-17T16:59:49-07:00
New Revision: 46df20ab63ee8c14c5d4eef07e2a7cccd466c064

URL: https://github.com/llvm/llvm-project/commit/46df20ab63ee8c14c5d4eef07e2a7cccd466c064
DIFF: https://github.com/llvm/llvm-project/commit/46df20ab63ee8c14c5d4eef07e2a7cccd466c064.diff

LOG: [sanitizer] Add TryMemCpy (#112668)

For posix implementation is similar to
`IsAccessibleMemoryRange`, using `pipe`.

We need this because we can't rely on non-atomic
`IsAccessibleMemoryRange` + `memcpy`, as the
protection or mapping may change and we may
crash.

Added: 
    

Modified: 
    compiler-rt/lib/sanitizer_common/sanitizer_common.h
    compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
    compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index 082d2158e579bd..3a28420ed02d78 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -268,7 +268,13 @@ class ScopedErrorReportLock {
 extern uptr stoptheworld_tracer_pid;
 extern uptr stoptheworld_tracer_ppid;
 
+// Returns true if the entire range can be read.
 bool IsAccessibleMemoryRange(uptr beg, uptr size);
+// Attempts to copy `n` bytes from memory range starting at `src` to `dest`.
+// Returns true if the entire range can be read. Returns `false` if any part of
+// the source range cannot be read, in which case the contents of `dest` are
+// undefined.
+bool TryMemCpy(void *dest, const void *src, uptr n);
 
 // Error report formatting.
 const char *StripPathPrefix(const char *filepath,

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
index 75dcf546729f6e..c2ace46c946587 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
@@ -444,6 +444,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
   return status == ZX_OK;
 }
 
+bool TryMemCpy(void *dest, const void *src, uptr n) {
+  // TODO: implement.
+  return false;
+}
+
 // FIXME implement on this platform.
 void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
 

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
index 3ab83977a4ee23..7ee2319456d23e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
@@ -326,6 +326,50 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
   return true;
 }
 
+bool TryMemCpy(void *dest, const void *src, uptr n) {
+  if (!n)
+    return true;
+  int fds[2];
+  CHECK_EQ(0, pipe(fds));
+
+  auto cleanup = at_scope_exit([&]() {
+    internal_close(fds[0]);
+    internal_close(fds[1]);
+  });
+
+  SetNonBlock(fds[0]);
+  SetNonBlock(fds[1]);
+
+  char *d = static_cast<char *>(dest);
+  const char *s = static_cast<const char *>(src);
+
+  while (n) {
+    int e;
+    uptr w = internal_write(fds[1], s, n);
+    if (internal_iserror(w, &e)) {
+      if (e == EINTR)
+        continue;
+      CHECK_EQ(EFAULT, e);
+      return false;
+    }
+    s += w;
+    n -= w;
+
+    while (w) {
+      uptr r = internal_read(fds[0], d, w);
+      if (internal_iserror(r, &e)) {
+        CHECK_EQ(EINTR, e);
+        continue;
+      }
+
+      d += r;
+      w -= r;
+    }
+  }
+
+  return true;
+}
+
 void PlatformPrepareForSandboxing(void *args) {
   // Some kinds of sandboxes may forbid filesystem access, so we won't be able
   // to read the file mappings from /proc/self/maps. Luckily, neither the

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
index 6fb947aa6d6c26..ea513d5f263fe2 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
@@ -968,6 +968,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
   return true;
 }
 
+bool TryMemCpy(void *dest, const void *src, uptr n) {
+  // TODO: implement.
+  return false;
+}
+
 bool SignalContext::IsStackOverflow() const {
   return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW;
 }

diff  --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp
index 04890f2f5e2af2..658ca60175b3bd 100644
--- 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,14 @@
 #include "sanitizer_common/sanitizer_platform.h"
 #if SANITIZER_POSIX
 
-#include "sanitizer_common/sanitizer_common.h"
-#include "gtest/gtest.h"
+#  include <pthread.h>
+#  include <sys/mman.h>
 
-#include <pthread.h>
-#include <sys/mman.h>
+#  include <algorithm>
+#  include <numeric>
+
+#  include "gtest/gtest.h"
+#  include "sanitizer_common/sanitizer_common.h"
 
 namespace __sanitizer {
 
@@ -86,6 +89,44 @@ TEST(SanitizerCommon, IsAccessibleMemoryRangeLarge) {
                                       buffer.size()));
 }
 
+TEST(SanitizerCommon, TryMemCpy) {
+  std::vector<char> src(10000000);
+  std::iota(src.begin(), src.end(), 123);
+  std::vector<char> dst;
+
+  // Don't use ::testing::ElementsAreArray or similar, as the huge output on an
+  // error is not helpful.
+
+  dst.assign(1, 0);
+  EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
+  EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
+
+  dst.assign(100, 0);
+  EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
+  EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
+
+  dst.assign(534, 0);
+  EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
+  EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
+
+  dst.assign(GetPageSize(), 0);
+  EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
+  EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
+
+  dst.assign(src.size(), 0);
+  EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
+  EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
+
+  dst.assign(src.size() - 1, 0);
+  EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
+  EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
+}
+
+TEST(SanitizerCommon, TryMemCpyNull) {
+  std::vector<char> dst(100);
+  EXPECT_FALSE(TryMemCpy(dst.data(), nullptr, dst.size()));
+}
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_POSIX


        


More information about the llvm-commits mailing list