diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -102,6 +102,11 @@ void SetStopped(); + /// Extend m_stop_description with logical and allocation tag values. + /// If there is an error along the way just add the information we were able + /// to get. + void AnnotateSyncTagCheckFault(const siginfo_t *info); + // Member Variables lldb::StateType m_state; ThreadStopInfo m_stop_info; diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/SmallString.h" #include "Plugins/Process/POSIX/CrashReason.h" +#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h" #include // Try to define a macro to encapsulate the tgkill syscall @@ -299,11 +300,66 @@ ? CrashReason::eInvalidAddress : GetCrashReason(*info); m_stop_description = GetCrashReasonString(reason, *info); + + if (reason == CrashReason::eSyncTagCheckFault) { + AnnotateSyncTagCheckFault(info); + } + break; } } } +void NativeThreadLinux::AnnotateSyncTagCheckFault(const siginfo_t *info) { + int32_t allocation_tag_type = 0; + switch (GetProcess().GetArchitecture().GetMachine()) { + // aarch64_32 deliberately not here because there's no 32 bit MTE + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + allocation_tag_type = MemoryTagManagerAArch64MTE::eMTE_allocation; + break; + default: + return; + } + + auto details = + GetRegisterContext().GetMemoryTaggingDetails(allocation_tag_type); + if (!details) { + llvm::consumeError(details.takeError()); + return; + } + + // We assume that the stop description is currently: + // signal SIGSEGV: sync tag check fault (fault address: ) + // Remove the closing ) + m_stop_description.pop_back(); + + std::stringstream ss; + lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); + std::unique_ptr manager(std::move(details->manager)); + + ss << " logical tag: 0x" << std::hex << manager->GetLogicalTag(fault_addr); + + std::vector allocation_tag_data; + Status status = GetProcess().ReadMemoryTags(allocation_tag_type, fault_addr, + manager->GetGranuleSize(), + allocation_tag_data); + + if (status.Success()) { + llvm::Expected> allocation_tag = + manager->UnpackTagsData(allocation_tag_data, 1); + if (allocation_tag) { + ss << " allocation tag: 0x" << std::hex << allocation_tag->front() << ")"; + } else { + llvm::consumeError(allocation_tag.takeError()); + ss << ")"; + } + } else + ss << ")"; + + m_stop_description += ss.str(); +} + bool NativeThreadLinux::IsStopped(int *signo) { if (!StateIsStoppedState(m_state, false)) return false; diff --git a/lldb/test/API/linux/aarch64/mte_tag_faults/Makefile b/lldb/test/API/linux/aarch64/mte_tag_faults/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/mte_tag_faults/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -march=armv8.5-a+memtag + +include Makefile.rules diff --git a/lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py b/lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py @@ -0,0 +1,59 @@ +""" +Test reporting of MTE tag access faults. +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AArch64LinuxMTEMemoryTagFaultsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + def setup_mte_test(self, fault_type): + if not self.isAArch64MTE(): + self.skipTest('Target must support MTE.') + + self.build() + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line(self, "main.c", + line_number('main.c', '// Breakpoint here'), + num_expected_locations=1) + + self.runCmd("run {}".format(fault_type), RUN_SUCCEEDED) + + if self.process().GetState() == lldb.eStateExited: + self.fail("Test program failed to run.") + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + @skipUnlessAArch64MTELinuxCompiler + def test_mte_tag_fault_sync(self): + self.setup_mte_test("sync") + # The logical tag should be included in the fault address + # and we know what the bottom byte should be. + self.expect("continue", + patterns=[ + "\* thread #1, name = 'a.out', stop reason = signal SIGSEGV: " + "sync tag check fault \(fault address: 0x9[0-9A-Fa-f]+00\ " + "logical tag: 0x9 allocation tag: 0xa\)"]) + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + @skipUnlessAArch64MTELinuxCompiler + def test_mte_tag_fault_async(self): + self.setup_mte_test("async") + self.expect("continue", + substrs=[ + "* thread #1, name = 'a.out', stop reason = " + "signal SIGSEGV: async tag check fault"]) diff --git a/lldb/test/API/linux/aarch64/mte_tag_faults/main.c b/lldb/test/API/linux/aarch64/mte_tag_faults/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/mte_tag_faults/main.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char const *argv[]) { + // We assume that the test runner has checked we're on an MTE system + + // Only expect to get the fault type + if (argc != 2) + return 1; + + // Tagged address ABI also enables MTE faults + // Allow tags 9 and 10 to be generated + unsigned long prctl_arg2 = + PR_TAGGED_ADDR_ENABLE | ((3 << 9) << PR_MTE_TAG_SHIFT); + if (!strcmp(argv[1], "sync")) + prctl_arg2 |= PR_MTE_TCF_SYNC; + else if (!strcmp(argv[1], "async")) + prctl_arg2 |= PR_MTE_TCF_ASYNC; + else + return 1; + + if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg2, 0, 0, 0)) + return 1; + + char *buf = mmap(0, sysconf(_SC_PAGESIZE), PROT_MTE | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (buf == MAP_FAILED) + return 1; + + // Tag ptr with 10 + char *tagged_buf = __arm_mte_create_random_tag(buf, 1 << 9); + // Set first allocation tag to 10 + __arm_mte_set_tag(tagged_buf); + // Retag the pointer with 9 + tagged_buf = __arm_mte_create_random_tag(buf, 1 << 10); + + // Breakpoint here + // Faults, 9 != 10 + *(tagged_buf) = '?'; + + return 0; +}