https://github.com/qiyao created
https://github.com/llvm/llvm-project/pull/207448
`ObjectFileMachO::ParseSymtab`, when handling a local shared cache image
(`MH_DYLIB_IN_CACHE` set and not read from memory), unconditionally called
`linkedit_section_sp->GetFileOffset()` to slide the load command offsets.
If the Mach-O has no `__LINKEDIT` segment, `FindSectionByName` returns a
`null` SectionSP, and this is a member call on a null pointer (caught by UBSan;
crashes in release builds).
```
* thread #1, queue = 'com.apple.main-thread', stop reason =
EXC_BAD_ACCESS (code=1, address=0x68)
* frame #0: 0x0000000100121ff0
ObjectFileMachOTests`lldb_private::Section::GetFileOffset(this=0x0000000000000000)
const at Section.h:181:49 [opt] [inlined]
frame #1: 0x0000000100121ff0
ObjectFileMachOTests`ObjectFileMachO::ParseSymtab(this=0x00000001055616b0,
symtab=
0x000000016fdfe988) at ObjectFileMachO.cpp:2353:59 [opt]
```
Guard the shared-cache slide branch with a null check on
`linkedit_section_sp`, matching the existing guard used on the in-memory
path.
Found by lldb-target-fuzzer.
>From 314ffe34bcadc3e81a55198cc9ea8fbee99fd4d7 Mon Sep 17 00:00:00 2001
From: Yao Qi <[email protected]>
Date: Fri, 3 Jul 2026 17:17:54 +0100
Subject: [PATCH] [lldb][Mach-O] Fix null __LINKEDIT deref in ParseSymtab for
shared cache images
`ObjectFileMachO::ParseSymtab`, when handling a local shared cache image
(`MH_DYLIB_IN_CACHE` set and not read from memory), unconditionally called
`linkedit_section_sp->GetFileOffset()` to slide the load command offsets.
If the Mach-O has no `__LINKEDIT` segment, `FindSectionByName` returns a
`null` SectionSP, and this is a member call on a null pointer (caught by UBSan;
crashes in release builds).
```
* thread #1, queue = 'com.apple.main-thread', stop reason =
EXC_BAD_ACCESS (code=1, address=0x68)
* frame #0: 0x0000000100121ff0
ObjectFileMachOTests`lldb_private::Section::GetFileOffset(this=0x0000000000000000)
const at Section.h:181:49 [opt] [inlined]
frame #1: 0x0000000100121ff0
ObjectFileMachOTests`ObjectFileMachO::ParseSymtab(this=0x00000001055616b0,
symtab=
0x000000016fdfe988) at ObjectFileMachO.cpp:2353:59 [opt]
```
Guard the shared-cache slide branch with a null check on
`linkedit_section_sp`, matching the existing guard used on the in-memory
path.
Found by lldb-target-fuzzer.
---
.../ObjectFile/Mach-O/ObjectFileMachO.cpp | 2 +-
.../ObjectFile/MachO/TestObjectFileMachO.cpp | 57 +++++++++++++++++++
2 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index 327c4dc431fb0..9566d54e9f81a 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -2342,7 +2342,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
}
}
} else {
- if (is_local_shared_cache_image) {
+ if (is_local_shared_cache_image && linkedit_section_sp) {
// The load commands in shared cache images are relative to the
// beginning of the shared cache, not the library image. The
// data we get handed when creating the ObjectFileMachO starts
diff --git a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
index 147ea55e85efa..700acbebb64a0 100644
--- a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
+++ b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
@@ -15,6 +15,10 @@
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/lldb-defines.h"
#include "gtest/gtest.h"
@@ -107,3 +111,56 @@ TEST_F(ObjectFileMachOTest,
IndirectSymbolsInTheSharedCache) {
OF->ParseSymtab(symtab);
}
#endif
+
+// Regression fixture: a Mach-O whose MH_DYLIB_IN_CACHE flag is set but which
+// has no __LINKEDIT segment.
+TEST_F(ObjectFileMachOTest, ParseSymtabSharedCacheMissingLinkedit) {
+ // Minimal little-endian x86_64 Mach-O flagged MH_DYLIB_IN_CACHE, containing
+ // a __TEXT segment (so a SectionList exists) and an LC_SYMTAB (so
ParseSymtab
+ // proceeds past its early return), but no __LINKEDIT segment.
+ // clang-format off
+ const uint8_t kData[] = {
+ // mach_header_64 (little-endian)
+ 0xCF, 0xFA, 0xED, 0xFE, // magic: MH_MAGIC_64
+ 0x07, 0x00, 0x00, 0x01, // cputype: CPU_TYPE_X86_64
+ 0x03, 0x00, 0x00, 0x00, // cpusubtype: CPU_SUBTYPE_X86_64_ALL
+ 0x06, 0x00, 0x00, 0x00, // filetype: MH_DYLIB
+ 0x02, 0x00, 0x00, 0x00, // ncmds: 2
+ 0x60, 0x00, 0x00, 0x00, // sizeofcmds: 0x60 (72 + 24)
+ 0x00, 0x00, 0x00, 0x80, // flags: MH_DYLIB_IN_CACHE
+ 0x00, 0x00, 0x00, 0x00, // reserved: 0
+ // segment_command_64 (__TEXT, no sections)
+ 0x19, 0x00, 0x00, 0x00, // cmd: LC_SEGMENT_64
+ 0x48, 0x00, 0x00, 0x00, // cmdsize: 72
+ 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, // segname: __TEXT
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // vmaddr: 0
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize: 0x1000
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff: 0
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize: 0
+ 0x07, 0x00, 0x00, 0x00, // maxprot: rwx
+ 0x05, 0x00, 0x00, 0x00, // initprot: r-x
+ 0x00, 0x00, 0x00, 0x00, // nsects: 0
+ 0x00, 0x00, 0x00, 0x00, // flags: 0
+ // symtab_command
+ 0x02, 0x00, 0x00, 0x00, // cmd: LC_SYMTAB
+ 0x18, 0x00, 0x00, 0x00, // cmdsize: 24
+ 0x00, 0x00, 0x00, 0x00, // symoff: 0
+ 0x00, 0x00, 0x00, 0x00, // nsyms: 0
+ 0x00, 0x00, 0x00, 0x00, // stroff: 0
+ 0x00, 0x00, 0x00, 0x00, // strsize: 0
+ };
+ // clang-format on
+ auto Buf = std::make_shared<DataBufferHeap>(kData, sizeof(kData));
+ lldb::DataExtractorSP DataSP = std::make_shared<lldb_private::DataExtractor>(
+ Buf, lldb::eByteOrderLittle, /*addr_size=*/8);
+
+ ModuleSpec spec(FileSpec(), UUID(), DataSP);
+ lldb::ModuleSP module = std::make_shared<Module>(spec);
+ ObjectFile *OF = module->GetObjectFile();
+ ASSERT_TRUE(llvm::isa<ObjectFileMachO>(OF));
+
+ // Simply no crashing is the regression check.
+ Symtab symtab(OF);
+ OF->ParseSymtab(symtab);
+}
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits