Merge "libunwindstack: Add Memory::ReadTag() function for reading memory tags." am: b416a925ab
Change-Id: I5a748b08e866cda1fd33c3b0d40b4bf8c9a094c5
This commit is contained in:
commit
3c1f492ee1
10 changed files with 238 additions and 42 deletions
|
@ -57,6 +57,7 @@ cc_defaults {
|
|||
"MapInfo.cpp",
|
||||
"Maps.cpp",
|
||||
"Memory.cpp",
|
||||
"MemoryMte.cpp",
|
||||
"LocalUnwinder.cpp",
|
||||
"Regs.cpp",
|
||||
"RegsArm.cpp",
|
||||
|
@ -101,6 +102,16 @@ cc_defaults {
|
|||
"liblog",
|
||||
"liblzma",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"bionic_libc_platform_headers",
|
||||
],
|
||||
|
||||
product_variables: {
|
||||
experimental_mte: {
|
||||
cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library {
|
||||
|
@ -213,6 +224,7 @@ cc_defaults {
|
|||
"tests/MemoryRangesTest.cpp",
|
||||
"tests/MemoryRemoteTest.cpp",
|
||||
"tests/MemoryTest.cpp",
|
||||
"tests/MemoryMteTest.cpp",
|
||||
"tests/RegsInfoTest.cpp",
|
||||
"tests/RegsIterateTest.cpp",
|
||||
"tests/RegsStepIfSignalHandlerTest.cpp",
|
||||
|
@ -268,6 +280,16 @@ cc_defaults {
|
|||
"tests/files/offline/straddle_arm/*",
|
||||
"tests/files/offline/straddle_arm64/*",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"bionic_libc_platform_headers",
|
||||
],
|
||||
|
||||
product_variables: {
|
||||
experimental_mte: {
|
||||
cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_test {
|
||||
|
|
|
@ -324,6 +324,16 @@ size_t MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
|
|||
return ProcessVmRead(getpid(), addr, dst, size);
|
||||
}
|
||||
|
||||
#if !defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
long MemoryRemote::ReadTag(uint64_t) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
long MemoryLocal::ReadTag(uint64_t) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
|
||||
uint64_t offset)
|
||||
: memory_(memory), begin_(begin), length_(length), offset_(offset) {}
|
||||
|
|
|
@ -33,6 +33,7 @@ class MemoryCache : public Memory {
|
|||
virtual ~MemoryCache() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
|
||||
|
||||
void Clear() override { cache_.clear(); }
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ class MemoryLocal : public Memory {
|
|||
bool IsLocal() const override { return true; }
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
long ReadTag(uint64_t addr) override;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
57
libunwindstack/MemoryMte.cpp
Normal file
57
libunwindstack/MemoryMte.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include <bionic/mte.h>
|
||||
#include <bionic/mte_kernel.h>
|
||||
|
||||
#include "MemoryLocal.h"
|
||||
#include "MemoryRemote.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
long MemoryRemote::ReadTag(uint64_t addr) {
|
||||
#if defined(__aarch64__)
|
||||
return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
|
||||
#else
|
||||
(void)addr;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
long MemoryLocal::ReadTag(uint64_t addr) {
|
||||
#if defined(__aarch64__)
|
||||
// Check that the memory is readable first. This is racy with the ldg but there's not much
|
||||
// we can do about it.
|
||||
char data;
|
||||
if (!mte_supported() || !Read(addr, &data, 1)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
__asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(addr) : : "memory");
|
||||
return (addr >> 56) & 0xf;
|
||||
#else
|
||||
(void)addr;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif
|
|
@ -32,6 +32,7 @@ class MemoryRemote : public Memory {
|
|||
virtual ~MemoryRemote() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
long ReadTag(uint64_t addr) override;
|
||||
|
||||
pid_t pid() { return pid_; }
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ class Memory {
|
|||
virtual bool IsLocal() const { return false; }
|
||||
|
||||
virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
|
||||
virtual long ReadTag(uint64_t) { return -1; }
|
||||
|
||||
bool ReadFully(uint64_t addr, void* dst, size_t size);
|
||||
|
||||
|
|
99
libunwindstack/tests/MemoryMteTest.cpp
Normal file
99
libunwindstack/tests/MemoryMteTest.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <bionic/mte.h>
|
||||
|
||||
#include "MemoryLocal.h"
|
||||
#include "MemoryRemote.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static uintptr_t CreateTagMapping() {
|
||||
uintptr_t mapping =
|
||||
reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
|
||||
return 0;
|
||||
}
|
||||
#if defined(__aarch64__)
|
||||
__asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
|
||||
:
|
||||
: "r"(mapping + (1ULL << 56))
|
||||
: "memory");
|
||||
#endif
|
||||
return mapping;
|
||||
}
|
||||
|
||||
TEST(MemoryMteTest, remote_read_tag) {
|
||||
#if !defined(__aarch64__)
|
||||
GTEST_SKIP() << "Requires aarch64";
|
||||
#else
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "Requires MTE";
|
||||
}
|
||||
#endif
|
||||
|
||||
uintptr_t mapping = CreateTagMapping();
|
||||
ASSERT_NE(0U, mapping);
|
||||
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
while (true)
|
||||
;
|
||||
exit(1);
|
||||
}
|
||||
ASSERT_LT(0, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
EXPECT_EQ(1, remote.ReadTag(mapping));
|
||||
EXPECT_EQ(0, remote.ReadTag(mapping + 16));
|
||||
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST(MemoryMteTest, local_read_tag) {
|
||||
#if !defined(__aarch64__)
|
||||
GTEST_SKIP() << "Requires aarch64";
|
||||
#else
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "Requires MTE";
|
||||
}
|
||||
#endif
|
||||
|
||||
uintptr_t mapping = CreateTagMapping();
|
||||
ASSERT_NE(0U, mapping);
|
||||
|
||||
MemoryLocal local;
|
||||
|
||||
EXPECT_EQ(1, local.ReadTag(mapping));
|
||||
EXPECT_EQ(0, local.ReadTag(mapping + 16));
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif
|
|
@ -26,8 +26,10 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/test_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/test_utils.h>
|
||||
#include <bionic/mte.h>
|
||||
#include <bionic/mte_kernel.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "MemoryRemote.h"
|
||||
|
@ -37,24 +39,7 @@
|
|||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryRemoteTest : public ::testing::Test {
|
||||
protected:
|
||||
static bool Attach(pid_t pid) {
|
||||
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TestQuiescePid(pid);
|
||||
}
|
||||
|
||||
static bool Detach(pid_t pid) {
|
||||
return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
|
||||
}
|
||||
|
||||
static constexpr size_t NS_PER_SEC = 1000000000ULL;
|
||||
};
|
||||
|
||||
TEST_F(MemoryRemoteTest, read) {
|
||||
TEST(MemoryRemoteTest, read) {
|
||||
std::vector<uint8_t> src(1024);
|
||||
memset(src.data(), 0x4c, 1024);
|
||||
|
||||
|
@ -66,7 +51,7 @@ TEST_F(MemoryRemoteTest, read) {
|
|||
ASSERT_LT(0, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
|
@ -76,10 +61,10 @@ TEST_F(MemoryRemoteTest, read) {
|
|||
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_large) {
|
||||
TEST(MemoryRemoteTest, read_large) {
|
||||
static constexpr size_t kTotalPages = 245;
|
||||
std::vector<uint8_t> src(kTotalPages * getpagesize());
|
||||
for (size_t i = 0; i < kTotalPages; i++) {
|
||||
|
@ -95,7 +80,7 @@ TEST_F(MemoryRemoteTest, read_large) {
|
|||
ASSERT_LT(0, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
|
@ -105,10 +90,10 @@ TEST_F(MemoryRemoteTest, read_large) {
|
|||
ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_partial) {
|
||||
TEST(MemoryRemoteTest, read_partial) {
|
||||
char* mapping = static_cast<char*>(
|
||||
mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_NE(MAP_FAILED, mapping);
|
||||
|
@ -128,7 +113,7 @@ TEST_F(MemoryRemoteTest, read_partial) {
|
|||
// Unmap from our process.
|
||||
ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
|
@ -149,10 +134,10 @@ TEST_F(MemoryRemoteTest, read_partial) {
|
|||
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_fail) {
|
||||
TEST(MemoryRemoteTest, read_fail) {
|
||||
int pagesize = getpagesize();
|
||||
void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
|
||||
memset(src, 0x4c, pagesize * 2);
|
||||
|
@ -169,7 +154,7 @@ TEST_F(MemoryRemoteTest, read_fail) {
|
|||
ASSERT_LT(0, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
|
@ -188,10 +173,10 @@ TEST_F(MemoryRemoteTest, read_fail) {
|
|||
|
||||
ASSERT_EQ(0, munmap(src, pagesize));
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_overflow) {
|
||||
TEST(MemoryRemoteTest, read_overflow) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
while (true)
|
||||
|
@ -201,7 +186,7 @@ TEST_F(MemoryRemoteTest, read_overflow) {
|
|||
ASSERT_LT(0, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
|
@ -209,10 +194,10 @@ TEST_F(MemoryRemoteTest, read_overflow) {
|
|||
std::vector<uint8_t> dst(200);
|
||||
ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_illegal) {
|
||||
TEST(MemoryRemoteTest, read_illegal) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
while (true);
|
||||
|
@ -221,7 +206,7 @@ TEST_F(MemoryRemoteTest, read_illegal) {
|
|||
ASSERT_LT(0, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
|
||||
|
@ -229,10 +214,10 @@ TEST_F(MemoryRemoteTest, read_illegal) {
|
|||
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
|
||||
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
|
||||
|
||||
ASSERT_TRUE(Detach(pid));
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_mprotect_hole) {
|
||||
TEST(MemoryRemoteTest, read_mprotect_hole) {
|
||||
size_t page_size = getpagesize();
|
||||
void* mapping =
|
||||
mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
|
@ -250,7 +235,7 @@ TEST_F(MemoryRemoteTest, read_mprotect_hole) {
|
|||
|
||||
ASSERT_EQ(0, munmap(mapping, 3 * page_size));
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
|
||||
|
@ -263,9 +248,11 @@ TEST_F(MemoryRemoteTest, read_mprotect_hole) {
|
|||
for (size_t i = read_size; i < dst.size(); ++i) {
|
||||
ASSERT_EQ(0xCC, dst[i]);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_munmap_hole) {
|
||||
TEST(MemoryRemoteTest, read_munmap_hole) {
|
||||
size_t page_size = getpagesize();
|
||||
void* mapping =
|
||||
mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
|
@ -285,7 +272,7 @@ TEST_F(MemoryRemoteTest, read_munmap_hole) {
|
|||
ASSERT_EQ(0, munmap(mapping, page_size));
|
||||
ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
MemoryRemote remote(pid);
|
||||
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
|
||||
|
@ -297,11 +284,13 @@ TEST_F(MemoryRemoteTest, read_munmap_hole) {
|
|||
for (size_t i = read_size; i < dst.size(); ++i) {
|
||||
ASSERT_EQ(0xCC, dst[i]);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
// Verify that the memory remote object chooses a memory read function
|
||||
// properly. Either process_vm_readv or ptrace.
|
||||
TEST_F(MemoryRemoteTest, read_choose_correctly) {
|
||||
TEST(MemoryRemoteTest, read_choose_correctly) {
|
||||
size_t page_size = getpagesize();
|
||||
void* mapping =
|
||||
mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
|
@ -320,7 +309,7 @@ TEST_F(MemoryRemoteTest, read_choose_correctly) {
|
|||
|
||||
ASSERT_EQ(0, munmap(mapping, 2 * page_size));
|
||||
|
||||
ASSERT_TRUE(Attach(pid));
|
||||
ASSERT_TRUE(TestAttach(pid));
|
||||
|
||||
// We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
|
||||
// Read from the PROT_NONE area first to force the choice of ptrace.
|
||||
|
@ -348,6 +337,8 @@ TEST_F(MemoryRemoteTest, read_choose_correctly) {
|
|||
bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
|
||||
ASSERT_EQ(sizeof(value), bytes);
|
||||
ASSERT_EQ(0xfcfcfcfcU, value);
|
||||
|
||||
ASSERT_TRUE(TestDetach(pid));
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
@ -50,6 +51,18 @@ inline bool TestQuiescePid(pid_t pid) {
|
|||
return ready;
|
||||
}
|
||||
|
||||
inline bool TestAttach(pid_t pid) {
|
||||
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TestQuiescePid(pid);
|
||||
}
|
||||
|
||||
inline bool TestDetach(pid_t pid) {
|
||||
return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
|
||||
}
|
||||
|
||||
void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
Loading…
Reference in a new issue