Skip to content

Commit 46df20a

Browse files
authored
[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.
1 parent 71b81e9 commit 46df20a

File tree

5 files changed

+105
-4
lines changed

5 files changed

+105
-4
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_common.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,13 @@ class ScopedErrorReportLock {
268268
extern uptr stoptheworld_tracer_pid;
269269
extern uptr stoptheworld_tracer_ppid;
270270

271+
// Returns true if the entire range can be read.
271272
bool IsAccessibleMemoryRange(uptr beg, uptr size);
273+
// Attempts to copy `n` bytes from memory range starting at `src` to `dest`.
274+
// Returns true if the entire range can be read. Returns `false` if any part of
275+
// the source range cannot be read, in which case the contents of `dest` are
276+
// undefined.
277+
bool TryMemCpy(void *dest, const void *src, uptr n);
272278

273279
// Error report formatting.
274280
const char *StripPathPrefix(const char *filepath,

compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
444444
return status == ZX_OK;
445445
}
446446

447+
bool TryMemCpy(void *dest, const void *src, uptr n) {
448+
// TODO: implement.
449+
return false;
450+
}
451+
447452
// FIXME implement on this platform.
448453
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
449454

compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,50 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
326326
return true;
327327
}
328328

329+
bool TryMemCpy(void *dest, const void *src, uptr n) {
330+
if (!n)
331+
return true;
332+
int fds[2];
333+
CHECK_EQ(0, pipe(fds));
334+
335+
auto cleanup = at_scope_exit([&]() {
336+
internal_close(fds[0]);
337+
internal_close(fds[1]);
338+
});
339+
340+
SetNonBlock(fds[0]);
341+
SetNonBlock(fds[1]);
342+
343+
char *d = static_cast<char *>(dest);
344+
const char *s = static_cast<const char *>(src);
345+
346+
while (n) {
347+
int e;
348+
uptr w = internal_write(fds[1], s, n);
349+
if (internal_iserror(w, &e)) {
350+
if (e == EINTR)
351+
continue;
352+
CHECK_EQ(EFAULT, e);
353+
return false;
354+
}
355+
s += w;
356+
n -= w;
357+
358+
while (w) {
359+
uptr r = internal_read(fds[0], d, w);
360+
if (internal_iserror(r, &e)) {
361+
CHECK_EQ(EINTR, e);
362+
continue;
363+
}
364+
365+
d += r;
366+
w -= r;
367+
}
368+
}
369+
370+
return true;
371+
}
372+
329373
void PlatformPrepareForSandboxing(void *args) {
330374
// Some kinds of sandboxes may forbid filesystem access, so we won't be able
331375
// to read the file mappings from /proc/self/maps. Luckily, neither the

compiler-rt/lib/sanitizer_common/sanitizer_win.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
968968
return true;
969969
}
970970

971+
bool TryMemCpy(void *dest, const void *src, uptr n) {
972+
// TODO: implement.
973+
return false;
974+
}
975+
971976
bool SignalContext::IsStackOverflow() const {
972977
return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW;
973978
}

compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cpp

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
#include "sanitizer_common/sanitizer_platform.h"
1414
#if SANITIZER_POSIX
1515

16-
#include "sanitizer_common/sanitizer_common.h"
17-
#include "gtest/gtest.h"
16+
# include <pthread.h>
17+
# include <sys/mman.h>
1818

19-
#include <pthread.h>
20-
#include <sys/mman.h>
19+
# include <algorithm>
20+
# include <numeric>
21+
22+
# include "gtest/gtest.h"
23+
# include "sanitizer_common/sanitizer_common.h"
2124

2225
namespace __sanitizer {
2326

@@ -86,6 +89,44 @@ TEST(SanitizerCommon, IsAccessibleMemoryRangeLarge) {
8689
buffer.size()));
8790
}
8891

92+
TEST(SanitizerCommon, TryMemCpy) {
93+
std::vector<char> src(10000000);
94+
std::iota(src.begin(), src.end(), 123);
95+
std::vector<char> dst;
96+
97+
// Don't use ::testing::ElementsAreArray or similar, as the huge output on an
98+
// error is not helpful.
99+
100+
dst.assign(1, 0);
101+
EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
102+
EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
103+
104+
dst.assign(100, 0);
105+
EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
106+
EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
107+
108+
dst.assign(534, 0);
109+
EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
110+
EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
111+
112+
dst.assign(GetPageSize(), 0);
113+
EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
114+
EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
115+
116+
dst.assign(src.size(), 0);
117+
EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
118+
EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
119+
120+
dst.assign(src.size() - 1, 0);
121+
EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size()));
122+
EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin()));
123+
}
124+
125+
TEST(SanitizerCommon, TryMemCpyNull) {
126+
std::vector<char> dst(100);
127+
EXPECT_FALSE(TryMemCpy(dst.data(), nullptr, dst.size()));
128+
}
129+
89130
} // namespace __sanitizer
90131

91132
#endif // SANITIZER_POSIX

0 commit comments

Comments
 (0)