Fix multiple PE/COFF parsing bugs (#1502) - #8538
Open
Weitao-Sun wants to merge 4 commits into
Open
Conversation
Bug #9169 - COFF symbol table entries (peview.cpp + coffview.cpp): - Cap section name string table reads at 1024 bytes per name - Validate section- and symbol-name string table offsets against the declared string table size before reading, and use 64-bit arithmetic (and the actual per-record size in coffview.cpp) when computing string table offsets - Add a configurable limit (default 1M) on how many COFF symbol table entries receive full name resolution, typing, and aux record definitions; every table slot still gets a data variable and marker symbol in coffview.cpp so relocations can resolve any symbol index the file declares - Add a per-name length cap (default 32 KB) via ReadCString(maxSymNameLen) - Add a total budget (default 1 GB) on accumulated symbol name bytes, weighted for the additional copies a resolved name ends up retained in once a symbol is created for it; cache names by string table offset so repeated offsets are read and counted once - Expose all limits as user-configurable loader settings (loader.pe.* and loader.coff.*), each accepting 0 to disable the corresponding check Bug #9170 - PointerToRawData not 0x200-aligned (peview.cpp): - For PE32/PE32+, always round PointerToRawData DOWN to the nearest 0x200 boundary, matching the Windows loader's hardcoded behaviour regardless of the FileAlignment field value Bug #9171 - SizeOfRawData not rounded to FileAlignment (peview.cpp): - For PE32/PE32+, round SizeOfRawData UP to the next FileAlignment multiple so that bytes between the declared raw size and the alignment boundary are included in the binary view, matching what Windows maps; cap the result at the remaining bytes in the file, clamping before narrowing to the 32-bit section field - Use uint64_t arithmetic for the rounding to prevent overflow when sizeOfRawData is near UINT32_MAX Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Segment/section creation and RVA characteristics/symbol placement bound a section's length using two different fields: file-backed reads use sizeOfRawData, while everything else uses virtualSize. When a section's raw data extends past its declared virtual size, the extra bytes end up readable via RVA-to-file-offset translation but outside the mapped segment, section, and symbol range. Grow virtualSize to cover sizeOfRawData so all of these stay consistent. This applies regardless of PE32/PE32+ magic, since it isn't describing loader-specific alignment behavior, just keeping the view's own internal bookkeeping self-consistent.
Section raw-data rounding to the 0x200 sector boundary only applies to normally page-aligned images. Per the PE spec, when SectionAlignment is below the architecture's page size, FileAlignment must equal SectionAlignment and raw offsets map directly to RVAs without the usual sector padding. Detect that case and skip both the PointerToRawData and SizeOfRawData rounding for it, so low-alignment images keep their declared file layout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
zznop
requested changes
Sep 10, 2026
… bounds - Charge every retained COFF symbol name against the total-name-bytes budget, including cache hits. - Resolve external symbol names on demand in the relocation loop for symbol table entries outside the per-file annotation limit, sharing the same budget and cache as the initial pass. - Make RVAToFileOffset and GetRVACharacteristics prefer the most recently added section when sections overlap, matching the rest of the loader. - Require the string table length to be at least 4 bytes, validate name offsets against the table bounds, and cap name reads to the bytes remaining in the table, across all three name lookups in these files (COFF symbol names, PE symbol names, COFF section names). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
Author
|
Fixed all four in bc9cad8: name-budget now charges every retained reference including cache hits; relocations to undefined-external symbols beyond the annotation limit are resolved on demand under the same budget; RVAToFileOffset/GetRVACharacteristics now keep the last matching section on overlap; string-table reads are bounds-checked and capped to the table's remaining length in all three lookup sites in these files (also found and fixed the same missing bounds check in the section-name lookup, which wasn't flagged but has the same issue). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes three PE/COFF parsing issues reported in Vector35/binaryninja#1502 (referenced there as Ghidra #9169, #9170, #9171):
This is the api-repo counterpart of Vector35/binaryninja#1625, which will be updated to just bump the
apisubmodule pointer once this merges.Review feedback addressed
An earlier version of this change went through a round of review. Summary of what changed as a result:
virtualSizeis grown tomax(virtualSize, sizeOfRawData)so segments, sections, RVA characteristics, and symbol placement all agree with the corrected raw-data extent.UINT32_MAXbefore narrowing touint32_t.coffSymbolCount * sizeofCOFFSymbolarithmetic is widened to 64-bit in both files, and the COFF section-name-resolution path uses the runtime symbol-record size instead of a hardcoded 18.peview.h, shared by both files.header.coffSymbolCountis no longer mutated. Every raw symbol-table slot still gets a data variable and marker symbol, which relocation resolution looks up by address; the configurable count limit only gates the more expensive per-symbol work (name resolution, typing, aux record definitions).Follow-up round
A second look raised four more points:
SectionAlignment < page sizeandSectionAlignment == FileAlignment(the PE spec's low-alignment case), raw offsets map directly to RVAs without sector rounding. Checked against a synthetic low-alignment image (SectionAlignment = FileAlignment = 0x20,PointerToRawData = 0x220) — the section's file offset and content now match the declared layout instead of getting rounded down to0x200.maxCoffSymbolCount = 1, 2 symbols, and a relocation referencing symbol index 1, the marker for index 1 is still created and the relocation resolves — this is what point 8 above covers.virtualSize = max(virtualSize, sizeOfRawData)change above.UINT32_MAXhappens before narrowing (point 3 above), so this doesn't wrap.If any of the last three still show up against this branch's current tip, a repro would help track it down further.
Known follow-up (not blocking)
The per-slot marker-symbol loop in the COFF symbol table walk (point 8) still iterates the full declared
coffSymbolCounteven when the count limit caps the heavier per-symbol work, so a very large declared count still costs a linear pass just to place markers. A bound derived from the file's actual size (a legitimate count can't exceed what fits in the remaining file) would close this without reintroducing the relocation-lookup issue point 8 fixed. Can follow up separately if useful.Testing
This changes PE section-layout logic, so part of the local analysis-output test suite differs from its stored oracles — expected, since section boundaries legitimately shift for several PE test corpus binaries. The full suite gives 81 differences against this branch; running the same suite against plain
dev(no commits from this branch) narrows it down:dev— unrelated to this change (test_Architecture,test_memory_leaks,test_struct_return_win32/win64, PDB tests, plus a handful of non-layout tests onacpipagr.sys/acpitime.sys).acpipagr.sys,acpitime.sys,jumptable_multiple_indirect,partial_register_dataflow(_rebasing),pe_thumb,quick3dcoreplugin.dll,win32_x86.dll,win32_x86_warp.dll). Same set with or without the latest (low-alignment) commit. These are the layout-shift kind — uniform address/length changes and/or new, well-formed content in the newly-exposed byte range.Will regenerate oracles for the 58 once the code here settles, scoped to just those binaries.
🤖 Generated with Claude Code