aboutsummaryrefslogtreecommitdiff
path: root/lldb/test
diff options
context:
space:
mode:
authorDavid Spickett <david.spickett@linaro.org>2022-07-11 13:26:55 +0100
committerDavid Spickett <david.spickett@linaro.org>2022-07-26 08:46:36 +0100
commit2f9fa9ef5387de3d87b0c866c678d93695c1c1f3 (patch)
tree4890ade4d90ace163f613d285cb67b4d3c51c243 /lldb/test
parent4075a811ad99b7e263b8b99954cef8c96b042e22 (diff)
downloadllvm-2f9fa9ef5387de3d87b0c866c678d93695c1c1f3.zip
llvm-2f9fa9ef5387de3d87b0c866c678d93695c1c1f3.tar.gz
llvm-2f9fa9ef5387de3d87b0c866c678d93695c1c1f3.tar.bz2
[lldb][AArch64] Add support for memory tags in core files
This teaches ProcessElfCore to recognise the MTE tag segments. https://www.kernel.org/doc/html/latest/arm64/memory-tagging-extension.html#core-dump-support These segments contain all the tags for a matching memory segment which will have the same size in virtual address terms. In real terms it's 2 tags per byte so the data in the segment is much smaller. Since MTE is the only tag type supported I have hardcoded some things to those values. We could and should support more formats as they appear but doing so now would leave code untested until that happens. A few things to note: * /proc/pid/smaps is not in the core file, only the details you have in "maps". Meaning we mark a region tagged only if it has a tag segment. * A core file supports memory tagging if it has at least 1 memory tag segment, there is no other flag we can check to tell if memory tagging was enabled. (unlike a live process that can support memory tagging even if there are currently no tagged memory regions) Tests have been added at the commands level for a core file with mte and without. There is a lot of overlap between the "memory tag read" tests here and the unit tests for MemoryTagManagerAArch64MTE::UnpackTagsFromCoreFileSegment, but I think it's worth keeping to check ProcessElfCore doesn't cause an assert. Depends on D129487 Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D129489
Diffstat (limited to 'lldb/test')
-rw-r--r--lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py170
-rw-r--r--lldb/test/API/linux/aarch64/mte_core_file/core.mtebin0 -> 20608 bytes
-rw-r--r--lldb/test/API/linux/aarch64/mte_core_file/core.nomtebin0 -> 20480 bytes
-rw-r--r--lldb/test/API/linux/aarch64/mte_core_file/main.c78
4 files changed, 248 insertions, 0 deletions
diff --git a/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
new file mode 100644
index 0000000..d16916c
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
@@ -0,0 +1,170 @@
+"""
+Test that memory tagging features work with Linux core files.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class AArch64LinuxMTEMemoryTagCoreFileTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ NO_DEBUG_INFO_TESTCASE = True
+
+ MTE_BUF_ADDR = hex(0xffff82c74000)
+ BUF_ADDR = hex(0xffff82c73000)
+
+ @skipIfLLVMTargetMissing("AArch64")
+ def test_mte_tag_core_file_memory_region(self):
+ """ Test that memory regions are marked as tagged when there is a tag
+ segment in the core file. """
+ self.runCmd("target create --core core.mte")
+
+ # There should only be one tagged region.
+ self.runCmd("memory region --all")
+ got = self.res.GetOutput()
+ found_tagged_region = False
+
+ for line in got.splitlines():
+ if "memory tagging: enabled" in line:
+ if found_tagged_region:
+ self.fail("Expected only one tagged region.")
+ found_tagged_region = True
+
+ self.assertTrue(found_tagged_region, "Did not find a tagged memory region.")
+
+ # mte_buf is tagged, buf is not.
+ tagged = "memory tagging: enabled"
+ self.expect("memory region {}".format(self.MTE_BUF_ADDR),
+ patterns=[tagged])
+ self.expect("memory region {}".format(self.BUF_ADDR),
+ patterns=[tagged], matching=False)
+
+ @skipIfLLVMTargetMissing("AArch64")
+ def test_mte_tag_core_file_tag_write(self):
+ """ Test that "memory tag write" does not work with core files
+ as they are read only. """
+ self.runCmd("target create --core core.mte")
+
+ self.expect("memory tag write {} 1".format(self.MTE_BUF_ADDR), error=True,
+ patterns=["error: elf-core does not support writing memory tags"])
+
+ @skipIfLLVMTargetMissing("AArch64")
+ def test_mte_tag_core_file_tag_read(self):
+ """ Test that "memory tag read" works with core files."""
+ self.runCmd("target create --core core.mte")
+
+ # Tags are packed 2 per byte meaning that in addition to granule alignment
+ # there is also 2 x granule alignment going on.
+
+ # All input validation should work as normal.
+ not_tagged_pattern = ("error: Address range 0x[A-Fa-f0-9]+:0x[A-Fa-f0-9]+ "
+ "is not in a memory tagged region")
+ self.expect("memory tag read {}".format(self.BUF_ADDR),
+ error=True, patterns=[not_tagged_pattern])
+ # The first part of this range is not tagged.
+ self.expect("memory tag read {addr}-16 {addr}+16".format(
+ addr=self.MTE_BUF_ADDR), error=True,
+ patterns=[not_tagged_pattern])
+ # The last part of this range is not tagged.
+ self.expect("memory tag read {addr}+4096-16 {addr}+4096+16".format(
+ addr=self.MTE_BUF_ADDR), error=True,
+ patterns=[not_tagged_pattern])
+
+ self.expect("memory tag read {addr}+16 {addr}".format(
+ addr=self.MTE_BUF_ADDR), error=True,
+ patterns=["error: End address \(0x[A-Fa-f0-9]+\) "
+ "must be greater than the start address "
+ "\(0x[A-Fa-f0-9]+\)"])
+
+ # The simplest scenario. 2 granules means 1 byte of packed tags
+ # with no realignment required.
+ self.expect("memory tag read {addr} {addr}+32".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)$"])
+
+ # Here we want just one tag so must use half of the first byte.
+ # (start is aligned length is not)
+ self.expect("memory tag read {addr} {addr}+16".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0$"])
+ # Get the other half of the first byte.
+ # (end is aligned start is not)
+ self.expect("memory tag read {addr}+16 {addr}+32".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)$"])
+
+ # Same thing but with a starting range > 1 granule.
+ self.expect("memory tag read {addr} {addr}+48".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n"
+ "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)$"])
+ self.expect("memory tag read {addr}+16 {addr}+64".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n"
+ "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)\n"
+ "\[0x[A-Fa-f0-9]+30, 0x[A-Fa-f0-9]+40\): 0x3 \(mismatch\)$"])
+ # Here both start and end are unaligned.
+ self.expect("memory tag read {addr}+16 {addr}+80".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n"
+ "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)\n"
+ "\[0x[A-Fa-f0-9]+30, 0x[A-Fa-f0-9]+40\): 0x3 \(mismatch\)\n"
+ "\[0x[A-Fa-f0-9]+40, 0x[A-Fa-f0-9]+50\): 0x4 \(mismatch\)$"])
+
+ # For the intial alignment of start/end to granule boundaries the tag manager
+ # is used, so this reads 1 tag as it would normally.
+ self.expect("memory tag read {addr} {addr}+1".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0$"])
+
+ # This range is aligned to granules as mte_buf to mte_buf+32 so the result
+ # should be 2 granules.
+ self.expect("memory tag read {addr} {addr}+17".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)$"])
+
+ # Alignment of this range causes it to become unaligned to 2*granule boundaries.
+ self.expect("memory tag read {addr} {addr}+33".format(
+ addr=self.MTE_BUF_ADDR),
+ patterns=[
+ "Allocation tags:\n"
+ "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+ "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n",
+ "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)$"])
+
+ @skipIfLLVMTargetMissing("AArch64")
+ def test_mte_commands_no_mte(self):
+ """ Test that memory tagging commands fail on an AArch64 corefile without
+ any tag segments."""
+
+ self.runCmd("target create --core core.nomte")
+
+ self.expect("memory tag read 0 1",
+ substrs=["error: Process does not support memory tagging"], error=True)
+ # Note that this tells you memory tagging is not supported at all, versus
+ # the MTE core file which does support it but does not allow writing tags.
+ self.expect("memory tag write 0 1",
+ substrs=["error: Process does not support memory tagging"], error=True)
diff --git a/lldb/test/API/linux/aarch64/mte_core_file/core.mte b/lldb/test/API/linux/aarch64/mte_core_file/core.mte
new file mode 100644
index 0000000..84a3266
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_core_file/core.mte
Binary files differ
diff --git a/lldb/test/API/linux/aarch64/mte_core_file/core.nomte b/lldb/test/API/linux/aarch64/mte_core_file/core.nomte
new file mode 100644
index 0000000..201f288
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_core_file/core.nomte
Binary files differ
diff --git a/lldb/test/API/linux/aarch64/mte_core_file/main.c b/lldb/test/API/linux/aarch64/mte_core_file/main.c
new file mode 100644
index 0000000..89027e0
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_core_file/main.c
@@ -0,0 +1,78 @@
+// Program to generate core files to test MTE tag features.
+//
+// This file uses ACLE intrinsics as detailed in:
+// https://developer.arm.com/documentation/101028/0012/10--Memory-tagging-intrinsics?lang=en
+//
+// Compile with:
+// <gcc or clang> -march=armv8.5-a+memtag -g main.c -o a.out.mte
+// <gcc or clang> -march=armv8.5-a+memtag -g main.c -DNO_MTE -o a.out.nomte
+//
+// /proc/self/coredump_filter was set to 2 when the core files were made.
+
+#include <arm_acle.h>
+#include <asm/mman.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+int main(int argc, char const *argv[]) {
+#ifdef NO_MTE
+ *(char *)(0) = 0;
+#endif
+
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL,
+ PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+ // Allow all tags to be generated by the addg
+ // instruction __arm_mte_increment_tag produces.
+ (0xffff << PR_MTE_TAG_SHIFT),
+ 0, 0, 0)) {
+ return 1;
+ }
+
+ size_t page_size = sysconf(_SC_PAGESIZE);
+ char *mte_buf = mmap(0, page_size, PROT_READ | PROT_WRITE | PROT_MTE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (!mte_buf)
+ return 1;
+
+ printf("mte_buf: %p\n", mte_buf);
+
+ // Allocate some untagged memory before the tagged memory.
+ char *buf = mmap(0, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (!buf)
+ return 1;
+
+ printf("buf: %p\n", buf);
+
+ // This write means that the memory for buf is included in the corefile.
+ // So we can read from the end of it into mte_buf during the test.
+ *buf = 1;
+
+ // These must be next to each other for the tests to work.
+ // <high address>
+ // mte_buf
+ // buf
+ // <low address>
+ if ((mte_buf - buf) != page_size) {
+ return 1;
+ }
+
+ // Set incrementing tags until end of the page.
+ char *tagged_ptr = mte_buf;
+ // This ignores tag bits when subtracting the addresses.
+ while (__arm_mte_ptrdiff(tagged_ptr, mte_buf) < page_size) {
+ // Set the allocation tag for this location.
+ __arm_mte_set_tag(tagged_ptr);
+ // + 16 for 16 byte granules.
+ // Earlier we allowed all tag values, so this will give us an
+ // incrementing pattern 0-0xF wrapping back to 0.
+ tagged_ptr = __arm_mte_increment_tag(tagged_ptr + 16, 1);
+ }
+
+ // Will fault because logical tag 0 != allocation tag 1.
+ *(mte_buf + 16) = 1;
+
+ return 0;
+}