From 089e258c18998a15778c175de9c5b3f0c72b0cdc Mon Sep 17 00:00:00 2001 From: Julian Lettner Date: Thu, 14 Nov 2024 15:43:24 -0800 Subject: [PATCH 1/2] [lldb][Darwin] Add `process launch --memory-tagging` option For debugging and bug-finding workflows on Darwin, support launching processes with memory tagging for binaries that are not entitled. This will cause the process to behave as if the binary was entitled with: ``` com.apple.security.hardened-process.checked-allocations ``` This has no effect on hardware without MTE support. Co-authored-by: Jonas Devlieghere --- lldb/include/lldb/lldb-enumerations.h | 2 ++ lldb/source/Commands/CommandObjectProcess.cpp | 3 ++ .../Commands/CommandOptionsProcessLaunch.cpp | 4 +++ .../Commands/CommandOptionsProcessLaunch.h | 2 ++ lldb/source/Commands/Options.td | 5 +++ lldb/source/Host/macosx/objcxx/Host.mm | 33 +++++++++++++++++++ lldb/test/API/macosx/mte/Makefile | 9 +++-- lldb/test/API/macosx/mte/TestDarwinMTE.py | 14 +++++++- 8 files changed, 68 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index fec9fdef44df9..1a7db8faecd94 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -130,6 +130,8 @@ FLAGS_ENUM(LaunchFlags){ eLaunchFlagInheritTCCFromParent = (1u << 12), ///< Don't make the inferior responsible for its own TCC ///< permissions but instead inherit them from its parent. + eLaunchFlagMemoryTagging = + (1u << 13), ///< Launch process with memory tagging explicitly enabled. }; /// Thread Run Modes. diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 7d326404a5503..cce0a380479f7 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -181,6 +181,9 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach { disable_aslr = target->GetDisableASLR(); } + if (m_options.memory_tagging == eLazyBoolYes) + m_options.launch_info.GetFlags().Set(eLaunchFlagMemoryTagging); + if (!m_class_options.GetName().empty()) { m_options.launch_info.SetProcessPluginName("ScriptedProcess"); ScriptedMetadataSP metadata_sp = std::make_shared( diff --git a/lldb/source/Commands/CommandOptionsProcessLaunch.cpp b/lldb/source/Commands/CommandOptionsProcessLaunch.cpp index 21d94d68ceb91..bb20bbbe2e8f7 100644 --- a/lldb/source/Commands/CommandOptionsProcessLaunch.cpp +++ b/lldb/source/Commands/CommandOptionsProcessLaunch.cpp @@ -127,6 +127,10 @@ Status CommandOptionsProcessLaunch::SetOptionValue( break; } + case 'M': + memory_tagging = true; + break; + case 'c': if (!option_arg.empty()) launch_info.SetShell(FileSpec(option_arg)); diff --git a/lldb/source/Commands/CommandOptionsProcessLaunch.h b/lldb/source/Commands/CommandOptionsProcessLaunch.h index 7ab7fabe10503..640495422a694 100644 --- a/lldb/source/Commands/CommandOptionsProcessLaunch.h +++ b/lldb/source/Commands/CommandOptionsProcessLaunch.h @@ -34,6 +34,7 @@ class CommandOptionsProcessLaunch : public lldb_private::OptionGroup { lldb_private::ExecutionContext *execution_context) override { launch_info.Clear(); disable_aslr = lldb_private::eLazyBoolCalculate; + memory_tagging = false; } llvm::ArrayRef GetDefinitions() override; @@ -42,6 +43,7 @@ class CommandOptionsProcessLaunch : public lldb_private::OptionGroup { lldb_private::ProcessLaunchInfo launch_info; lldb_private::LazyBool disable_aslr; + bool memory_tagging; }; // CommandOptionsProcessLaunch } // namespace lldb_private diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 595b3d08abec5..a9f054e1d3d45 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1173,6 +1173,11 @@ let Command = "process launch" in { Arg<"Boolean">, Desc<"Set whether to shell expand arguments to the process when " "launching.">; + def process_launch_memory_tagging + : Option<"memory-tagging", "M">, + Desc<"Set whether to explicitly enable memory tagging when launching " + "the process. Requires hardware support. " + "(Only supported on Darwin.)">; } let Command = "process attach" in { diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index 3c1d1179963d2..71208927e7c94 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1210,6 +1210,39 @@ static Status LaunchProcessPosixSpawn(const char *exe_path, } } + if (launch_info.GetFlags().Test(eLaunchFlagMemoryTagging)) { + // The following function configures the spawn attributes to launch the + // process with memory tagging explicitly enabled. We look it up + // dynamically since it is only available on newer OS. Does nothing on + // hardware which does not support MTE. + // + // int posix_spawnattr_set_use_sec_transition_shims_np( + // posix_spawnattr_t *attr, uint32_t flags); + // + using posix_spawnattr_set_use_sec_transition_shims_np_t = + int (*)(posix_spawnattr_t *attr, uint32_t flags); + auto posix_spawnattr_set_use_sec_transition_shims_np_fn = + (posix_spawnattr_set_use_sec_transition_shims_np_t)dlsym( + RTLD_DEFAULT, "posix_spawnattr_set_use_sec_transition_shims_np"); + if (posix_spawnattr_set_use_sec_transition_shims_np_fn) { + error = + Status(posix_spawnattr_set_use_sec_transition_shims_np_fn(&attr, 0), + eErrorTypePOSIX); + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, " + "posix_spawnattr_set_use_sec_transition_shims_np(&attr, 0)", + error); + return error; + } + } else { + LLDB_LOG(log, + "error: posix_spawnattr_set_use_sec_transition_shims_np not " + "available", + error); + } + } + // Don't set the binpref if a shell was provided. After all, that's only // going to affect what version of the shell is launched, not what fork of // the binary is launched. We insert "arch --arch as part of the diff --git a/lldb/test/API/macosx/mte/Makefile b/lldb/test/API/macosx/mte/Makefile index cb20942805e2a..d614e0f0a3bcd 100644 --- a/lldb/test/API/macosx/mte/Makefile +++ b/lldb/test/API/macosx/mte/Makefile @@ -1,12 +1,15 @@ C_SOURCES := main.c -EXE := uaf_mte +EXE := uaf -all: uaf_mte sign +binary-plain: uaf +binary-entitled: uaf sign + +all: binary-entitled include Makefile.rules -sign: mte-entitlements.plist uaf_mte +sign: mte-entitlements.plist uaf 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 index ef858b1fc2710..0454b971e8d0a 100644 --- a/lldb/test/API/macosx/mte/TestDarwinMTE.py +++ b/lldb/test/API/macosx/mte/TestDarwinMTE.py @@ -7,12 +7,24 @@ from lldbsuite.test import lldbutil import lldbsuite.test.cpu_feature as cpu_feature -exe_name = "uaf_mte" # Must match Makefile +exe_name = "uaf" # Must match Makefile class TestDarwinMTE(TestBase): NO_DEBUG_INFO_TESTCASE = True + @skipUnlessFeature(cpu_feature.AArch64.MTE) + def test_process_launch_memory_tagging(self): + self.build(make_targets=["binary-plain"]) + self.createTestTarget(self.getBuildArtifact(exe_name)) + + self.expect("process launch", substrs=["exited with status = 0"]) + + self.expect( + "process launch --memory-tagging", + substrs=["stopped", "stop reason = EXC_ARM_MTE_TAG_FAULT"], + ) + @skipUnlessFeature(cpu_feature.AArch64.MTE) def test_tag_fault(self): self.build() From 18dca9857167a984db4d181fb18b6820794cf538 Mon Sep 17 00:00:00 2001 From: Julian Lettner Date: Fri, 10 Oct 2025 16:41:43 -0700 Subject: [PATCH 2/2] [lldb][Darwin] Directly set memory_tagging in launch info flags Directly set `memory_tagging` in launch info flags eliminating the need for a dedicated member in `CommandOptionsProcessLaunch`. --- lldb/source/Commands/CommandObjectProcess.cpp | 3 --- lldb/source/Commands/CommandOptionsProcessLaunch.cpp | 2 +- lldb/source/Commands/CommandOptionsProcessLaunch.h | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index cce0a380479f7..7d326404a5503 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -181,9 +181,6 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach { disable_aslr = target->GetDisableASLR(); } - if (m_options.memory_tagging == eLazyBoolYes) - m_options.launch_info.GetFlags().Set(eLaunchFlagMemoryTagging); - if (!m_class_options.GetName().empty()) { m_options.launch_info.SetProcessPluginName("ScriptedProcess"); ScriptedMetadataSP metadata_sp = std::make_shared( diff --git a/lldb/source/Commands/CommandOptionsProcessLaunch.cpp b/lldb/source/Commands/CommandOptionsProcessLaunch.cpp index bb20bbbe2e8f7..8ae20bd76f456 100644 --- a/lldb/source/Commands/CommandOptionsProcessLaunch.cpp +++ b/lldb/source/Commands/CommandOptionsProcessLaunch.cpp @@ -128,7 +128,7 @@ Status CommandOptionsProcessLaunch::SetOptionValue( } case 'M': - memory_tagging = true; + launch_info.GetFlags().Set(eLaunchFlagMemoryTagging); break; case 'c': diff --git a/lldb/source/Commands/CommandOptionsProcessLaunch.h b/lldb/source/Commands/CommandOptionsProcessLaunch.h index 640495422a694..7ab7fabe10503 100644 --- a/lldb/source/Commands/CommandOptionsProcessLaunch.h +++ b/lldb/source/Commands/CommandOptionsProcessLaunch.h @@ -34,7 +34,6 @@ class CommandOptionsProcessLaunch : public lldb_private::OptionGroup { lldb_private::ExecutionContext *execution_context) override { launch_info.Clear(); disable_aslr = lldb_private::eLazyBoolCalculate; - memory_tagging = false; } llvm::ArrayRef GetDefinitions() override; @@ -43,7 +42,6 @@ class CommandOptionsProcessLaunch : public lldb_private::OptionGroup { lldb_private::ProcessLaunchInfo launch_info; lldb_private::LazyBool disable_aslr; - bool memory_tagging; }; // CommandOptionsProcessLaunch } // namespace lldb_private