Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class HostInfoMacOSX : public HostInfoPosix {
static bool SharedCacheIndexFiles(FileSpec &filepath, UUID &uuid,
lldb::SymbolSharedCacheUse sc_mode);

/// Check whether a bundle at the given path has a valid code signature that
/// chains to a trusted anchor in the system trust store.
static bool IsBundleCodeSignTrusted(const FileSpec &bundle_path);

protected:
static bool ComputeSupportExeDirectory(FileSpec &file_spec);
static void ComputeHostArchitectureSupport(ArchSpec &arch_32,
Expand Down
5 changes: 5 additions & 0 deletions lldb/include/lldb/Target/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ class Platform : public PluginInterface {
FileSpec module_spec,
const Target &target);

/// Returns true if the module's symbol file (e.g. a dSYM bundle) is
/// code-signed with a trusted signature. Used to decide whether to
/// auto-loaded scripts.
virtual bool IsSymbolFileTrusted(Module &module);

/// \param[in] module_spec
/// The ModuleSpec of a binary to find.
///
Expand Down
3 changes: 2 additions & 1 deletion lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ enum InlineStrategy {
enum LoadScriptFromSymFile {
eLoadScriptFromSymFileTrue,
eLoadScriptFromSymFileFalse,
eLoadScriptFromSymFileWarn
eLoadScriptFromSymFileWarn,
eLoadScriptFromSymFileTrusted,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to different/more specific naming. I went with "trusted" to cover both the code sign scenario as well as the concept of having default trusted paths, but we could certainly separate the two and have something like eLoadScriptFromSymFileSigned and eLoadScriptFromSymFileStandardLocation or something.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to distinguish between "system trusted" binaries and 3rd party/ad-hoc signed binaries ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re separate: Are there systems/circumstances where a user might want both Signed and StandardLocation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is starting to feel more like a bit mask. I might want "trusted and no others" or "trusted and warn about the others" for instance.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@medismailben Ad-hoc signed means nothing, so I don't think that's worth having as an option. In theory I can see value in distinguishing between Apple signed vs trusted 3rd party signed, but in practice I think it's simpler to trust what the system is trusting, which is what's currently implemented.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimingham Flags would be nice, but AFAIK there's no way to specify flags as a setting.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we simulate it why an array/list of enum values?

};

enum LoadCWDlldbinitFile {
Expand Down
18 changes: 12 additions & 6 deletions lldb/source/Core/ModuleList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1376,16 +1376,22 @@ bool ModuleList::LoadScriptingResourceInTargetForModule(Module &module,
debugger.ReportWarning(feedback_stream.GetString().str(), debugger.GetID());

for (const auto &[scripting_fspec, load_style] : file_specs) {
if (load_style == eLoadScriptFromSymFileFalse)
continue;

if (!FileSystem::Instance().Exists(scripting_fspec))
continue;

if (load_style == eLoadScriptFromSymFileWarn) {
// clang-format off
switch (load_style) {
case eLoadScriptFromSymFileFalse:
continue;
case eLoadScriptFromSymFileTrue:
break;
case eLoadScriptFromSymFileTrusted:
if (!platform_sp->IsSymbolFileTrusted(module))
continue;
break;
case eLoadScriptFromSymFileWarn:
debugger.ReportWarning(
llvm::formatv(
// clang-format off
R"('{0}' contains a debug script. To run this script in this debug session:

command script import "{1}"
Expand All @@ -1394,10 +1400,10 @@ To run all discovered debug scripts in this session:

settings set target.load-script-from-symbol-file true
)",
// clang-format on
module.GetFileSpec().GetFileNameStrippingExtension(),
scripting_fspec.GetPath()),
debugger.GetID());
// clang-format on

return false;
}
Expand Down
4 changes: 3 additions & 1 deletion lldb/source/Host/macosx/objcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

remove_module_flags()
include_directories(..)

find_library(SECURITY_FRAMEWORK Security)

add_lldb_library(lldbHostMacOSXObjCXX NO_PLUGIN_DEPENDENCIES
Host.mm
HostInfoMacOSX.mm
Expand All @@ -16,6 +17,7 @@ add_lldb_library(lldbHostMacOSXObjCXX NO_PLUGIN_DEPENDENCIES
LINK_LIBS
lldbUtility
${EXTRA_LIBS}
${SECURITY_FRAMEWORK}
)

target_compile_options(lldbHostMacOSXObjCXX PRIVATE
Expand Down
28 changes: 28 additions & 0 deletions lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <AvailabilityMacros.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#include <Security/Security.h>
#include <mach-o/dyld.h>
#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0
Expand Down Expand Up @@ -1096,3 +1097,30 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)(
uuid, filepath.GetPath());
return false;
}

bool HostInfoMacOSX::IsBundleCodeSignTrusted(const FileSpec &bundle_path) {
std::string path = bundle_path.GetPath();
CFURLRef url = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(path.data()),
path.size(), /*isDirectory=*/true);
if (!url)
return false;
auto url_cleanup = llvm::make_scope_exit([&]() { CFRelease(url); });

SecStaticCodeRef static_code = nullptr;
if (SecStaticCodeCreateWithPath(url, kSecCSDefaultFlags, &static_code) !=
errSecSuccess)
return false;
auto code_cleanup = llvm::make_scope_exit([&]() { CFRelease(static_code); });

// Check that the signature chains to a trusted root CA.
SecRequirementRef requirement = nullptr;
if (SecRequirementCreateWithString(CFSTR("anchor trusted"),
kSecCSDefaultFlags,
&requirement) != errSecSuccess)
return false;
auto req_cleanup = llvm::make_scope_exit([&]() { CFRelease(requirement); });

return SecStaticCodeCheckValidity(static_code, kSecCSDefaultFlags,
requirement) == errSecSuccess;
}
35 changes: 35 additions & 0 deletions lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "llvm/Support/VersionTuple.h"

#if defined(__APPLE__)
#include "lldb/Host/macosx/HostInfoMacOSX.h"
#include <TargetConditionals.h>
#endif

Expand Down Expand Up @@ -295,6 +296,40 @@ PlatformDarwin::LocateExecutableScriptingResourcesForPlatform(
return empty;
}

bool PlatformDarwin::IsSymbolFileTrusted(Module &module) {
#if defined(__APPLE__)
SymbolFile *symfile = module.GetSymbolFile();
if (!symfile)
return false;

ObjectFile *objfile = symfile->GetObjectFile();
if (!objfile)
return false;

std::string symfile_path = objfile->GetFileSpec().GetPath();
llvm::StringRef path_ref(symfile_path);

// Find the .dSYM bundle root from the symfile path, which is typically
// .dSYM/Contents/Resources/DWARF/<name>.
auto pos = path_ref.find(".dSYM/");
if (pos == llvm::StringRef::npos)
return false;

FileSpec bundle_spec(path_ref.substr(0, pos + 5));

if (HostInfoMacOSX::IsBundleCodeSignTrusted(bundle_spec)) {
LLDB_LOG(GetLog(LLDBLog::Modules),
"dSYM bundle '{0}' has valid trusted code signature",
bundle_spec.GetPath());
return true;
}

return false;
#else
return false;
#endif
}

Status PlatformDarwin::ResolveSymbolFile(Target &target,
const ModuleSpec &sym_spec,
FileSpec &sym_file) {
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class PlatformDarwin : public PlatformPOSIX {
LocateExecutableScriptingResourcesForPlatform(
Target *target, Module &module_spec, Stream &feedback_stream) override;

bool IsSymbolFileTrusted(Module &module) override;

Status GetSharedModule(const ModuleSpec &module_spec, Process *process,
lldb::ModuleSP &module_sp,
llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Target/Platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ Status Platform::GetFileWithUUID(const FileSpec &platform_file,
return Status();
}

bool Platform::IsSymbolFileTrusted(Module &module) { return false; }

llvm::SmallDenseMap<FileSpec, LoadScriptFromSymFile>
Platform::LocateExecutableScriptingResourcesFromSafePaths(
Stream &feedback_stream, FileSpec module_spec, const Target &target) {
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4371,6 +4371,12 @@ static constexpr OptionEnumValueElement g_load_script_from_sym_file_values[] = {
"warn",
"Warn about debug scripts inside symbol files but do not load them.",
},
{
eLoadScriptFromSymFileTrusted,
"trusted",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably update (or create) some docs describing how this relates to "safe paths". E.g., symbol files from "safe paths" aren't currently trusted in the sense of this setting. I guess one way we could think about the distinction is that LLDB is willing to consider loading from safe-paths, but the actual loading is condition on the target.load-script-from-symbol-file?

"Load debug scripts inside trusted symbol files, and warn about "
"scripts from untrusted symbol files.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warn setting is rather niche now. Should we consider claiming it deprecated? Unless we want to be able to say "don't load trusted files but do warn". OTOH it doesn't look like much maintenance.

There's some logic in Debugger.cpp that handles reloading of the scripts when the setting gets changed. You might want to add a case for the new enum there too. But feel free to look at that separate from this PR. I'm not sure we have any tests for that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's important to have a "show me all the things you would load but load none of them" mode. That will help people who are getting used to what all the author of the extensions intends to do. Maybe that's the appropriate use of warn?

@Michael137 Michael137 Mar 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that's a good point. Though currently when we warn we bail. We should change that to continue if we want the dry-run semantics. I was planning on doing that for one of my PRs anyway. Happy to keep the warn

},
};

static constexpr OptionEnumValueElement g_load_cwd_lldbinit_values[] = {
Expand Down
10 changes: 6 additions & 4 deletions lldb/source/Target/TargetProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,12 @@ let Definition = "target", Path = "target" in {
def UseFastStepping: Property<"use-fast-stepping", "Boolean">,
DefaultTrue,
Desc<"Use a fast stepping algorithm based on running from branch to branch rather than instruction single-stepping.">;
def LoadScriptFromSymbolFile: Property<"load-script-from-symbol-file", "Enum">,
DefaultEnumValue<"eLoadScriptFromSymFileWarn">,
EnumValues<"OptionEnumValues(g_load_script_from_sym_file_values)">,
Desc<"Allow LLDB to load scripting resources embedded in symbol files when available.">;
def LoadScriptFromSymbolFile
: Property<"load-script-from-symbol-file", "Enum">,
DefaultEnumValue<"eLoadScriptFromSymFileTrusted">,
EnumValues<"OptionEnumValues(g_load_script_from_sym_file_values)">,
Desc<"Allow LLDB to load scripting resources embedded in symbol files "
"when available.">;
def LoadCWDlldbinitFile: Property<"load-cwd-lldbinit", "Enum">,
DefaultEnumValue<"eLoadCWDlldbinitWarn">,
EnumValues<"OptionEnumValues(g_load_cwd_lldbinit_values)">,
Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/macosx/dsym_codesign/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C_SOURCES := main.c

include Makefile.rules
78 changes: 78 additions & 0 deletions lldb/test/API/macosx/dsym_codesign/TestdSYMCodesign.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import os
import shutil
import subprocess

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *


def has_lldb_codesign():
"""Check if the lldb_codesign certificate is available."""
try:
result = subprocess.run(
[
"security",
"find-certificate",
"-c",
"lldb_codesign",
"/Library/Keychains/System.keychain",
],
capture_output=True,
)
return result.returncode == 0
except FileNotFoundError:
return False


@skipUnlessDarwin
class TestdSYMCodesign(TestBase):
NO_DEBUG_INFO_TESTCASE = True
SHARED_BUILD_TESTCASE = False

def build_dsym_with_script(self):
self.build(debug_info="dsym")
exe = self.getBuildArtifact("a.out")
dsym = self.getBuildArtifact("a.out.dSYM")
python_dir = os.path.join(dsym, "Contents", "Resources", "Python")
os.makedirs(python_dir, exist_ok=True)
shutil.copy(
os.path.join(self.getSourceDir(), "dsym_script.py"),
os.path.join(python_dir, "a.py"),
)
return exe, dsym

def test_adhoc_signed_dsym(self):
"""An ad-hoc signed dSYM should not be loaded because the
signature doesn't chain to a trusted root CA."""
exe, dsym = self.build_dsym_with_script()
subprocess.check_call(["codesign", "-f", "-s", "-", dsym])

self.runCmd("settings set target.load-script-from-symbol-file trusted")
self.createTestTarget(file_path=exe)

self.expect(
"script -- print('SENTINEL')",
substrs=["SENTINEL"],
)
# The script should NOT have been loaded.
self.assertFalse(
hasattr(lldb, "_dsym_codesign_test_loaded"),
"Script should not auto-load from ad-hoc signed dSYM",
)

@unittest.skipUnless(has_lldb_codesign(), "requires lldb_codesign certificate")
def test_trusted_signed_dsym_auto_loads(self):
"""A dSYM signed with the trusted lldb_codesign certificate should
auto-load scripts."""
exe, dsym = self.build_dsym_with_script()
subprocess.check_call(["codesign", "-f", "-s", "lldb_codesign", dsym])

self.runCmd("settings set target.load-script-from-symbol-file trusted")
self.createTestTarget(file_path=exe)

# The script sets a marker attribute on the lldb module.
self.assertTrue(
getattr(lldb, "_dsym_codesign_test_loaded", False),
"Script should auto-load from trusted signed dSYM",
)
5 changes: 5 additions & 0 deletions lldb/test/API/macosx/dsym_codesign/dsym_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import lldb


def __lldb_init_module(debugger, internal_dict):
lldb._dsym_codesign_test_loaded = True
1 change: 1 addition & 0 deletions lldb/test/API/macosx/dsym_codesign/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int main(void) { return 0; }
4 changes: 3 additions & 1 deletion llvm/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ Changes to LLDB
* ``SBTarget::GetDataByteSize()``, ``SBTarget::GetCodeByteSize()``, and ``SBSection::GetTargetByteSize()``
have been deprecated. They always return 1, as before.
* A new ``webinspector-wasm`` platform was added to list and attach to WebAssebly processes in Safari.
* The default for `load-script-from-symbol-file` was changed from `warn` to `trusted`. This means that scripts from
code signed dSYM bundles are now loaded automatically, while untrusted bundles continue to produce a warning.

### FreeBSD

Expand All @@ -256,7 +258,7 @@ Changes to LLDB
#### Kernel Debugging

* The plugin that analyzes FreeBSD kernel core dump and live core has been renamed from `freebsd-kernel` to
`freebsd-kernel-core`. Remote kernel debugging is still handled by the `gdb-remote` plugin.
`freebsd-kernel-core`. Remote kernel debugging is still handled by the `gdb-remote` plugin.
* Support for libfbsdvmcore has been removed. As a result, FreeBSD kernel dump debugging is now only
available on FreeBSD hosts. Live kernel debugging through the GDB remote protocol is still available
from any platform.
Expand Down
Loading