A .resources file's resource count, type count, name lengths, binary
resource lengths and serialized-object lengths all come from the file
and were only checked for being non-negative before sizing an allocation.
A crafted file can therefore request multi-gigabyte arrays from a
few-hundred-byte payload (CWE-789), turning a click on a resource node
into an out-of-memory condition. The serialization-format kind was
additionally an assert-only check that vanishes in Release builds.
Each element of these counts occupies at least one byte in the stream, so
a value needing more bytes than remain after the current position cannot
be honest. Reject it with the same BadImageFormatException the callers
already handle, and promote the format-kind assert into a real check.
Assisted-by: Claude:claude-fable-5:Claude Code
MetadataFile now declares IDisposable using the canonical pattern
(public non-virtual Dispose() + protected virtual Dispose(bool)).
PEFile and WebCilFile become sealed and override Dispose(bool) to
release the PEReader and MemoryMappedViewAccessor they own;
ResourcesFile is also sealed. PortableDebugInfoProvider disposes the
MetadataReaderProvider it owns. LoadedAssembly implements IDisposable
and disposes both the loaded MetadataFile and the debug-info provider.
AssemblyList.Unload / Clear / ReloadAssembly / HotReplaceAssembly now
dispose the LoadedAssembly instances they evict, fixing a resource leak
where every "Reload Assembly" held the previous PEReader (and the
underlying file handle / memory-mapped view) alive until GC eventually
finalized it.
The disposal contract terminates at the AssemblyList tier: downstream
holders of MetadataFile (MetadataModule, DecompilerTypeSystem,
AssemblyListSnapshot, ...) hold borrowed references rather than owned
ones, so making the base IDisposable does not cascade into CA1001 /
CA2213 warnings elsewhere.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>