Skip to content

C#: Choose between .NET framework or core DLLs in standalone #14368

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

Merged
merged 17 commits into from
Oct 16, 2023

Conversation

tamasvajk
Copy link
Contributor

@tamasvajk tamasvajk commented Oct 4, 2023

This PR

  • fixes the lookup path of the Mono lib folder,
  • adds logic to choose between .NET framework/core DLLs ,
  • adds integration tests for standalone extraction showing the extracted references.

The changes help with the extraction of projects that target the full framework. For example:

Results for rapPayne/WebGoat.Net
 Query CallTargets.ql:
-  Result count - both/traced/standalone: 894/459/36
-  Result count in the matching 87 files - both/traced/standalone: 894/459/36
-   Matching 64.36%
+  Result count - both/traced/standalone: 1353/0/0
+  Result count in the matching 87 files - both/traced/standalone: 1353/0/0
+   Matching 100.0%

Results for jerryhoff/WebGoat.NET
 Query CallTargets.ql:
-  Result count - both/traced/standalone: 989/737/1685
-  Result count in the matching 156 files - both/traced/standalone: 989/737/25
-   Matching 56.48%
+  Result count - both/traced/standalone: 1654/72/1887
+  Result count in the matching 156 files - both/traced/standalone: 1654/72/0
+   Matching 95.83%

@github-actions github-actions bot added the C# label Oct 4, 2023
@tamasvajk
Copy link
Contributor Author

DCA results don't look great, I checked a couple of the projects, and compiler errors were reported because of conflicting framework DLLs. The conflict was between nuget restored reference assemblies and installed DLLs. So I'll handle this todo: https://github.com/github/codeql/pull/14368/files#diff-0179d8d74e2e78735e1d6cde08b1cff43d2a132f2c53a86c277cfdf3bbd95017R102-R104

existsNetCoreRefNugetPackage = IsNugetPackageAvailable("microsoft.netcore.app.ref");
existsNetFrameworkRefNugetPackage = IsNugetPackageAvailable("microsoft.netframework.referenceassemblies");

if (existsNetCoreRefNugetPackage || existsNetFrameworkRefNugetPackage)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The DCA changes show that this logic is not working great. If dotnet core 6 is installed, and there are net6.0 and net5.0 targets, then the reference assemblies of net5.0 are downloaded and preferred over the installed dotnet 6 DLLs. And this causes an issue if there are other references which are compiled against dotnet 6.0.

We could probably force the download of the ref assemblies for all target platforms, and we should choose the latest one from those.

@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from dbaa0b8 to 52ccc2a Compare October 6, 2023 08:23
@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch 3 times, most recently from 83e5057 to 3f9b989 Compare October 6, 2023 21:56
@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch 4 times, most recently from fdc8db5 to c2099fd Compare October 9, 2023 10:37
import os
from create_database_utils import *

os.environ['PROCESSOR_ARCHITECTURE'] = 'x64'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should not be merged. I added a fix for this in the internal repo.

@@ -0,0 +1,5 @@
[VALUE_NOT_IN_TYPE] predicate attributes(@attribute id, int kind, @type_or_ref type_id, @attributable target): Value 25 of field target is not in type @attributable. The value is however in the following types: @void_type. Appears in tuple (949897,0,960,25)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is being fixed in #14436

@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from 59faeb3 to 7e30485 Compare October 11, 2023 12:49
@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from 39351be to ada5dcc Compare October 11, 2023 14:25
Comment on lines +651 to +654
catch (Exception exc)
{
progressMonitor.LogInfo("Couldn't delete package directory: " + exc.Message);
}

Check notice

Code scanning / CodeQL

Generic catch clause

Generic catch clause.
Comment on lines +661 to +664
catch (Exception exc)
{
progressMonitor.LogInfo("Couldn't delete temporary working directory: " + exc.Message);
}

Check notice

Code scanning / CodeQL

Generic catch clause

Generic catch clause.
}
catch (Exception exc)
{
progressMonitor.LogInfo("Couldn't delete package directory: " + exc.Message);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This logs Couldn't delete package directory: The process cannot access the file 'System.EnterpriseServices.Wrapper.dll' because it is being used by another process. on Windows. The DLL can't be loaded as an assembly, and it looks like System.Reflection.PortableExecutable.PEReader doesn't release it.

The DLL is coming from the microsoft.netframework.referenceassemblies.net48.1.0.3 nuget package.

Directory.CreateDirectory(path);
}

args += $" /p:TargetFrameworkRootPath=\"{path}\" /p:NetCoreTargetingPackRoot=\"{path}\"";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This feels like an ugly hack, but I couldn't find any other msbuild parameters that would do the same.

The goal is to depend on the nuget reference assemblies instead of the locally installed ones (for both dotnet core and the full framework)

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks fine to me :-D

var monoPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "mono.exe" : "mono");
var monoDirs = monoPath is not null
? new[] { Path.GetFullPath(Path.Combine(monoPath, "..", "lib", "mono")), monoPath }
: new[] { "/usr/lib/mono", "/usr/local/mono", "/usr/local/bin/mono", @"C:\Program Files\Mono\lib\mono" };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a couple more mono installation directories.

if (Directory.Exists(@"C:\Windows\Microsoft.NET\Framework64"))
{
return Directory.EnumerateDirectories(@"C:\Windows\Microsoft.NET\Framework64", "v*")
.OrderByDescending(Path.GetFileName);
}

var monoPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "mono.exe" : "mono");
var monoDirs = monoPath is not null
? new[] { Path.GetFullPath(Path.Combine(monoPath, "..", "lib", "mono")), monoPath }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The paths didn't match:

❯ where mono
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono

vs

/Library/Frameworks/Mono.framework/Versions/Current/lib/mono

@@ -8,6 +8,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);CA1822</NoWarn>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not reporting warnings on members that could be static, such as Runtime.ExecutingRuntime.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not make it static instead of disabling the warning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just stylistic. This way AspNetCoreRuntime, ExecutingRuntime, DesktopRuntime, and NetCoreRuntime can be called the same way.

| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.XmlSerializer.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/mscorlib.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/netstandard.dll |
| /microsoft.windowsdesktop.app.ref/7.0.2/analyzers/dotnet/System.Windows.Forms.Analyzers.dll |
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The same test is added for unix-only too. The dependencies differ because of the platform. On Windows microsoft.windowsdesktop.app.ref is also fetched.

@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from e6c0a89 to 3b4ea27 Compare October 12, 2023 09:10
@tamasvajk tamasvajk marked this pull request as ready for review October 12, 2023 09:53
@tamasvajk tamasvajk requested review from a team as code owners October 12, 2023 09:53
@tamasvajk tamasvajk removed the request for review from a team October 12, 2023 10:55
@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from cc02190 to ba68e2d Compare October 13, 2023 11:43
not a.getCompilation().getOutputAssembly() = a and
exists(string s | s = a.getFile().getAbsolutePath() |
result =
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is 16 also added explicitly (looks like this is also the length of "GitHub/packages/"?

Copy link
Contributor Author

@tamasvajk tamasvajk Oct 13, 2023

Choose a reason for hiding this comment

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

We're using a 16-character-long hash as a subfolder name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from ba68e2d to 06cda11 Compare October 13, 2023 11:57
@tamasvajk tamasvajk force-pushed the standalone/use-legacy-framework-dlls branch from 06cda11 to 15ec0a1 Compare October 13, 2023 12:10
}
var subFolderIndex = secondDirectorySeparatorCharIndex + 1;
var isInAnalyzersFolder = lowerFilename.IndexOf("analyzers", subFolderIndex) == subFolderIndex;
if (isInAnalyzersFolder)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nuget packages can ship analyzers, code fixes, and source generators in the analyzers folder. These DLLs need to be passed in the analyzers csc argument and not in references. The base classes used in these DLLs are not part of the dotnet runtime (but the dotnet SDK), so if these are passed as references, they will produce compile errors, so we remove them from the reference list.

Copy link
Contributor

@michaelnebel michaelnebel left a comment

Choose a reason for hiding this comment

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

Excellent work 👍

@tamasvajk tamasvajk merged commit d723905 into github:main Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants