https://github.com/qiyao updated https://github.com/llvm/llvm-project/pull/205289
>From c36aa4aac976f80f26e69e1b75d9528e2f139c7c Mon Sep 17 00:00:00 2001 From: Yao Qi <[email protected]> Date: Mon, 22 Jun 2026 21:52:21 +0100 Subject: [PATCH 1/3] [lldb][Mach-O] Bounds-check GetArchitectureAtIndex against m_fat_archs `ObjectContainerUniversalMachO::GetArchitectureAtIndex` used `m_header.nfat_arch` (read directly from the file and untrusted, up to 0xFFFFFFFF) as the bound before indexing `m_fat_archs`. When ParseHeader exhausts the data partway through and breaks early, `m_fat_archs.size()` can be smaller than `nfat_arch`, so the indexed load is out of bounds. Bound the check on the actual vector size instead. Found by lldb-target-fuzzer. --- .../ObjectContainerUniversalMachO.cpp | 4 ++- .../ObjectContainerUniversalMachOTest.cpp | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp index f3127ef920982..363cc47e59662 100644 --- a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp +++ b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp @@ -142,7 +142,9 @@ size_t ObjectContainerUniversalMachO::GetNumArchitectures() const { bool ObjectContainerUniversalMachO::GetArchitectureAtIndex( uint32_t idx, ArchSpec &arch) const { - if (idx < m_header.nfat_arch) { + // guard against m_fat_archs.size() to keep this safe regardless of + // how the header was populated. + if (idx < m_fat_archs.size()) { arch.SetArchitecture(eArchTypeMachO, m_fat_archs[idx].GetCPUType(), m_fat_archs[idx].GetCPUSubType()); return true; diff --git a/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp b/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp index a4346befbfd8b..25262952f20ef 100644 --- a/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp +++ b/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp @@ -12,6 +12,8 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/FileSystem.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpec.h" #include "llvm/Support/FileSystem.h" #include "llvm/Testing/Support/Error.h" @@ -117,3 +119,29 @@ TEST_F(ObjectContainerUniversalMachOTest, SliceOffsetZero) { ASSERT_THAT_ERROR(TmpFile->discard(), llvm::Succeeded()); } + +// Regression fixture: a universal (fat) Mach-O whose header claims a huge +// nfat_arch (here 0xAFAFAFAF) but provides no fat_arch entries beyond the +// header bytes. Found by lldb-target-fuzzer. +TEST_F(ObjectContainerUniversalMachOTest, NfatArchTruncatedSlices) { + // Hand-crafted fat header: FAT_MAGIC_64 + nfat_arch=0xAFAFAFAF + 2 stray + // payload bytes, not enough for even one fat_arch_64 entry (32 bytes). + const uint8_t kData[] = { + 0xCA, 0xFE, 0xBA, 0xBF, // magic: FAT_MAGIC_64 (big endian) + 0xAF, 0xAF, 0xAF, 0xAF, // nfat_arch: 0xAFAFAFAF (untrusted, huge) + 0xAF, 0xAF, // truncated arch payload + }; + lldb::DataBufferSP Buf = + std::make_shared<DataBufferHeap>(kData, sizeof(kData)); + + std::unique_ptr<lldb_private::ObjectContainer> Container( + ObjectContainerUniversalMachO::CreateInstance( + /*module_sp=*/nullptr, Buf, /*data_offset=*/0, /*file=*/nullptr, + /*file_offset=*/0, /*length=*/sizeof(kData))); + ASSERT_NE(Container.get(), nullptr); + + // Before the fix, this m_fat_archs[0] causes an OOB std::vector access; after + // the fix the bounds check against m_fat_archs.size() returns false. + ArchSpec Arch; + EXPECT_FALSE(Container->GetArchitectureAtIndex(0, Arch)); +} >From aec662508b5ef0cfe0b6eab3bf603571ec509e1f Mon Sep 17 00:00:00 2001 From: Yao Qi <[email protected]> Date: Wed, 24 Jun 2026 08:44:36 +0100 Subject: [PATCH 2/3] Remove unnecessary comments --- .../Universal-Mach-O/ObjectContainerUniversalMachO.cpp | 2 -- .../ObjectContainer/ObjectContainerUniversalMachOTest.cpp | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp index 363cc47e59662..1fcf7dd882bf4 100644 --- a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp +++ b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp @@ -142,8 +142,6 @@ size_t ObjectContainerUniversalMachO::GetNumArchitectures() const { bool ObjectContainerUniversalMachO::GetArchitectureAtIndex( uint32_t idx, ArchSpec &arch) const { - // guard against m_fat_archs.size() to keep this safe regardless of - // how the header was populated. if (idx < m_fat_archs.size()) { arch.SetArchitecture(eArchTypeMachO, m_fat_archs[idx].GetCPUType(), m_fat_archs[idx].GetCPUSubType()); diff --git a/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp b/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp index 25262952f20ef..a5080886a9e29 100644 --- a/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp +++ b/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp @@ -140,8 +140,7 @@ TEST_F(ObjectContainerUniversalMachOTest, NfatArchTruncatedSlices) { /*file_offset=*/0, /*length=*/sizeof(kData))); ASSERT_NE(Container.get(), nullptr); - // Before the fix, this m_fat_archs[0] causes an OOB std::vector access; after - // the fix the bounds check against m_fat_archs.size() returns false. + // m_fat_archs has zero emlement, returns false. ArchSpec Arch; EXPECT_FALSE(Container->GetArchitectureAtIndex(0, Arch)); } >From 6944d64534f4b9cab3b7e19630bd24b2cb38a2a7 Mon Sep 17 00:00:00 2001 From: Yao Qi <[email protected]> Date: Wed, 24 Jun 2026 21:48:18 +0100 Subject: [PATCH 3/3] Update lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp Co-authored-by: Alex Langford <[email protected]> --- .../ObjectContainer/ObjectContainerUniversalMachOTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp b/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp index a5080886a9e29..50ab4c9557b66 100644 --- a/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp +++ b/lldb/unittests/ObjectContainer/ObjectContainerUniversalMachOTest.cpp @@ -140,7 +140,7 @@ TEST_F(ObjectContainerUniversalMachOTest, NfatArchTruncatedSlices) { /*file_offset=*/0, /*length=*/sizeof(kData))); ASSERT_NE(Container.get(), nullptr); - // m_fat_archs has zero emlement, returns false. + // m_fat_archs has zero elements, returns false. ArchSpec Arch; EXPECT_FALSE(Container->GetArchitectureAtIndex(0, Arch)); } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
