[lldb][AArch64] Annotate synchronous tag faults

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 #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
This commit is contained in:
David Spickett
2021-04-12 15:40:32 +01:00
parent 971f4173f8
commit d510b5f199
5 changed files with 189 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
C_SOURCES := main.c
CFLAGS_EXTRAS := -march=armv8.5-a+memtag
include Makefile.rules

View File

@@ -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"])

View File

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