aboutsummaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/packages/Python/lldbsuite/test/cpu_feature.py2
-rw-r--r--lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp166
-rw-r--r--lldb/test/API/macosx/mte/Makefile12
-rw-r--r--lldb/test/API/macosx/mte/TestDarwinMTE.py110
-rw-r--r--lldb/test/API/macosx/mte/main.c28
-rw-r--r--lldb/test/API/macosx/mte/mte-entitlements.plist10
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp2
-rw-r--r--lldb/tools/debugserver/source/DNB.cpp10
-rw-r--r--lldb/tools/debugserver/source/DNB.h3
-rw-r--r--lldb/tools/debugserver/source/DNBDefs.h3
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.h2
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.mm17
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp59
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMMemory.h2
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp46
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMRegion.h3
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.cpp105
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.h2
18 files changed, 462 insertions, 120 deletions
diff --git a/lldb/packages/Python/lldbsuite/test/cpu_feature.py b/lldb/packages/Python/lldbsuite/test/cpu_feature.py
index b46a5ac..d7668c1 100644
--- a/lldb/packages/Python/lldbsuite/test/cpu_feature.py
+++ b/lldb/packages/Python/lldbsuite/test/cpu_feature.py
@@ -62,7 +62,7 @@ class CPUFeature:
class AArch64:
FPMR = CPUFeature("fpmr")
GCS = CPUFeature("gcs")
- MTE = CPUFeature("mte")
+ MTE = CPUFeature("mte", "hw.optional.arm.FEAT_MTE4")
MTE_STORE_ONLY = CPUFeature("mtestoreonly")
PTR_AUTH = CPUFeature("paca", "hw.optional.arm.FEAT_PAuth2")
SME = CPUFeature("sme", "hw.optional.arm.FEAT_SME")
diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index 91c93be..9cdb846 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -2067,6 +2067,43 @@ static bool ParseTrieEntries(DataExtractor &data, lldb::offset_t offset,
return true;
}
+static bool
+TryParseV2ObjCMetadataSymbol(const char *&symbol_name,
+ const char *&symbol_name_non_abi_mangled,
+ SymbolType &type) {
+ static constexpr llvm::StringLiteral g_objc_v2_prefix_class("_OBJC_CLASS_$_");
+ static constexpr llvm::StringLiteral g_objc_v2_prefix_metaclass(
+ "_OBJC_METACLASS_$_");
+ static constexpr llvm::StringLiteral g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
+
+ llvm::StringRef symbol_name_ref(symbol_name);
+ if (symbol_name_ref.empty())
+ return false;
+
+ if (symbol_name_ref.starts_with(g_objc_v2_prefix_class)) {
+ symbol_name_non_abi_mangled = symbol_name + 1;
+ symbol_name = symbol_name + g_objc_v2_prefix_class.size();
+ type = eSymbolTypeObjCClass;
+ return true;
+ }
+
+ if (symbol_name_ref.starts_with(g_objc_v2_prefix_metaclass)) {
+ symbol_name_non_abi_mangled = symbol_name + 1;
+ symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
+ type = eSymbolTypeObjCMetaClass;
+ return true;
+ }
+
+ if (symbol_name_ref.starts_with(g_objc_v2_prefix_ivar)) {
+ symbol_name_non_abi_mangled = symbol_name + 1;
+ symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
+ type = eSymbolTypeObjCIVar;
+ return true;
+ }
+
+ return false;
+}
+
static SymbolType GetSymbolType(const char *&symbol_name,
bool &demangled_is_synthesized,
const SectionSP &text_section_sp,
@@ -2183,9 +2220,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
uint32_t i;
FileSpecList dylib_files;
- llvm::StringRef g_objc_v2_prefix_class("_OBJC_CLASS_$_");
- llvm::StringRef g_objc_v2_prefix_metaclass("_OBJC_METACLASS_$_");
- llvm::StringRef g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
UUID image_uuid;
for (i = 0; i < m_header.ncmds; ++i) {
@@ -2805,33 +2839,15 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
is_gsym = true;
sym[sym_idx].SetExternal(true);
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
-
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
+ if (TryParseV2ObjCMetadataSymbol(
+ symbol_name, symbol_name_non_abi_mangled,
+ type)) {
demangled_is_synthesized = true;
} else {
if (nlist.n_value != 0)
symbol_section = section_info.GetSection(
nlist.n_sect, nlist.n_value);
+
type = eSymbolTypeData;
}
break;
@@ -3317,48 +3333,10 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
symbol_sect_name) {
type = eSymbolTypeRuntime;
- if (symbol_name) {
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with("_OBJC_")) {
- llvm::StringRef
- g_objc_v2_prefix_class(
- "_OBJC_CLASS_$_");
- llvm::StringRef
- g_objc_v2_prefix_metaclass(
- "_OBJC_METACLASS_$_");
- llvm::StringRef
- g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
- if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled =
- symbol_name + 1;
- symbol_name =
- symbol_name +
- g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
- } else if (
- symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled =
- symbol_name + 1;
- symbol_name =
- symbol_name +
- g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled =
- symbol_name + 1;
- symbol_name =
- symbol_name +
- g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
- demangled_is_synthesized = true;
- }
- }
- }
+ if (TryParseV2ObjCMetadataSymbol(
+ symbol_name,
+ symbol_name_non_abi_mangled, type))
+ demangled_is_synthesized = true;
} else if (symbol_sect_name &&
::strstr(symbol_sect_name,
"__gcc_except_tab") ==
@@ -3665,27 +3643,14 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
is_gsym = true;
sym[sym_idx].SetExternal(true);
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with(g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name = symbol_name + g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
-
- } else if (symbol_name_ref.starts_with(g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
+ if (TryParseV2ObjCMetadataSymbol(symbol_name,
+ symbol_name_non_abi_mangled, type)) {
demangled_is_synthesized = true;
} else {
if (nlist.n_value != 0)
symbol_section =
section_info.GetSection(nlist.n_sect, nlist.n_value);
+
type = eSymbolTypeData;
}
} break;
@@ -4124,38 +4089,9 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
::strstr(symbol_sect_name, "__objc") == symbol_sect_name) {
type = eSymbolTypeRuntime;
- if (symbol_name) {
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with("_OBJC_")) {
- llvm::StringRef g_objc_v2_prefix_class(
- "_OBJC_CLASS_$_");
- llvm::StringRef g_objc_v2_prefix_metaclass(
- "_OBJC_METACLASS_$_");
- llvm::StringRef g_objc_v2_prefix_ivar(
- "_OBJC_IVAR_$_");
- if (symbol_name_ref.starts_with(g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
- demangled_is_synthesized = true;
- }
- }
- }
+ if (TryParseV2ObjCMetadataSymbol(
+ symbol_name, symbol_name_non_abi_mangled, type))
+ demangled_is_synthesized = true;
} else if (symbol_sect_name &&
::strstr(symbol_sect_name, "__gcc_except_tab") ==
symbol_sect_name) {
diff --git a/lldb/test/API/macosx/mte/Makefile b/lldb/test/API/macosx/mte/Makefile
new file mode 100644
index 0000000..cb20942
--- /dev/null
+++ b/lldb/test/API/macosx/mte/Makefile
@@ -0,0 +1,12 @@
+C_SOURCES := main.c
+
+EXE := uaf_mte
+
+all: uaf_mte sign
+
+include Makefile.rules
+
+sign: mte-entitlements.plist uaf_mte
+ifeq ($(OS),Darwin)
+ codesign -s - -f --entitlements $^
+endif
diff --git a/lldb/test/API/macosx/mte/TestDarwinMTE.py b/lldb/test/API/macosx/mte/TestDarwinMTE.py
new file mode 100644
index 0000000..ef858b1
--- /dev/null
+++ b/lldb/test/API/macosx/mte/TestDarwinMTE.py
@@ -0,0 +1,110 @@
+"""Test MTE Memory Tagging on Apple platforms"""
+
+import lldb
+import re
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbsuite.test.cpu_feature as cpu_feature
+
+exe_name = "uaf_mte" # Must match Makefile
+
+
+class TestDarwinMTE(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_tag_fault(self):
+ self.build()
+ exe = self.getBuildArtifact(exe_name)
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, VALID_TARGET)
+
+ process = target.LaunchSimple(None, None, None)
+ self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
+
+ self.expect(
+ "thread info",
+ substrs=[
+ "stop reason = EXC_ARM_MTE_TAG_FAULT",
+ "MTE tag mismatch detected",
+ ],
+ )
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_memory_region(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
+ )
+
+ # (lldb) memory region ptr
+ # [0x00000001005ec000-0x00000001009ec000) rw-
+ # memory tagging: enabled
+ # Modified memory (dirty) page list provided, 2 entries.
+ # Dirty pages: 0x1005ec000, 0x1005fc000.
+ self.expect("memory region ptr", substrs=["memory tagging: enabled"])
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_memory_read_with_tags(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
+ )
+
+ # (lldb) memory read ptr-16 ptr+48 --show-tags
+ # 0x7d2c00930: 00 00 00 00 00 00 00 00 d0 e3 a5 0a 02 00 00 00 ................ (tag: 0x3)
+ # 0x7d2c00940: 48 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 Hello........... (tag: 0xb)
+ # 0x7d2c00950: 57 6f 72 6c 64 00 00 00 00 00 00 00 00 00 00 00 World........... (tag: 0xb)
+ # 0x7d2c00960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ (tag: 0x9)
+ self.expect(
+ "memory read ptr-16 ptr+48 --show-tags",
+ substrs=[" Hello...........", " World..........."],
+ patterns=[r"(.*\(tag: 0x[0-9a-f]\)\n){4}"],
+ )
+
+ def _parse_pointer_tag(self, output):
+ return re.search(r"Logical tag: (0x[0-9a-f])", output).group(1)
+
+ def _parse_memory_tags(self, output, expected_tag_count):
+ tags = re.findall(r"\): (0x[0-9a-f])", output)
+ self.assertEqual(len(tags), expected_tag_count)
+ return tags
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_memory_tag_read(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
+ )
+
+ # (lldb) memory tag read ptr-1 ptr+33
+ # Logical tag: 0x5
+ # Allocation tags:
+ # [0x100a65a40, 0x100a65a50): 0xf (mismatch)
+ # [0x100a65a50, 0x100a65a60): 0x5
+ # [0x100a65a60, 0x100a65a70): 0x5
+ # [0x100a65a70, 0x100a65a80): 0x2 (mismatch)
+ self.expect(
+ "memory tag read ptr-1 ptr+33",
+ substrs=["Logical tag: 0x", "Allocation tags:", "(mismatch)"],
+ patterns=[r"(\[.*\): 0x[0-9a-f].*\n){4}"],
+ )
+ output = self.res.GetOutput()
+ self.assertEqual(output.count("(mismatch)"), 2)
+ ptr_tag = self._parse_pointer_tag(output)
+ tags = self._parse_memory_tags(output, 4)
+ self.assertEqual(tags[1], ptr_tag)
+ self.assertEqual(tags[2], ptr_tag)
+ self.assertNotEqual(tags[0], ptr_tag) # Memory that comes before/after
+ self.assertNotEqual(tags[3], ptr_tag) # allocation has different tag.
+
+ # Continue running until MTE fault
+ self.expect("process continue", substrs=["stop reason = EXC_ARM_MTE_TAG_FAULT"])
+
+ self.runCmd("memory tag read ptr-1 ptr+33")
+ output = self.res.GetOutput()
+ self.assertEqual(output.count("(mismatch)"), 4)
+ tags = self._parse_memory_tags(output, 4)
+ self.assertTrue(all(t != ptr_tag for t in tags))
diff --git a/lldb/test/API/macosx/mte/main.c b/lldb/test/API/macosx/mte/main.c
new file mode 100644
index 0000000..f9f6b15
--- /dev/null
+++ b/lldb/test/API/macosx/mte/main.c
@@ -0,0 +1,28 @@
+#include <malloc/malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Produce some names on the trace
+const size_t tag_granule = 16;
+static uint8_t *my_malloc(void) { return malloc(2 * tag_granule); }
+static uint8_t *allocate(void) { return my_malloc(); }
+
+static void my_free(void *ptr) { free(ptr); }
+static void deallocate(void *ptr) { my_free(ptr); }
+
+static void touch_memory(uint8_t *ptr) { ptr[7] = 1; } // invalid access
+static void modify(uint8_t *ptr) { touch_memory(ptr); }
+
+int main() {
+ uint8_t *ptr = allocate();
+
+ strncpy((char *)ptr, "Hello", 16);
+ strncpy((char *)ptr + 16, "World", 16);
+
+ deallocate(ptr); // before free
+
+ modify(ptr); // use-after-free
+
+ return 0;
+}
diff --git a/lldb/test/API/macosx/mte/mte-entitlements.plist b/lldb/test/API/macosx/mte/mte-entitlements.plist
new file mode 100644
index 0000000..6de5d56
--- /dev/null
+++ b/lldb/test/API/macosx/mte/mte-entitlements.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.hardened-process</key>
+ <true/>
+ <key>com.apple.security.hardened-process.checked-allocations</key>
+ <true/>
+</dict>
+</plist>
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp b/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp
index 81d643d..beb5ae2 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp
@@ -1,4 +1,4 @@
-// REQUIRES: x86
+// REQUIRES: lld, x86
// Test symtab reading
// RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib -o %t.exe -- %s
diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp
index f541134..0cd48d9 100644
--- a/lldb/tools/debugserver/source/DNB.cpp
+++ b/lldb/tools/debugserver/source/DNB.cpp
@@ -1386,6 +1386,16 @@ int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
return -1;
}
+nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size,
+ std::vector<uint8_t> &tags) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().GetMemoryTags(addr, size, tags);
+
+ return false;
+}
+
std::string DNBProcessGetProfileData(nub_process_t pid,
DNBProfileDataScanType scanType) {
MachProcessSP procSP;
diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h
index 10d1f68..1f3d539 100644
--- a/lldb/tools/debugserver/source/DNB.h
+++ b/lldb/tools/debugserver/source/DNB.h
@@ -105,6 +105,9 @@ nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid,
nub_addr_t addr) DNB_EXPORT;
int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
DNBRegionInfo *region_info) DNB_EXPORT;
+nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size,
+ std::vector<uint8_t> &tags) DNB_EXPORT;
std::string
DNBProcessGetProfileData(nub_process_t pid,
DNBProfileDataScanType scanType) DNB_EXPORT;
diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h
index df8ca80..d98399a 100644
--- a/lldb/tools/debugserver/source/DNBDefs.h
+++ b/lldb/tools/debugserver/source/DNBDefs.h
@@ -358,10 +358,11 @@ struct DNBExecutableImageInfo {
struct DNBRegionInfo {
public:
DNBRegionInfo()
- : addr(0), size(0), permissions(0), dirty_pages(), vm_types() {}
+ : addr(0), size(0), permissions(0), flags(), dirty_pages(), vm_types() {}
nub_addr_t addr;
nub_addr_t size;
uint32_t permissions;
+ std::vector<std::string> flags;
std::vector<nub_addr_t> dirty_pages;
std::vector<std::string> vm_types;
};
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.h b/lldb/tools/debugserver/source/MacOSX/MachTask.h
index 2284f6b..c4a20b8 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachTask.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.h
@@ -56,6 +56,8 @@ public:
nub_size_t ReadMemory(nub_addr_t addr, nub_size_t size, void *buf);
nub_size_t WriteMemory(nub_addr_t addr, nub_size_t size, const void *buf);
int GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info);
+ nub_bool_t GetMemoryTags(nub_addr_t addr, nub_size_t size,
+ std::vector<uint8_t> &tags);
std::string GetProfileData(DNBProfileDataScanType scanType);
nub_addr_t AllocateMemory(nub_size_t size, uint32_t permissions);
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
index e2395cf..21156fe 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachTask.mm
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
@@ -229,6 +229,23 @@ int MachTask::GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info) {
return ret;
}
+//----------------------------------------------------------------------
+// MachTask::GetMemoryTags
+//----------------------------------------------------------------------
+nub_bool_t MachTask::GetMemoryTags(nub_addr_t addr, nub_size_t size,
+ std::vector<uint8_t> &tags) {
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return false;
+
+ bool ok = m_vm_memory.GetMemoryTags(task, addr, size, tags);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::GetMemoryTags ( addr = 0x%8.8llx, "
+ "size = 0x%8.8llx ) => %s ( tag count = %llu)",
+ (uint64_t)addr, (uint64_t)size, (ok ? "ok" : "err"),
+ (uint64_t)tags.size());
+ return ok;
+}
+
#define TIME_VALUE_TO_TIMEVAL(a, r) \
do { \
(r)->tv_sec = (a)->seconds; \
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
index f3aa4d7..bb57245 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
@@ -13,6 +13,7 @@
#include "MachVMMemory.h"
#include "DNBLog.h"
#include "MachVMRegion.h"
+#include <cassert>
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
@@ -123,6 +124,7 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address,
region_info->addr = vmRegion.StartAddress();
region_info->size = vmRegion.GetByteSize();
region_info->permissions = vmRegion.GetDNBPermissions();
+ region_info->flags = vmRegion.GetFlags();
region_info->dirty_pages =
get_dirty_pages(task, vmRegion.StartAddress(), vmRegion.GetByteSize());
region_info->vm_types = vmRegion.GetMemoryTypes();
@@ -150,6 +152,63 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address,
return true;
}
+// API availability:
+// mach_vm_update_pointers_with_remote_tags() - 26.0
+// VM_OFFSET_LIST_MAX macro - 26.1
+#ifndef VM_OFFSET_LIST_MAX
+#define VM_OFFSET_LIST_MAX 512
+#endif
+using mach_vm_offset_list_t = mach_vm_offset_t *;
+using mach_vm_update_pointers_with_remote_tags_t = kern_return_t(
+ mach_port_name_t target, mach_vm_offset_list_t in_pointer_list,
+ mach_msg_type_number_t in_pointer_listCnt,
+ mach_vm_offset_list_t out_pointer_list,
+ mach_msg_type_number_t *out_pointer_listCnt);
+
+nub_bool_t MachVMMemory::GetMemoryTags(task_t task, nub_addr_t address,
+ nub_size_t size,
+ std::vector<uint8_t> &tags) {
+ static auto mach_vm_update_pointers_with_remote_tags =
+ (mach_vm_update_pointers_with_remote_tags_t *)dlsym(
+ RTLD_DEFAULT, "mach_vm_update_pointers_with_remote_tags");
+ assert(mach_vm_update_pointers_with_remote_tags);
+
+ // Max batch size supported by mach_vm_update_pointers_with_remote_tags.
+ constexpr uint32_t max_ptr_count = VM_OFFSET_LIST_MAX;
+ constexpr uint32_t tag_shift = 56;
+ constexpr nub_addr_t tag_mask =
+ ((nub_addr_t)0x0f << tag_shift); // Lower half of top byte.
+ constexpr uint32_t tag_granule = 16;
+
+ mach_msg_type_number_t ptr_count =
+ (size / tag_granule) + ((size % tag_granule > 0) ? 1 : 0);
+ ptr_count = std::min(ptr_count, max_ptr_count);
+
+ auto ptr_arr = std::make_unique<mach_vm_offset_t[]>(ptr_count);
+ for (size_t i = 0; i < ptr_count; i++)
+ ptr_arr[i] = (address + i * tag_granule);
+
+ mach_msg_type_number_t ptr_count_out = ptr_count;
+ m_err = mach_vm_update_pointers_with_remote_tags(
+ task, ptr_arr.get(), ptr_count, ptr_arr.get(), &ptr_count_out);
+
+ const bool failed = (m_err.Fail() || (ptr_count != ptr_count_out));
+ if (failed || DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded("::mach_vm_update_pointers_with_remote_tags ( task = "
+ "0x%4.4x, ptr_count = %d ) => %i ( ptr_count_out = %d)",
+ task, ptr_count, m_err.Status(), ptr_count_out);
+ if (failed)
+ return false;
+
+ tags.reserve(ptr_count);
+ for (size_t i = 0; i < ptr_count; i++) {
+ nub_addr_t tag = (ptr_arr[i] & tag_mask) >> tag_shift;
+ tags.push_back(tag);
+ }
+
+ return true;
+}
+
static uint64_t GetPhysicalMemory() {
// This doesn't change often at all. No need to poll each time.
static uint64_t physical_memory = 0;
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
index 05d2c02..8a76160 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
@@ -28,6 +28,8 @@ public:
nub_size_t PageSize(task_t task);
nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address,
DNBRegionInfo *region_info);
+ nub_bool_t GetMemoryTags(task_t task, nub_addr_t address, nub_size_t size,
+ std::vector<uint8_t> &tags);
nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task,
struct task_basic_info ti, cpu_type_t cputype,
nub_process_t pid, vm_statistics64_data_t &vminfo,
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
index 97908b4..9d0d60f 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
@@ -114,6 +114,11 @@ bool MachVMRegion::RestoreProtections() {
return false;
}
+#ifdef VM_REGION_FLAG_JIT_ENABLED
+#define VM_REGION_HAS_FLAGS 1
+#else
+#define VM_REGION_HAS_FLAGS 0
+#endif
bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
// Restore any original protections and clear our vars
Clear();
@@ -140,6 +145,30 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
if (failed)
return false;
if (log_protections) {
+#if VM_REGION_HAS_FLAGS
+ DNBLogThreaded("info = { prot = %u, "
+ "max_prot = %u, "
+ "inheritance = 0x%8.8x, "
+ "offset = 0x%8.8llx, "
+ "user_tag = 0x%8.8x, "
+ "ref_count = %u, "
+ "shadow_depth = %u, "
+ "ext_pager = %u, "
+ "share_mode = %u, "
+ "is_submap = %d, "
+ "behavior = %d, "
+ "object_id = 0x%8.8x, "
+ "user_wired_count = 0x%4.4x, "
+ "flags = %d }",
+ m_data.protection, m_data.max_protection, m_data.inheritance,
+ (uint64_t)m_data.offset, m_data.user_tag, m_data.ref_count,
+ m_data.shadow_depth, m_data.external_pager,
+ m_data.share_mode, m_data.is_submap, m_data.behavior,
+ m_data.object_id, m_data.user_wired_count, m_data.flags);
+#else
+ // Duplicate log call instead of #if-defing printing of flags to avoid
+ // compiler warning: 'embedding a directive within macro arguments has
+ // undefined behavior'
DNBLogThreaded("info = { prot = %u, "
"max_prot = %u, "
"inheritance = 0x%8.8x, "
@@ -158,6 +187,7 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
m_data.shadow_depth, m_data.external_pager,
m_data.share_mode, m_data.is_submap, m_data.behavior,
m_data.object_id, m_data.user_wired_count);
+#endif
}
m_curr_protection = m_data.protection;
@@ -183,6 +213,22 @@ uint32_t MachVMRegion::GetDNBPermissions() const {
return dnb_permissions;
}
+#ifndef VM_REGION_FLAG_MTE_ENABLED
+#define VM_REGION_FLAG_MTE_ENABLED 0x4
+#endif
+std::vector<std::string> MachVMRegion::GetFlags() const {
+ std::vector<std::string> flags;
+#if VM_REGION_HAS_FLAGS
+ if (m_data.flags & VM_REGION_FLAG_JIT_ENABLED)
+ flags.push_back("jit");
+ if (m_data.flags & VM_REGION_FLAG_TPRO_ENABLED)
+ flags.push_back("tpro");
+ if (m_data.flags & VM_REGION_FLAG_MTE_ENABLED)
+ flags.push_back("mt");
+#endif
+ return flags;
+}
+
std::vector<std::string> MachVMRegion::GetMemoryTypes() const {
std::vector<std::string> types;
if (m_data.user_tag == VM_MEMORY_STACK) {
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
index cb77058..ba6e1f3 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
@@ -40,9 +40,10 @@ public:
vm_prot_t prot);
bool RestoreProtections();
bool GetRegionForAddress(nub_addr_t addr);
- std::vector<std::string> GetMemoryTypes() const;
uint32_t GetDNBPermissions() const;
+ std::vector<std::string> GetFlags() const;
+ std::vector<std::string> GetMemoryTypes() const;
const DNBError &GetError() { return m_err; }
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index d9fb22c..434e9cf 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -22,6 +22,9 @@
#include <mach/mach_vm.h>
#include <mach/task_info.h>
#include <memory>
+#if __has_include(<os/security_config.h>)
+#include <os/security_config.h>
+#endif
#include <pwd.h>
#include <string>
#include <sys/stat.h>
@@ -502,6 +505,8 @@ void RNBRemote::CreatePacketTable() {
memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL,
"qMemoryRegionInfo", "Return size and attributes of a memory region that "
"contains the given address"));
+ t.push_back(Packet(get_memory_tags, &RNBRemote::HandlePacket_qMemTags, NULL,
+ "qMemTags", "Return tags for a region of memory"));
t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData,
NULL, "qGetProfileData",
"Return profiling data of the current target."));
@@ -3475,6 +3480,18 @@ static bool GetProcessNameFrom_vAttach(const char *&p,
return return_val;
}
+static bool supports_memory_tagging() {
+ const char *name = "hw.optional.arm.FEAT_MTE4";
+ uint32_t val;
+ size_t len = sizeof(val);
+ int ret = ::sysctlbyname(name, &val, &len, nullptr, 0);
+ if (ret != 0)
+ return false;
+
+ assert(len == sizeof(val));
+ return val;
+}
+
rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
uint32_t max_packet_size = 128 * 1024; // 128 KiB is a reasonable max packet
// size--debugger can always use less
@@ -3505,6 +3522,9 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
reply << "SupportedWatchpointTypes=x86_64;";
#endif
+ if (supports_memory_tagging())
+ reply << "memory-tagging+;";
+
return SendPacket(reply.str().c_str());
}
@@ -4251,7 +4271,6 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
is in unmapped memory
Region lookup cannot be performed on this platform or process is not
yet launched
- This packet isn't implemented
Examples of use:
qMemoryRegionInfo:3a55140
@@ -4303,6 +4322,16 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
ostrm << 'x';
ostrm << ';';
+ if (!region_info.flags.empty()) {
+ ostrm << "flags:";
+ for (size_t i = 0; i < region_info.flags.size(); i++) {
+ if (i != 0)
+ ostrm << " "; // Separator is whitespace
+ ostrm << region_info.flags[i];
+ }
+ ostrm << ";";
+ }
+
ostrm << "dirty-pages:";
if (region_info.dirty_pages.size() > 0) {
bool first = true;
@@ -4327,6 +4356,62 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
return SendPacket(ostrm.str());
}
+// qMemTags:<hex address>,<hex length>:<hex type>
+rnb_err_t RNBRemote::HandlePacket_qMemTags(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ StdStringExtractor packet(p);
+ packet.SetFilePos(strlen("qMemTags:"));
+
+ // Address
+ nub_addr_t addr =
+ packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS);
+ if (addr == INVALID_NUB_ADDRESS)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing address in qMemTags packet");
+ // ,
+ if (packet.GetChar() != ',')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+ // Length
+ uint64_t length = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0);
+ if (length == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing length in qMemTags packet");
+ // :
+ if (packet.GetChar() != ':')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+ // Type
+ // On the LLDB side this is a `int32_t` serialized as (unsigned) hex, which
+ // means negative values will show up as large positive values here. Right
+ // now, we only support MTE (type 1), so we can ignore this complication.
+ uint32_t type = packet.GetHexMaxU32(StdStringExtractor::BigEndian, 0);
+ if (type != 1 /* MTE */)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing type in qMemTags packet, "
+ "only MTE (type 1) is supported");
+ // <EOF>
+ if (packet.GetBytesLeft() != 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+
+ std::vector<uint8_t> tags;
+ bool ok = DNBProcessGetMemoryTags(pid, addr, length, tags);
+ if (!ok)
+ return SendErrorPacket("E91");
+
+ std::ostringstream ostrm;
+ ostrm << "m"; // Multi part replies
+ for (uint8_t tag : tags) {
+ ostrm << RAWHEX8(tag); // 2 hex chars per tag
+ }
+
+ return SendPacket(ostrm.str());
+}
+
// qGetProfileData;scan_type:0xYYYYYYY
rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) {
nub_process_t pid = m_ctx.ProcessID();
@@ -6162,6 +6247,21 @@ GetCPUTypesFromHost(nub_process_t pid) {
return {cputype, cpusubtype};
}
+static bool ProcessRunningWithMemoryTagging(pid_t pid) {
+#if __has_include(<os/security_config.h>)
+ if (__builtin_available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0,
+ visionOS 26.0, driverkit 25.0, *)) {
+ os_security_config_t config;
+ int ret = ::os_security_config_get_for_proc(pid, &config);
+ if (ret != 0)
+ return false;
+
+ return (config & OS_SECURITY_CONFIG_MTE);
+ }
+#endif
+ return false;
+}
+
// Note that all numeric values returned by qProcessInfo are hex encoded,
// including the pid and the cpu type.
@@ -6338,6 +6438,9 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) {
rep << "vendor:apple;";
+ if (ProcessRunningWithMemoryTagging(pid))
+ rep << "mte:enabled;";
+
#if defined(__LITTLE_ENDIAN__)
rep << "endian:little;";
#elif defined(__BIG_ENDIAN__)
diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h
index ad254ae..cf1c978 100644
--- a/lldb/tools/debugserver/source/RNBRemote.h
+++ b/lldb/tools/debugserver/source/RNBRemote.h
@@ -121,6 +121,7 @@ public:
set_list_threads_in_stop_reply, // 'QListThreadsInStopReply:'
sync_thread_state, // 'QSyncThreadState:'
memory_region_info, // 'qMemoryRegionInfo:'
+ get_memory_tags, // 'qMemTags:'
get_profile_data, // 'qGetProfileData'
set_enable_profiling, // 'QSetEnableAsyncProfiling'
enable_compression, // 'QEnableCompression:'
@@ -237,6 +238,7 @@ public:
rnb_err_t HandlePacket_SaveRegisterState(const char *p);
rnb_err_t HandlePacket_RestoreRegisterState(const char *p);
rnb_err_t HandlePacket_MemoryRegionInfo(const char *p);
+ rnb_err_t HandlePacket_qMemTags(const char *p);
rnb_err_t HandlePacket_GetProfileData(const char *p);
rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p);
rnb_err_t HandlePacket_QEnableCompression(const char *p);