-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[debugserver] Support for qMemTags packet
#160952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
e16adb1
0f365cd
99ea474
2b82f3f
5927388
433c820
90b6396
5a87f39
b7c9fc1
ac37406
1e54eb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| """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_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): | ||
| return re.search(r"Logical tag: (0x[0-9a-f])", self.res.GetOutput()).group(1) | ||
|
|
||
| def _parse_memory_tags(self, expected_tag_count): | ||
| tags = re.findall(r"\): (0x[0-9a-f])", self.res.GetOutput()) | ||
| 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}"], | ||
| ) | ||
| self.assertEqual(self.res.GetOutput().count("(mismatch)"), 2) | ||
| ptr_tag = self._parse_pointer_tag() | ||
yln marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| tags = self._parse_memory_tags(4) | ||
| self.assertEqual(tags[1], ptr_tag) | ||
| self.assertEqual(tags[2], ptr_tag) | ||
| self.assertNotEqual(tags[0], ptr_tag) | ||
yln marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| self.assertNotEqual(tags[3], ptr_tag) | ||
|
|
||
| # Continue running until MTE fault | ||
| self.runCmd("process continue") | ||
|
|
||
yln marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self.runCmd("memory tag read ptr-1 ptr+33") | ||
| self.assertEqual(self.res.GetOutput().count("(mismatch)"), 4) | ||
| tags = self._parse_memory_tags(4) | ||
| self.assertTrue(all(t != ptr_tag for t in tags)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #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; | ||
| uint8_t *my_malloc(void) { return malloc(2 * tag_granule); } | ||
yln marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| uint8_t *allocate(void) { return my_malloc(); } | ||
|
|
||
| void my_free(void *ptr) { free(ptr); } | ||
yln marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| void deallocate(void *ptr) { my_free(ptr); } | ||
|
|
||
| void touch_memory(uint8_t *ptr) { ptr[7] = 1; } // invalid access | ||
| void modify(uint8_t *ptr) { touch_memory(ptr); } | ||
|
|
||
| int main() { | ||
| uint8_t *ptr = allocate(); | ||
| printf("ptr: %p\n", ptr); | ||
DavidSpickett marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| strcpy((char *)ptr, "Hello"); | ||
| strcpy((char *)ptr + 16, "World"); | ||
yln marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| deallocate(ptr); // before free | ||
|
|
||
| modify(ptr); // use-after-free | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I'm guessing since you don't have to PROT_MTE like we do on Linux, that tagging is a per process property? Which makes your job significantly easier. |
||
|
|
||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this opts the entire process into tagging, correct?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This enables MTE in processes spawned from this binary in the production config (for security), but it does not mean that all memory of this process has tags. Only memory regions mapped with So the scenario you mentioned above (intermingled tagged and untagged pages) can happen. |
||
| <true/> | ||
| </dict> | ||
| </plist> | ||
Uh oh!
There was an error while loading. Please reload this page.