diff --git a/pkgs/development/compilers/llvm/18/lldb/backport-ParseTrieEntries-fixes.patch b/pkgs/development/compilers/llvm/18/lldb/backport-ParseTrieEntries-fixes.patch new file mode 100644 index 000000000000..f165c7e9ebbe --- /dev/null +++ b/pkgs/development/compilers/llvm/18/lldb/backport-ParseTrieEntries-fixes.patch @@ -0,0 +1,124 @@ +diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +index 2c7005449f..e0c426afa0 100644 +--- a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp ++++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +@@ -137,6 +137,14 @@ + using namespace lldb_private; + using namespace llvm::MachO; + ++/// Upper bound on the length of a symbol name assembled from export-trie edge ++/// labels. A corrupt trie can encode an edge label whose terminator is far ++/// away in the trie data, so a single label is many megabytes long; appending ++/// it to the running name would otherwise request an unbounded allocation. No ++/// legitimate symbol name comes close to this size. Also 1 MiB is the ++/// symbol length limit in ld. ++static constexpr size_t kMaxTrieSymbolNameLength = 1 << 20; // 1 MiB ++ + LLDB_PLUGIN_DEFINE(ObjectFileMachO) + + static void PrintRegisterValue(RegisterContext *reg_ctx, const char *name, +@@ -2050,15 +2058,21 @@ + } + }; + +-static bool ParseTrieEntries(DataExtractor &data, lldb::offset_t offset, +- const bool is_arm, addr_t text_seg_base_addr, +- std::vector &nameSlices, +- std::set &resolver_addresses, +- std::vector &reexports, +- std::vector &ext_symbols) { ++static bool ParseTrieEntriesImpl(DataExtractor &data, lldb::offset_t offset, ++ const bool is_arm, addr_t text_seg_base_addr, ++ std::string &prefix, ++ std::set &resolver_addresses, ++ std::vector &reexports, ++ std::vector &ext_symbols, ++ std::set &visited_nodes) { + if (!data.ValidOffset(offset)) + return true; + ++ // Every node in a well-formed trie is reached by exactly one path, so a node ++ // offset seen twice means the trie is corrupt. ++ if (!visited_nodes.insert(offset).second) ++ return false; ++ + // Terminal node -- end of a branch, possibly add this to + // the symbol table or resolver table. + const uint64_t terminalSize = data.GetULEB128(&offset); +@@ -2098,14 +2112,9 @@ + add_this_entry = true; + } + if (add_this_entry) { +- std::string name; +- if (!nameSlices.empty()) { +- for (auto name_slice : nameSlices) +- name.append(name_slice.data(), name_slice.size()); +- } +- if (name.size() > 1) { ++ if (prefix.size() > 1) { + // Skip the leading '_' +- e.entry.name.SetCStringWithLength(name.c_str() + 1, name.size() - 1); ++ e.entry.name.SetString(llvm::StringRef(prefix).drop_front()); + } + if (import_name) { + // Skip the leading '_' +@@ -2126,23 +2135,36 @@ + const uint8_t childrenCount = data.GetU8(&children_offset); + for (uint8_t i = 0; i < childrenCount; ++i) { + const char *cstr = data.GetCStr(&children_offset); +- if (cstr) +- nameSlices.push_back(llvm::StringRef(cstr)); +- else ++ if (!cstr) + return false; // Corrupt data ++ if (prefix.size() + llvm::StringRef(cstr).size() > kMaxTrieSymbolNameLength) ++ return false; // Corrupt data: implausibly long symbol name. ++ const size_t prevSize = prefix.size(); ++ prefix.append(cstr); + lldb::offset_t childNodeOffset = data.GetULEB128(&children_offset); +- if (childNodeOffset) { +- if (!ParseTrieEntries(data, childNodeOffset, is_arm, text_seg_base_addr, +- nameSlices, resolver_addresses, reexports, +- ext_symbols)) { +- return false; +- } +- } +- nameSlices.pop_back(); ++ // A child offset of 0 points back at the root; like any other repeated ++ // offset it is a cycle, which ParseTrieEntriesImpl rejects as corrupt. ++ if (!ParseTrieEntriesImpl(data, childNodeOffset, is_arm, text_seg_base_addr, ++ prefix, resolver_addresses, reexports, ++ ext_symbols, visited_nodes)) ++ return false; ++ prefix.resize(prevSize); + } + return true; + } + ++static bool ParseTrieEntries( ++ DataExtractor &data, const bool is_arm, lldb::addr_t text_seg_base_addr, ++ std::set &resolver_addresses, ++ std::vector &reexports, ++ std::vector &ext_symbols) { ++ lldb::offset_t offset = 0; ++ std::set visited_nodes; ++ std::string prefix; ++ return ParseTrieEntriesImpl(data, offset, is_arm, text_seg_base_addr, prefix, ++ resolver_addresses, reexports, ext_symbols, ++ visited_nodes); ++} + static SymbolType GetSymbolType(const char *&symbol_name, + bool &demangled_is_synthesized, + const SectionSP &text_section_sp, +@@ -2666,9 +2688,8 @@ + lldb::addr_t text_segment_file_addr = LLDB_INVALID_ADDRESS; + if (text_segment_sp) + text_segment_file_addr = text_segment_sp->GetFileAddress(); +- std::vector nameSlices; +- ParseTrieEntries(dyld_trie_data, 0, is_arm, text_segment_file_addr, +- nameSlices, resolver_addresses, reexport_trie_entries, ++ ParseTrieEntries(dyld_trie_data, is_arm, text_segment_file_addr, ++ resolver_addresses, reexport_trie_entries, + external_sym_trie_entries); + } + diff --git a/pkgs/development/compilers/llvm/22/lldb/backport-ParseTrieEntries-fixes.patch b/pkgs/development/compilers/llvm/22/lldb/backport-ParseTrieEntries-fixes.patch new file mode 100644 index 000000000000..312be42ec2fb --- /dev/null +++ b/pkgs/development/compilers/llvm/22/lldb/backport-ParseTrieEntries-fixes.patch @@ -0,0 +1,125 @@ +diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +index 0be7b4c6f8..f9d9a05920 100644 +--- a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp ++++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +@@ -137,6 +137,14 @@ + static constexpr llvm::StringLiteral g_loader_path = "@loader_path"; + static constexpr llvm::StringLiteral g_executable_path = "@executable_path"; + ++/// Upper bound on the length of a symbol name assembled from export-trie edge ++/// labels. A corrupt trie can encode an edge label whose terminator is far ++/// away in the trie data, so a single label is many megabytes long; appending ++/// it to the running name would otherwise request an unbounded allocation. No ++/// legitimate symbol name comes close to this size. Also 1 MiB is the ++/// symbol length limit in ld. ++static constexpr size_t kMaxTrieSymbolNameLength = 1 << 20; // 1 MiB ++ + LLDB_PLUGIN_DEFINE(ObjectFileMachO) + + static void PrintRegisterValue(RegisterContext *reg_ctx, const char *name, +@@ -1994,15 +2002,21 @@ + } + }; + +-static bool ParseTrieEntries(DataExtractor &data, lldb::offset_t offset, +- const bool is_arm, addr_t text_seg_base_addr, +- std::vector &nameSlices, +- std::set &resolver_addresses, +- std::vector &reexports, +- std::vector &ext_symbols) { ++static bool ParseTrieEntriesImpl(DataExtractor &data, lldb::offset_t offset, ++ const bool is_arm, addr_t text_seg_base_addr, ++ std::string &prefix, ++ std::set &resolver_addresses, ++ std::vector &reexports, ++ std::vector &ext_symbols, ++ std::set &visited_nodes) { + if (!data.ValidOffset(offset)) + return true; + ++ // Every node in a well-formed trie is reached by exactly one path, so a node ++ // offset seen twice means the trie is corrupt. ++ if (!visited_nodes.insert(offset).second) ++ return false; ++ + // Terminal node -- end of a branch, possibly add this to + // the symbol table or resolver table. + const uint64_t terminalSize = data.GetULEB128(&offset); +@@ -2042,14 +2056,9 @@ + add_this_entry = true; + } + if (add_this_entry) { +- std::string name; +- if (!nameSlices.empty()) { +- for (auto name_slice : nameSlices) +- name.append(name_slice.data(), name_slice.size()); +- } +- if (name.size() > 1) { ++ if (prefix.size() > 1) { + // Skip the leading '_' +- e.entry.name.SetCStringWithLength(name.c_str() + 1, name.size() - 1); ++ e.entry.name.SetString(llvm::StringRef(prefix).drop_front()); + } + if (import_name) { + // Skip the leading '_' +@@ -2070,23 +2079,37 @@ + const uint8_t childrenCount = data.GetU8(&children_offset); + for (uint8_t i = 0; i < childrenCount; ++i) { + const char *cstr = data.GetCStr(&children_offset); +- if (cstr) +- nameSlices.push_back(llvm::StringRef(cstr)); +- else ++ if (!cstr) + return false; // Corrupt data ++ if (prefix.size() + llvm::StringRef(cstr).size() > kMaxTrieSymbolNameLength) ++ return false; // Corrupt data: implausibly long symbol name. ++ const size_t prevSize = prefix.size(); ++ prefix.append(cstr); + lldb::offset_t childNodeOffset = data.GetULEB128(&children_offset); +- if (childNodeOffset) { +- if (!ParseTrieEntries(data, childNodeOffset, is_arm, text_seg_base_addr, +- nameSlices, resolver_addresses, reexports, +- ext_symbols)) { +- return false; +- } +- } +- nameSlices.pop_back(); ++ // A child offset of 0 points back at the root; like any other repeated ++ // offset it is a cycle, which ParseTrieEntriesImpl rejects as corrupt. ++ if (!ParseTrieEntriesImpl(data, childNodeOffset, is_arm, text_seg_base_addr, ++ prefix, resolver_addresses, reexports, ++ ext_symbols, visited_nodes)) ++ return false; ++ prefix.resize(prevSize); + } + return true; + } + ++static bool ParseTrieEntries( ++ DataExtractor &data, const bool is_arm, lldb::addr_t text_seg_base_addr, ++ std::set &resolver_addresses, ++ std::vector &reexports, ++ std::vector &ext_symbols) { ++ lldb::offset_t offset = 0; ++ std::set visited_nodes; ++ std::string prefix; ++ return ParseTrieEntriesImpl(data, offset, is_arm, text_seg_base_addr, prefix, ++ resolver_addresses, reexports, ext_symbols, ++ visited_nodes); ++} ++ + static bool + TryParseV2ObjCMetadataSymbol(const char *&symbol_name, + const char *&symbol_name_non_abi_mangled, +@@ -2659,9 +2682,8 @@ + lldb::addr_t text_segment_file_addr = LLDB_INVALID_ADDRESS; + if (text_segment_sp) + text_segment_file_addr = text_segment_sp->GetFileAddress(); +- std::vector nameSlices; +- ParseTrieEntries(dyld_trie_data, 0, is_arm, text_segment_file_addr, +- nameSlices, resolver_addresses, reexport_trie_entries, ++ ParseTrieEntries(dyld_trie_data, is_arm, text_segment_file_addr, ++ resolver_addresses, reexport_trie_entries, + external_sym_trie_entries); + } + diff --git a/pkgs/development/compilers/llvm/common/lldb/default.nix b/pkgs/development/compilers/llvm/common/lldb/default.nix index e146f1d896b8..8bca8f4edc1f 100644 --- a/pkgs/development/compilers/llvm/common/lldb/default.nix +++ b/pkgs/development/compilers/llvm/common/lldb/default.nix @@ -77,6 +77,10 @@ stdenv.mkDerivation ( # Fix build with gcc15 # https://github.com/llvm/llvm-project/commit/bb59f04e7e75dcbe39f1bf952304a157f0035314 ./lldb-add-include-cstdint.patch + ] + ++ lib.optionals (lib.versionOlder (lib.versions.major release_version) "23") [ + # Backports several fixes to export trie parsing. Otherwise, LLDB crashes when starting a debugging session on macOS 27. + (getVersionFile "lldb/backport-ParseTrieEntries-fixes.patch") ]; nativeBuildInputs = [ diff --git a/pkgs/development/compilers/llvm/common/patches.nix b/pkgs/development/compilers/llvm/common/patches.nix index 5ea9faa3d2e2..a2ecc75785c9 100644 --- a/pkgs/development/compilers/llvm/common/patches.nix +++ b/pkgs/development/compilers/llvm/common/patches.nix @@ -20,6 +20,17 @@ path = ../18; } ]; + "lldb/backport-ParseTrieEntries-fixes.patch" = [ + { + before = "22"; + path = ../18; + } + { + after = "22"; + before = "23"; + path = ../22; + } + ]; "llvm/backport-darwin-triple-parsing.patch" = [ { after = "18";