Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw BadImageFormatException when missing PE metadata #6270

Merged
merged 2 commits into from
Mar 31, 2021

Conversation

FiniteReality
Copy link
Contributor

Fixes #6200

Context

InvalidOperationException was being thrown by AssemblyInformation.CorePopulateMetadata, causing the ResolveAssemblyReference task to fail rather than disregarding the file.

Changes Made

I copied the code from AssemblyNameExtension to ensure there is metadata present before getting a metadata reader:

bool hasMetadata = false;
try
{
// This can throw if the stream is too small, which means
// the assembly doesn't have metadata.
hasMetadata = peFile.HasMetadata;
}
finally
{
// If the file does not contain PE metadata, throw BadImageFormatException to preserve
// behavior from AssemblyName.GetAssemblyName(). RAR will deal with this correctly.
if (!hasMetadata)
{
throw new BadImageFormatException(string.Format(CultureInfo.CurrentCulture,
AssemblyResources.GetString("ResolveAssemblyReference.AssemblyDoesNotContainPEMetadata"),
path));
}
}

Testing

I looked for unit tests for AssemblyInformation, but could not find any, so I didn't add a unit test for this case. However, I successfully built the project that was reproducing the error in #6200, indicating that the change did indeed fix the issue.

Copy link
Member

@benvillalobos benvillalobos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're checking this here, we should add the same check in MetadataReader.cs. This is what it looks like currently, which could also throw.

                    if (_peReader != null)
                    {
                        if (_peReader.HasMetadata)
                        {
                            _reader = _peReader.GetMetadataReader();
                        }
                    }

@FiniteReality
Copy link
Contributor Author

FiniteReality commented Mar 16, 2021

we should add the same check in MetadataReader.cs.

It seems MetadataReader already has a try/catch surrounding the HasMetadata check, which closes the reader, meaning MetadataReader.Create() returns null as _reader is set to null.

@ladipro
Copy link
Member

ladipro commented Mar 19, 2021

This looks good although I wonder if pulling the code into a helper and calling it from both AssemblyNameExtension and AssemblyInformation wouldn't be slightly more elegant.

@FiniteReality
Copy link
Contributor Author

This looks good although I wonder if pulling the code into a helper and calling it from both AssemblyNameExtension and AssemblyInformation wouldn't be slightly more elegant.

So maybe an extension method PEReader.ThrowIfMissingMetadata()?

Copy link
Contributor

@cdmihai cdmihai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "prescribed / designed" way of referencing non-CLR projects is telling the sdk to not treat the ProjectReference output as a managed assembly. But I'm not 100% sure, I couldn't find any docs on the expected way to do this (@KathleenDollard @dsplaisted). Might be safer to go down that route than to allow native assemblies to flow into RAR which might have unknown, cascading consequences for the downstream code that reads RAR outputs.

@ladipro
Copy link
Member

ladipro commented Mar 21, 2021

How does RAR behave if you pass a non-PE file?

@FiniteReality
Copy link
Contributor Author

How does RAR behave if you pass a non-PE file?

Generally, a BadImageFormatException is thrown, which RAR catches and aborts with "File does not contain managed metadata". For example:

         Primary reference "HelloWorldLibraryC".
             Could not find dependent files. Assembly file '/home/dev/Development/Finite.Cpp.Sdk/artifacts/bin/samples/HelloWorldLibraryC/Debug/HelloWorldLibraryC.so' could not be opened -- PE image doesn't contain managed metadata.
             Could not find dependent files. Image is too small.
             Resolved file path is "/home/dev/Development/Finite.Cpp.Sdk/artifacts/bin/samples/HelloWorldLibraryC/Debug/HelloWorldLibraryC.so".
             Reference found at search path location "".
             The ImageRuntimeVersion for this reference is "".

@KirillOsenkov
Copy link
Member

Not sure if relevant, but Microsoft.Build.Tasks.Core.dll has an IsManagedAssembly() method:
https://source.dot.net/#Microsoft.Build.Tasks.Core/ManifestUtil/PathUtil.cs,ef4ea3875d69614b

Also here's a way to find out if an assembly is managed without throwing:
https://github.com/KirillOsenkov/MetadataTools/blob/main/src/PEFile/IsManagedAssembly.cs

@KirillOsenkov
Copy link
Member

@cdmihai the project reference stuff is certainly valid, but I'm questioning whether that should be done as a separate PR? This feels nicely self-contained and relatively isolated and it does fix the issue at hand?

@Forgind Forgind added the merge-when-branch-open PRs that are approved, except that there is a problem that means we are not merging stuff right now. label Mar 28, 2021
@Forgind Forgind merged commit 7a89210 into dotnet:main Mar 31, 2021
@Forgind
Copy link
Member

Forgind commented Mar 31, 2021

Thanks @FiniteReality!

@FiniteReality FiniteReality deleted the finitereality/issue-6200 branch March 31, 2021 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge-when-branch-open PRs that are approved, except that there is a problem that means we are not merging stuff right now.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ResolveAssemblyReference throws InvalidOperationException on invalid PE files
6 participants