Skip to content

Commit

Permalink
[sanitizer] Add TryMemCpy (#112668)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vitalybuka authored Oct 17, 2024
1 parent 71b81e9 commit 46df20a
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 4 deletions.
6 changes: 6 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}

Expand Down
44 changes: 44 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
49 changes: 45 additions & 4 deletions compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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

0 comments on commit 46df20a

Please sign in to comment.