Skip to content

Commit

Permalink
[lldb][AArch64] Annotate synchronous tag faults
Browse files Browse the repository at this point in the history
In the latest Linux kernels synchronous tag faults
include the tag bits in their address.
This change adds logical and allocation tags to the
description of synchronous tag faults.
(asynchronous faults have no address)

Process 1626 stopped
* thread flang-compiler#1, name = 'a.out', stop reason = signal SIGSEGV: sync tag check fault (fault address: 0x900fffff7ff9010 logical tag: 0x9 allocation tag: 0x0)

This extends the existing description and will
show as much as it can on the rare occasion something
fails.

This change supports AArch64 MTE only but other
architectures could be added by extending the
switch at the start of AnnotateSyncTagCheckFault.
The rest of the function is generic code.

Tests have been added for synchronous and asynchronous
MTE faults.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D105178

(cherry picked from commit d510b5f)
  • Loading branch information
DavidSpickett committed Aug 3, 2021
1 parent dc00e19 commit bc0cc10
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 0 deletions.
59 changes: 59 additions & 0 deletions lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "llvm/ADT/SmallString.h"

#include "Plugins/Process/POSIX/CrashReason.h"
#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"

#include <sys/syscall.h>
// Try to define a macro to encapsulate the tgkill syscall
Expand Down Expand Up @@ -299,11 +300,69 @@ void NativeThreadLinux::SetStoppedBySignal(uint32_t signo,
? 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: <addr>)
// Remove the closing )
m_stop_description.pop_back();

std::stringstream ss;
lldb::addr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
std::unique_ptr<MemoryTagManager> manager(std::move(details->manager));

ss << " logical tag: 0x" << std::hex << manager->GetLogicalTag(fault_addr);

std::vector<uint8_t> allocation_tag_data;
// The fault address may not be granule aligned. ReadMemoryTags will granule
// align any range you give it, potentially making it larger.
// To prevent this set len to 1. This always results in a range that is at
// most 1 granule in size and includes fault_addr.
Status status = GetProcess().ReadMemoryTags(allocation_tag_type, fault_addr,
1, allocation_tag_data);

if (status.Success()) {
llvm::Expected<std::vector<lldb::addr_t>> 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;
Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ class NativeThreadLinux : public NativeThreadProtocol {

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;
Expand Down
4 changes: 4 additions & 0 deletions lldb/test/API/linux/aarch64/mte_tag_faults/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
C_SOURCES := main.c
CFLAGS_EXTRAS := -march=armv8.5-a+memtag

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
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.
# It will be 0x10 (to be in the 2nd granule), +1 to be 0x11.
# Which tests that lldb-server handles fault addresses that
# are not granule aligned.
self.expect("continue",
patterns=[
"\* thread #1, name = 'a.out', stop reason = signal SIGSEGV: "
"sync tag check fault \(fault address: 0x9[0-9A-Fa-f]+11\ "
"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"])
59 changes: 59 additions & 0 deletions lldb/test/API/linux/aarch64/mte_tag_faults/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <arm_acle.h>
#include <asm/hwcap.h>
#include <asm/mman.h>
#include <stdbool.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>

// Set bits 59-56 to tag, removing any existing tag
static char *set_tag(char *ptr, size_t tag) {
return (char *)(((size_t)ptr & ~((size_t)0xf << 56)) | (tag << 56));
}

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;

unsigned long prctl_arg2 = 0;
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;

// Set fault type
if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg2, 0, 0, 0))
return 1;

// Allocate some memory with tagging enabled that we
// can read/write if we use correct tags.
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;

// Our pointer will have tag 9
char *tagged_buf = set_tag(buf, 9);
// Set allocation tags for the first 2 granules
__arm_mte_set_tag(set_tag(tagged_buf, 9));
__arm_mte_set_tag(set_tag(tagged_buf + 16, 10));

// Confirm that we can write when tags match
*tagged_buf = ' ';

// Breakpoint here
// Faults because tag 9 in the ptr != allocation tag of 10.
// + 16 puts us in the second granule and +1 makes the fault address
// misaligned relative to the granule size. This misalignment must
// be accounted for by lldb-server.
*(tagged_buf + 16 + 1) = '?';

return 0;
}

0 comments on commit bc0cc10

Please sign in to comment.