From d78249ce06fc994e28162e93e77572613938f539 Mon Sep 17 00:00:00 2001 From: rbran Date: Sun, 14 Jun 2026 08:32:13 -0300 Subject: [PATCH 1/8] ensure ElfView::ReadStringTable strlen dont overrun --- view/elf/elfview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index bfb5c68b2..8cf5c0259 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2754,7 +2754,7 @@ string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& } const std::vector& tableCache = itr->second; - return std::string(&tableCache[offset], strlen(tableCache.data() + offset)); + return std::string(&tableCache[offset], strnlen(tableCache.data() + offset, tableCache.size() - offset)); } From 8b263cfa4d751794ad87ecfa85e25310ccc4307e Mon Sep 17 00:00:00 2001 From: rbran Date: Sun, 14 Jun 2026 08:38:44 -0300 Subject: [PATCH 2/8] ensure ElfView::ReadStringTable dont read too much data --- view/elf/elfview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 8cf5c0259..6c821f833 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2734,7 +2734,7 @@ void ElfView::ApplyTypesToStringTable(const Elf64SectionHeader& section, const i string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& section, uint64_t offset) { - if (offset == 0 || offset > section.size) + if (offset == 0 || offset > section.size || section.size > 0x1000000) return ""; auto itr = m_stringTableCache.find(section.offset); From ab2de9219c929739262d7e3102f50a44494b28a9 Mon Sep 17 00:00:00 2001 From: rbran Date: Sun, 14 Jun 2026 10:53:30 -0300 Subject: [PATCH 3/8] ensure ElfView::ReadStringTable dont read after the offset --- view/elf/elfview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 6c821f833..218107da0 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2754,6 +2754,10 @@ string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& } const std::vector& tableCache = itr->second; + if (offset > tableCache.size()) { + m_logger->LogError("Unable to read string from table cache offset: 0x%" PRIx64 " size: 0x%" PRIx64, section.offset, section.size); + return ""; + } return std::string(&tableCache[offset], strnlen(tableCache.data() + offset, tableCache.size() - offset)); } From 414ef77f1ef0973c606a8750c82c3148b96ce37c Mon Sep 17 00:00:00 2001 From: rbran Date: Mon, 15 Jun 2026 12:29:24 -0300 Subject: [PATCH 4/8] limit localMipsSyms in ElfView::Init to sane values --- view/elf/elfview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 218107da0..9ab7467d0 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -1149,7 +1149,8 @@ bool ElfView::Init() { if (mipsSymValid && (gotStart != 0)) { - for (size_t i = 2; i < localMipsSyms; i++) + auto localMipsSymsFixed = localMipsSyms % 0x1000; + for (size_t i = 2; i < localMipsSymsFixed; i++) { m_gotEntryLocations.emplace(gotStart + i * (m_elf32 ? 4 : 8)); } From 6d922c852fc150c16fdd085a66e898321030a6c1 Mon Sep 17 00:00:00 2001 From: rbran Date: Tue, 16 Jun 2026 08:19:50 -0300 Subject: [PATCH 5/8] limit section.size in ElfView::ApplyTypesToParentStringTable to sane values --- view/elf/elfview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 9ab7467d0..40d81c81f 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2667,7 +2667,7 @@ void ElfView::ApplyTypesToParentStringTable(const Elf64SectionHeader& section, c { m_logger->LogInfo("Found string table of size %p at offset %p", section.size, section.offset); DataBuffer buffer = GetParentView()->ReadBuffer(section.offset, section.size); - if (buffer.GetLength() != section.size) + if (section.size > 0x1000 || buffer.GetLength() != section.size) return; unordered_map> cachedTypes; for (size_t start_address = (offset ? 1 : 0); start_address < section.size; ++start_address) From d4160657e2b0e1839d0be8b054514d16cdf45120 Mon Sep 17 00:00:00 2001 From: rbran Date: Sun, 21 Jun 2026 14:24:00 -0300 Subject: [PATCH 6/8] ensure ElfView::ReadStringTable dont read after the offset by one --- view/elf/elfview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 40d81c81f..99bb11895 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2755,7 +2755,7 @@ string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& } const std::vector& tableCache = itr->second; - if (offset > tableCache.size()) { + if (offset >= tableCache.size()) { m_logger->LogError("Unable to read string from table cache offset: 0x%" PRIx64 " size: 0x%" PRIx64, section.offset, section.size); return ""; } From 822a0ce6707bca6e8f39d91c9bc887b4cd6ca3b0 Mon Sep 17 00:00:00 2001 From: Weitao Sun Date: Wed, 1 Jul 2026 13:34:28 -0400 Subject: [PATCH 7/8] Improve bounds checking and add file-backed validation in ElfView Replace magic-number loop guards in MIPS GOT processing with segment-derived limits and configurable budgets. Fix integer underflow when localMipsSyms is zero. Correct string table validation to compare against file length rather than virtual address space. Add IsRangeBackedByFile helper that validates every byte in the given range is file-backed, and guard against overflow when a program header's declared file extent is used to bound the GOT entry count. Co-Authored-By: Claude Sonnet 4.6 --- view/elf/elfview.cpp | 80 +++++++++++++++++++++++++++++++++++++++----- view/elf/elfview.h | 2 ++ 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 99bb11895..0a91efb17 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -51,6 +51,28 @@ void BinaryNinja::InitElfViewType() "ignore" : ["SettingsProjectScope", "SettingsResourceScope"] })~"); + settings->RegisterSetting("files.elf.maxMipsLocalGotEntries", + R"({ + "title" : "Maximum MIPS Local GOT Entry Count", + "type" : "number", + "default" : 1048576, + "minValue" : 0, + "maxValue" : 67108864, + "description" : "Maximum number of local GOT entries to process in MIPS ELF files", + "ignore" : ["SettingsProjectScope"] + })"); + + settings->RegisterSetting("files.elf.maxStringTableTypeSizeMB", + R"~({ + "title" : "Maximum ELF String Table Size for Type Application (MB)", + "type" : "number", + "default" : 4, + "minValue" : 0, + "maxValue" : 256, + "description" : "Maximum string table size in megabytes when applying types from string tables", + "ignore" : ["SettingsProjectScope"] + })~"); + } @@ -1149,15 +1171,33 @@ bool ElfView::Init() { if (mipsSymValid && (gotStart != 0)) { - auto localMipsSymsFixed = localMipsSyms % 0x1000; - for (size_t i = 2; i < localMipsSymsFixed; i++) + const uint64_t entrySize = m_elf32 ? 4 : 8; + Ref viewSettings = Settings::Instance(); + const uint64_t entryBudget = viewSettings->Get("files.elf.maxMipsLocalGotEntries", this); + + // Find the file-backed region containing gotStart once, so the loop needs no per-entry check. + uint64_t gotFileEnd = gotStart; + for (const auto& hdr : m_programHeaders) { - m_gotEntryLocations.emplace(gotStart + i * (m_elf32 ? 4 : 8)); + if (hdr.type != ELF_PT_LOAD || hdr.fileSize == 0 || hdr.fileSize > UINT64_MAX - hdr.virtualAddress) + continue; + uint64_t segEnd = hdr.virtualAddress + hdr.fileSize; + if (hdr.virtualAddress > gotStart || gotStart >= segEnd) + continue; + // Only trust this segment's declared extent up to what the file actually backs. + if (hdr.fileSize > UINT64_MAX - hdr.offset || hdr.offset + hdr.fileSize > (uint64_t)GetParentView()->GetLength()) + continue; + gotFileEnd = segEnd; + break; } + const uint64_t maxFromFile = (gotFileEnd > gotStart) ? (gotFileEnd - gotStart) / entrySize : 0; + const uint64_t limit = std::min({localMipsSyms, entryBudget, maxFromFile}); + for (size_t i = 2; i < limit; i++) + m_gotEntryLocations.emplace(gotStart + i * entrySize); for (uint64_t i = firstMipsSym; i < (m_auxSymbolTable.size / (m_elf32 ? 16 : 24)); i++) { - uint64_t gotEntry = gotStart + ((localMipsSyms + i - firstMipsSym) * (m_elf32 ? 4 : 8)); - if (!IsValidOffset(gotEntry)) + uint64_t gotEntry = gotStart + ((localMipsSyms + i - firstMipsSym) * entrySize); + if (!IsRangeBackedByFile(gotEntry, entrySize)) { m_logger->LogWarn("ELF GOT entry %" PRIx64 " is invalid", gotEntry); break; @@ -1797,7 +1837,7 @@ bool ElfView::Init() } // Perform fixup processing on the local GOT entries if the view is relocatable. - if (m_relocatable) + if (m_relocatable && localMipsSyms > 0) { uint64_t lastLocalGotEntry = gotStart + (localMipsSyms - 1) * (m_elf32 ? 4 : 8); for (auto gotEntry : m_gotEntryLocations) @@ -2666,8 +2706,16 @@ void ElfView::DefineElfSymbol(BNSymbolType type, const string& incomingName, uin void ElfView::ApplyTypesToParentStringTable(const Elf64SectionHeader& section, const bool offset) { m_logger->LogInfo("Found string table of size %p at offset %p", section.size, section.offset); + if (section.size == 0 || + section.size > UINT64_MAX - section.offset || + section.offset + section.size > GetParentView()->GetLength()) + return; + Ref viewSettings = Settings::Instance(); + const uint64_t sizeBudget = viewSettings->Get("files.elf.maxStringTableTypeSizeMB", this) * 1024 * 1024; + if (section.size > sizeBudget) + return; DataBuffer buffer = GetParentView()->ReadBuffer(section.offset, section.size); - if (section.size > 0x1000 || buffer.GetLength() != section.size) + if (buffer.GetLength() != section.size) return; unordered_map> cachedTypes; for (size_t start_address = (offset ? 1 : 0); start_address < section.size; ++start_address) @@ -2735,7 +2783,7 @@ void ElfView::ApplyTypesToStringTable(const Elf64SectionHeader& section, const i string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& section, uint64_t offset) { - if (offset == 0 || offset > section.size || section.size > 0x1000000) + if (offset == 0 || offset >= section.size || section.size > GetParentView()->GetLength()) return ""; auto itr = m_stringTableCache.find(section.offset); @@ -2763,6 +2811,22 @@ string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& } +// Returns true only if the entire range [start, start+size) is backed by file data. +bool ElfView::IsRangeBackedByFile(uint64_t start, uint64_t size) const +{ + if (size == 0) + return false; + if (size > UINT64_MAX - start) + return false; + for (uint64_t offset = start; offset < start + size; offset++) + { + if (!IsOffsetBackedByFile(offset)) + return false; + } + return true; +} + + // http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES bool ElfView::DerefPpc64Descriptor(BinaryReader& reader, uint64_t addr, uint64_t& result) { diff --git a/view/elf/elfview.h b/view/elf/elfview.h index f81fa832e..a0b56fda4 100644 --- a/view/elf/elfview.h +++ b/view/elf/elfview.h @@ -527,6 +527,8 @@ namespace BinaryNinja void DefineElfSymbol(BNSymbolType type, const std::string& name, uint64_t addr, bool gotEntry, BNSymbolBinding binding, size_t size = 0, const Confidence>& typeObj = nullptr); + bool IsRangeBackedByFile(uint64_t start, uint64_t size) const; + void ApplyTypesToParentStringTable(const Elf64SectionHeader& section, const bool offset = true); void ApplyTypesToStringTable(const Elf64SectionHeader& section, const int64_t imageBaseAdjustment, const bool offset = true); std::string ReadStringTable(BinaryReader& view, const Elf64SectionHeader& section, uint64_t offset); From f8ad791b370273bb1ab5eb983345f4c900ee8d7e Mon Sep 17 00:00:00 2001 From: Weitao Sun Date: Wed, 1 Jul 2026 15:47:36 -0400 Subject: [PATCH 8/8] Change MIPS GOT budget setting from entry count to megabytes Mirrors the Linux kernel pattern (fs/binfmt_elf.c) of bounding by total byte size rather than entry count, so the same limit applies equally to 32-bit and 64-bit MIPS ELF files regardless of entry size. The setting files.elf.maxMipsGotMB (default 4 MB) is divided by the per-entry size at load time to derive the effective entry limit. Ref: https://github.com/torvalds/linux/blob/master/fs/binfmt_elf.c Co-Authored-By: Claude Sonnet 4.6 --- view/elf/elfview.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 0a91efb17..70ea3d69c 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -51,14 +51,14 @@ void BinaryNinja::InitElfViewType() "ignore" : ["SettingsProjectScope", "SettingsResourceScope"] })~"); - settings->RegisterSetting("files.elf.maxMipsLocalGotEntries", + settings->RegisterSetting("files.elf.maxMipsGotMB", R"({ - "title" : "Maximum MIPS Local GOT Entry Count", + "title" : "Maximum MIPS GOT Data Size in MB", "type" : "number", - "default" : 1048576, + "default" : 4, "minValue" : 0, - "maxValue" : 67108864, - "description" : "Maximum number of local GOT entries to process in MIPS ELF files", + "maxValue" : 64, + "description" : "Maximum total GOT data size in megabytes to process in MIPS ELF files", "ignore" : ["SettingsProjectScope"] })"); @@ -1173,7 +1173,8 @@ bool ElfView::Init() { const uint64_t entrySize = m_elf32 ? 4 : 8; Ref viewSettings = Settings::Instance(); - const uint64_t entryBudget = viewSettings->Get("files.elf.maxMipsLocalGotEntries", this); + const uint64_t mbBudget = viewSettings->Get("files.elf.maxMipsGotMB", this); + const uint64_t entryBudget = (mbBudget * 1024 * 1024) / entrySize; // Find the file-backed region containing gotStart once, so the loop needs no per-entry check. uint64_t gotFileEnd = gotStart;