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

Reply via email to