WebCilFile builds raw native pointers into the memory-mapped view directly
from section-header fields read out of attacker-controlled metadata. Unlike
PEFile.GetSectionData, which delegates to the bounds-checked PEReader, this
hand-rolled path validated nothing: a crafted section header could produce a
SectionData (and hence a BlobReader) pointing far outside the view, an
out-of-bounds read reachable on normal decompilation through method-body and
field-data RVA resolution. The (int)RawDataSize narrowing cast could also
yield a negative length.
Resolve and bounds-check the raw-data range against the view length before
constructing SectionData, widening the arithmetic to long so crafted uint
fields cannot wrap the range check or narrow into an apparently valid length.
Structural parsing in FromFile now reports a crafted or truncated module as
"not a WebCIL file" (null) rather than letting EndOfStreamException,
OverflowException or BadImageFormatException escape the loader.
Assisted-by: Claude:claude-opus-4-8:Claude Code
On FIPS-mode systems the platform crypto provider refuses to create
SHA-1 instances (OpenSSL: error:03000098 invalid digest), so merely
displaying a strong-named assembly's identity failed. The public-key
token is a non-secret identity hash whose algorithm is fixed by
ECMA-335, so the two token sites now use dotnet/runtime's managed
Sha1ForNonSecretPurposes, vendored with its license header intact and
shielded from the repo formatter via generated_code in .editorconfig
so future upstream syncs diff cleanly. IncrementalHash was considered
and rejected: like SHA1.Create(), it resolves the digest through the
host crypto policy, and Roslyn's equivalent token code also relies on
the platform SHA-1, so it offers no precedent for FIPS safety.
Assisted-by: Claude:claude-fable-5:Claude Code
Hosts without a .NET Framework installation (e.g. Linux and macOS) have
no GAC; the only system-wide assembly store there is the shared-framework
directory of the runtime executing the decompiler, and
UniversalAssemblyResolver only consulted it through the version <= 4.0
legacy fallback. This made e.g. the type-forwards of a netstandard facade
(pointing to a versioned System.Runtime) unresolvable, which left
well-known types like Nullable<T> without a definition and among other
things misaligned nullability decoding (Nullable<T> occupies no slot in
the nullable metadata, so it must be recognized).
On Windows nothing the GAC answered changes; the new fallback only adds
resolutions that previously failed outright.
Assisted-by: Claude:claude-fable-5:Claude Code
* ExtensionDeclaration.SymbolKind (CA1065) — was throwing
NotImplementedException; return SymbolKind.TypeDefinition to match
TypeDeclaration / DelegateDeclaration, since `extension` declarations
are type-level.
* CustomAttribute.DecodeValue (CA2002) — replace `lock(this)` on the
sealed-but-internal class with a private syncRoot field.
* PlainTextOutput (CA1001) — implement IDisposable; track an
ownsWriter flag so we only dispose the underlying TextWriter when
the parameterless constructor created its own StringWriter.
* DotNetCorePathFinder (CA1060) — move the libc realpath / free
PInvokes into a private nested NativeMethods class.
* ILSpy.ReadyToRun (CA1016) — add [assembly: AssemblyVersion("1.0.0.0")]
in Properties/AssemblyInfo.cs to match the BamlDecompiler plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four cases where the analyzer rule conflicts with intentional design:
* EmptyList<T>.IDisposable.Dispose (CA1063) — explicit IDisposable on
IEnumerator<T>; making it public would conflict with the rest of the
IList<T> / IEnumerator<T> surface.
* MetadataFile.SectionHeaders (CA1065) — throw documents that this
MetadataFileKind has no PE sections; PE-like derived kinds override.
* LongSet.GetHashCode + LongSet itself (CA1065 + CA2231) — explicit
guards against using LongSet in hash containers / via equality
operators; SetEquals is the supported comparison and
IEquatable<LongSet>.Equals is itself [Obsolete].
* AnnotationList.Clone (CA2002) — AnnotationList is a private nested
type; the surrounding Annotatable class deliberately locks on the
AnnotationList instance to serialize annotation reads/writes, and
external code cannot obtain a reference to it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
* Support detecting .NET Core 1.0 and 1.1
* Formatting
* Fix version number
* Add support for using System.Private.CoreLib in version detection
* Move mscorlib for consistency and readability
* Ensure that netstandard is always checked before System.Runtime
* Ensure that System.Runtime is always checked before netstandard
* Formatting
This fails in culture-invariant mode (ilspycmd) when trying to work with satellite assemblies, because System.Reflection.AssemblyName tries to retrieve CultureInfo of the assembly culture.