diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index ab59a4bab..57c4c2efe 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -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 { diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 8de3d9808..fac9085b4 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -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, uint64_t begin, uint64_t length, uint64_t offset) : memory_(memory), begin_(begin), length_(length), offset_(offset) {} diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h index 769d90746..d97640d8b 100644 --- a/libunwindstack/MemoryCache.h +++ b/libunwindstack/MemoryCache.h @@ -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(); } diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h index 7e027cf4b..741f1077c 100644 --- a/libunwindstack/MemoryLocal.h +++ b/libunwindstack/MemoryLocal.h @@ -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 diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp new file mode 100644 index 000000000..d1d0ebc29 --- /dev/null +++ b/libunwindstack/MemoryMte.cpp @@ -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 + +#include +#include + +#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 diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h index db367d649..dd09c8838 100644 --- a/libunwindstack/MemoryRemote.h +++ b/libunwindstack/MemoryRemote.h @@ -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_; } diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h index ecd908a5d..26252b6b0 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -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); diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp new file mode 100644 index 000000000..3ae322e35 --- /dev/null +++ b/libunwindstack/tests/MemoryMteTest.cpp @@ -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 +#include + +#include + +#include + +#include "MemoryLocal.h" +#include "MemoryRemote.h" +#include "TestUtils.h" + +namespace unwindstack { + +static uintptr_t CreateTagMapping() { + uintptr_t mapping = + reinterpret_cast(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + if (reinterpret_cast(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 diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp index c90dedcec..385078dca 100644 --- a/libunwindstack/tests/MemoryRemoteTest.cpp +++ b/libunwindstack/tests/MemoryRemoteTest.cpp @@ -26,8 +26,10 @@ #include -#include #include +#include +#include +#include #include #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 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 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( 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 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 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(mapping) + 2 * page_size, page_size)); - ASSERT_TRUE(Attach(pid)); + ASSERT_TRUE(TestAttach(pid)); MemoryRemote remote(pid); std::vector 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(mapping) + page_size, &value, sizeof(value)); ASSERT_EQ(sizeof(value), bytes); ASSERT_EQ(0xfcfcfcfcU, value); + + ASSERT_TRUE(TestDetach(pid)); } } // namespace unwindstack diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h index a4d7b9b33..0685006bf 100644 --- a/libunwindstack/tests/TestUtils.h +++ b/libunwindstack/tests/TestUtils.h @@ -21,6 +21,7 @@ #include #include #include +#include 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