Merge "libunwindstack: Add Memory::ReadTag() function for reading memory tags." am: b416a925ab

Change-Id: I5a748b08e866cda1fd33c3b0d40b4bf8c9a094c5
This commit is contained in:
Peter Collingbourne 2020-04-17 18:33:05 +00:00 committed by Automerger Merge Worker
commit 3c1f492ee1
10 changed files with 238 additions and 42 deletions

View file

@ -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 {

View file

@ -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) {}

View file

@ -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(); }

View file

@ -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

View 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

View file

@ -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_; }

View file

@ -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);

View 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

View file

@ -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

View file

@ -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