Skip to content

Commit f4784fd

Browse files
authored
[debugserver] Support for qMemTags packet (#160952)
Support for `qMemTags` packet in debugserver which allows usage of LLDB's `memory tag read` on Darwin.
1 parent f4370fb commit f4784fd

File tree

16 files changed

+410
-4
lines changed

16 files changed

+410
-4
lines changed

lldb/packages/Python/lldbsuite/test/cpu_feature.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _is_supported_darwin(self, cmd_runner):
6262
class AArch64:
6363
FPMR = CPUFeature("fpmr")
6464
GCS = CPUFeature("gcs")
65-
MTE = CPUFeature("mte")
65+
MTE = CPUFeature("mte", "hw.optional.arm.FEAT_MTE4")
6666
MTE_STORE_ONLY = CPUFeature("mtestoreonly")
6767
PTR_AUTH = CPUFeature("paca", "hw.optional.arm.FEAT_PAuth2")
6868
SME = CPUFeature("sme", "hw.optional.arm.FEAT_SME")

lldb/test/API/macosx/mte/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
C_SOURCES := main.c
2+
3+
EXE := uaf_mte
4+
5+
all: uaf_mte sign
6+
7+
include Makefile.rules
8+
9+
sign: mte-entitlements.plist uaf_mte
10+
ifeq ($(OS),Darwin)
11+
codesign -s - -f --entitlements $^
12+
endif
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""Test MTE Memory Tagging on Apple platforms"""
2+
3+
import lldb
4+
import re
5+
from lldbsuite.test.decorators import *
6+
from lldbsuite.test.lldbtest import *
7+
from lldbsuite.test import lldbutil
8+
import lldbsuite.test.cpu_feature as cpu_feature
9+
10+
exe_name = "uaf_mte" # Must match Makefile
11+
12+
13+
class TestDarwinMTE(TestBase):
14+
NO_DEBUG_INFO_TESTCASE = True
15+
16+
@skipUnlessFeature(cpu_feature.AArch64.MTE)
17+
def test_tag_fault(self):
18+
self.build()
19+
exe = self.getBuildArtifact(exe_name)
20+
21+
target = self.dbg.CreateTarget(exe)
22+
self.assertTrue(target, VALID_TARGET)
23+
24+
process = target.LaunchSimple(None, None, None)
25+
self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
26+
27+
self.expect(
28+
"thread info",
29+
substrs=[
30+
"stop reason = EXC_ARM_MTE_TAG_FAULT",
31+
"MTE tag mismatch detected",
32+
],
33+
)
34+
35+
@skipUnlessFeature(cpu_feature.AArch64.MTE)
36+
def test_memory_region(self):
37+
self.build()
38+
lldbutil.run_to_source_breakpoint(
39+
self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
40+
)
41+
42+
# (lldb) memory region ptr
43+
# [0x00000001005ec000-0x00000001009ec000) rw-
44+
# memory tagging: enabled
45+
# Modified memory (dirty) page list provided, 2 entries.
46+
# Dirty pages: 0x1005ec000, 0x1005fc000.
47+
self.expect("memory region ptr", substrs=["memory tagging: enabled"])
48+
49+
@skipUnlessFeature(cpu_feature.AArch64.MTE)
50+
def test_memory_read_with_tags(self):
51+
self.build()
52+
lldbutil.run_to_source_breakpoint(
53+
self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
54+
)
55+
56+
# (lldb) memory read ptr-16 ptr+48 --show-tags
57+
# 0x7d2c00930: 00 00 00 00 00 00 00 00 d0 e3 a5 0a 02 00 00 00 ................ (tag: 0x3)
58+
# 0x7d2c00940: 48 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 Hello........... (tag: 0xb)
59+
# 0x7d2c00950: 57 6f 72 6c 64 00 00 00 00 00 00 00 00 00 00 00 World........... (tag: 0xb)
60+
# 0x7d2c00960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ (tag: 0x9)
61+
self.expect(
62+
"memory read ptr-16 ptr+48 --show-tags",
63+
substrs=[" Hello...........", " World..........."],
64+
patterns=[r"(.*\(tag: 0x[0-9a-f]\)\n){4}"],
65+
)
66+
67+
def _parse_pointer_tag(self, output):
68+
return re.search(r"Logical tag: (0x[0-9a-f])", output).group(1)
69+
70+
def _parse_memory_tags(self, output, expected_tag_count):
71+
tags = re.findall(r"\): (0x[0-9a-f])", output)
72+
self.assertEqual(len(tags), expected_tag_count)
73+
return tags
74+
75+
@skipUnlessFeature(cpu_feature.AArch64.MTE)
76+
def test_memory_tag_read(self):
77+
self.build()
78+
lldbutil.run_to_source_breakpoint(
79+
self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
80+
)
81+
82+
# (lldb) memory tag read ptr-1 ptr+33
83+
# Logical tag: 0x5
84+
# Allocation tags:
85+
# [0x100a65a40, 0x100a65a50): 0xf (mismatch)
86+
# [0x100a65a50, 0x100a65a60): 0x5
87+
# [0x100a65a60, 0x100a65a70): 0x5
88+
# [0x100a65a70, 0x100a65a80): 0x2 (mismatch)
89+
self.expect(
90+
"memory tag read ptr-1 ptr+33",
91+
substrs=["Logical tag: 0x", "Allocation tags:", "(mismatch)"],
92+
patterns=[r"(\[.*\): 0x[0-9a-f].*\n){4}"],
93+
)
94+
output = self.res.GetOutput()
95+
self.assertEqual(output.count("(mismatch)"), 2)
96+
ptr_tag = self._parse_pointer_tag(output)
97+
tags = self._parse_memory_tags(output, 4)
98+
self.assertEqual(tags[1], ptr_tag)
99+
self.assertEqual(tags[2], ptr_tag)
100+
self.assertNotEqual(tags[0], ptr_tag) # Memory that comes before/after
101+
self.assertNotEqual(tags[3], ptr_tag) # allocation has different tag.
102+
103+
# Continue running until MTE fault
104+
self.expect("process continue", substrs=["stop reason = EXC_ARM_MTE_TAG_FAULT"])
105+
106+
self.runCmd("memory tag read ptr-1 ptr+33")
107+
output = self.res.GetOutput()
108+
self.assertEqual(output.count("(mismatch)"), 4)
109+
tags = self._parse_memory_tags(output, 4)
110+
self.assertTrue(all(t != ptr_tag for t in tags))

lldb/test/API/macosx/mte/main.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <malloc/malloc.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
// Produce some names on the trace
7+
const size_t tag_granule = 16;
8+
static uint8_t *my_malloc(void) { return malloc(2 * tag_granule); }
9+
static uint8_t *allocate(void) { return my_malloc(); }
10+
11+
static void my_free(void *ptr) { free(ptr); }
12+
static void deallocate(void *ptr) { my_free(ptr); }
13+
14+
static void touch_memory(uint8_t *ptr) { ptr[7] = 1; } // invalid access
15+
static void modify(uint8_t *ptr) { touch_memory(ptr); }
16+
17+
int main() {
18+
uint8_t *ptr = allocate();
19+
20+
strncpy((char *)ptr, "Hello", 16);
21+
strncpy((char *)ptr + 16, "World", 16);
22+
23+
deallocate(ptr); // before free
24+
25+
modify(ptr); // use-after-free
26+
27+
return 0;
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.hardened-process</key>
6+
<true/>
7+
<key>com.apple.security.hardened-process.checked-allocations</key>
8+
<true/>
9+
</dict>
10+
</plist>

lldb/tools/debugserver/source/DNB.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,16 @@ int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
13861386
return -1;
13871387
}
13881388

1389+
nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,
1390+
nub_size_t size,
1391+
std::vector<uint8_t> &tags) {
1392+
MachProcessSP procSP;
1393+
if (GetProcessSP(pid, procSP))
1394+
return procSP->Task().GetMemoryTags(addr, size, tags);
1395+
1396+
return false;
1397+
}
1398+
13891399
std::string DNBProcessGetProfileData(nub_process_t pid,
13901400
DNBProfileDataScanType scanType) {
13911401
MachProcessSP procSP;

lldb/tools/debugserver/source/DNB.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid,
105105
nub_addr_t addr) DNB_EXPORT;
106106
int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
107107
DNBRegionInfo *region_info) DNB_EXPORT;
108+
nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,
109+
nub_size_t size,
110+
std::vector<uint8_t> &tags) DNB_EXPORT;
108111
std::string
109112
DNBProcessGetProfileData(nub_process_t pid,
110113
DNBProfileDataScanType scanType) DNB_EXPORT;

lldb/tools/debugserver/source/DNBDefs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,11 @@ struct DNBExecutableImageInfo {
358358
struct DNBRegionInfo {
359359
public:
360360
DNBRegionInfo()
361-
: addr(0), size(0), permissions(0), dirty_pages(), vm_types() {}
361+
: addr(0), size(0), permissions(0), flags(), dirty_pages(), vm_types() {}
362362
nub_addr_t addr;
363363
nub_addr_t size;
364364
uint32_t permissions;
365+
std::vector<std::string> flags;
365366
std::vector<nub_addr_t> dirty_pages;
366367
std::vector<std::string> vm_types;
367368
};

lldb/tools/debugserver/source/MacOSX/MachTask.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class MachTask {
5656
nub_size_t ReadMemory(nub_addr_t addr, nub_size_t size, void *buf);
5757
nub_size_t WriteMemory(nub_addr_t addr, nub_size_t size, const void *buf);
5858
int GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info);
59+
nub_bool_t GetMemoryTags(nub_addr_t addr, nub_size_t size,
60+
std::vector<uint8_t> &tags);
5961
std::string GetProfileData(DNBProfileDataScanType scanType);
6062

6163
nub_addr_t AllocateMemory(nub_size_t size, uint32_t permissions);

lldb/tools/debugserver/source/MacOSX/MachTask.mm

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@
229229
return ret;
230230
}
231231

232+
//----------------------------------------------------------------------
233+
// MachTask::GetMemoryTags
234+
//----------------------------------------------------------------------
235+
nub_bool_t MachTask::GetMemoryTags(nub_addr_t addr, nub_size_t size,
236+
std::vector<uint8_t> &tags) {
237+
task_t task = TaskPort();
238+
if (task == TASK_NULL)
239+
return false;
240+
241+
bool ok = m_vm_memory.GetMemoryTags(task, addr, size, tags);
242+
DNBLogThreadedIf(LOG_MEMORY, "MachTask::GetMemoryTags ( addr = 0x%8.8llx, "
243+
"size = 0x%8.8llx ) => %s ( tag count = %llu)",
244+
(uint64_t)addr, (uint64_t)size, (ok ? "ok" : "err"),
245+
(uint64_t)tags.size());
246+
return ok;
247+
}
248+
232249
#define TIME_VALUE_TO_TIMEVAL(a, r) \
233250
do { \
234251
(r)->tv_sec = (a)->seconds; \

0 commit comments

Comments
 (0)