Skip to content

Commit 2defc3d

Browse files
authored
Disable CET compatibility setting on apphost when CetCompat property is set to false (dotnet#41483)
1 parent 4ebb500 commit 2defc3d

File tree

6 files changed

+105
-2
lines changed

6 files changed

+105
-2
lines changed

src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class CreateAppHost : TaskBase
4646

4747
public bool EnableMacOSCodeSign { get; set; } = false;
4848

49+
public bool DisableCetCompat { get; set; } = false;
50+
4951
protected override void ExecuteCore()
5052
{
5153
try
@@ -64,12 +66,12 @@ protected override void ExecuteCore()
6466
appBinaryFilePath: AppBinaryName,
6567
windowsGraphicalUserInterface: isGUI,
6668
assemblyToCopyResourcesFrom: resourcesAssembly,
67-
enableMacOSCodeSign: EnableMacOSCodeSign);
69+
enableMacOSCodeSign: EnableMacOSCodeSign,
70+
disableCetCompat: DisableCetCompat);
6871
return;
6972
}
7073
catch (Exception ex) when (ex is IOException ||
7174
ex is UnauthorizedAccessException ||
72-
ex is HResultException ||
7375
(ex is AggregateException && (ex.InnerException is IOException || ex.InnerException is UnauthorizedAccessException)))
7476
{
7577
if (Retries < 0 || attempts == Retries)

src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets

+1
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ Copyright (c) .NET Foundation. All rights reserved.
715715
Retries="$(CopyRetryCount)"
716716
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
717717
EnableMacOSCodeSign="$(_EnableMacOSCodeSign)"
718+
DisableCetCompat="$(_DisableCetCompat)"
718719
/>
719720
</Target>
720721

src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets

+2
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ Copyright (c) .NET Foundation. All rights reserved.
719719
'$(SingleFileHostSourcePath)' != '' and
720720
'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and
721721
$([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), 5.0))">true</_UseSingleFileHostForPublish>
722+
<_DisableCetCompat Condition="'$(CetCompat)' == 'false'">true</_DisableCetCompat>
722723
</PropertyGroup>
723724
</Target>
724725

@@ -746,6 +747,7 @@ Copyright (c) .NET Foundation. All rights reserved.
746747
Retries="$(CopyRetryCount)"
747748
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
748749
EnableMacOSCodeSign="$(_EnableMacOSCodeSign)"
750+
DisableCetCompat="$(_DisableCetCompat)"
749751
/>
750752
</Target>
751753

test/Microsoft.NET.Build.Tests/AppHostTests.cs

+35
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,41 @@ public void It_uses_an_apphost_based_on_platform_target(string target)
284284
}
285285
}
286286

287+
[Theory]
288+
[InlineData(null)]
289+
[InlineData(true)]
290+
[InlineData(false)]
291+
public void It_can_disable_cetcompat(bool? cetCompat)
292+
{
293+
string rid = "win-x64"; // CET compat support is currently only on Windows x64
294+
var testProject = new TestProject()
295+
{
296+
Name = "CetCompat",
297+
TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
298+
RuntimeIdentifier = rid,
299+
IsExe = true,
300+
};
301+
if (cetCompat.HasValue)
302+
{
303+
testProject.AdditionalProperties.Add("CetCompat", cetCompat.ToString());
304+
}
305+
306+
var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: cetCompat.HasValue ? cetCompat.Value.ToString() : "default");
307+
var buildCommand = new BuildCommand(testAsset);
308+
buildCommand.Execute()
309+
.Should()
310+
.Pass();
311+
312+
var outputDirectory = buildCommand.GetOutputDirectory(runtimeIdentifier: rid);
313+
string apphostPath = Path.Combine(outputDirectory.FullName, $"{testProject.Name}.exe");
314+
bool isCetCompatible = PeReaderUtils.IsCetCompatible(apphostPath);
315+
316+
// CetCompat not set : enabled
317+
// CetCompat = true : enabled
318+
// CetCompat = false : disabled
319+
isCetCompatible.Should().Be(!cetCompat.HasValue || cetCompat.Value);
320+
}
321+
287322
[WindowsOnlyFact]
288323
public void AppHost_contains_resources_from_the_managed_dll()
289324
{

test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishASingleFileApp.cs

+37
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,43 @@ public void It_runs_single_file_apps(string targetFramework, bool selfContained,
805805
.HaveStdOutContaining("Hello World");
806806
}
807807

808+
[Theory]
809+
[InlineData(null)]
810+
[InlineData(true)]
811+
[InlineData(false)]
812+
public void It_can_disable_cetcompat(bool? cetCompat)
813+
{
814+
string rid = "win-x64"; // CET compat support is currently only on Windows x64
815+
var testProject = new TestProject()
816+
{
817+
Name = "CetCompat",
818+
TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
819+
RuntimeIdentifier = rid,
820+
IsExe = true,
821+
};
822+
if (cetCompat.HasValue)
823+
{
824+
testProject.AdditionalProperties.Add("CetCompat", cetCompat.ToString());
825+
}
826+
827+
var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: cetCompat.HasValue ? cetCompat.Value.ToString() : "default");
828+
var publishCommand = new PublishCommand(testAsset);
829+
publishCommand.Execute(PublishSingleFile)
830+
.Should()
831+
.Pass();
832+
833+
DirectoryInfo publishDir = publishCommand.GetOutputDirectory(
834+
targetFramework: testProject.TargetFrameworks,
835+
runtimeIdentifier: rid);
836+
string singleFilePath = Path.Combine(publishDir.FullName, $"{testProject.Name}.exe");
837+
bool isCetCompatible = PeReaderUtils.IsCetCompatible(singleFilePath);
838+
839+
// CetCompat not set : enabled
840+
// CetCompat = true : enabled
841+
// CetCompat = false : disabled
842+
isCetCompatible.Should().Be(!cetCompat.HasValue || cetCompat.Value);
843+
}
844+
808845
[RequiresMSBuildVersionTheory("16.8.0")]
809846
[InlineData(false)]
810847
[InlineData(true)]

test/Microsoft.NET.TestFramework/Utilities/PeReaderUtils.cs

+26
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,32 @@ public static bool IsCrossgened(this PEReader peReader)
2222
return isCrossgened;
2323
}
2424

25+
public static bool IsCetCompatible(string filePath)
26+
{
27+
using (PEReader reader = new PEReader(new FileStream(filePath, FileMode.Open, FileAccess.Read)))
28+
{
29+
foreach (DebugDirectoryEntry entry in reader.ReadDebugDirectory())
30+
{
31+
// https://learn.microsoft.com/windows/win32/debug/pe-format#debug-type
32+
const int IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20;
33+
if ((int)entry.Type != IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS)
34+
continue;
35+
36+
// Get the extended DLL characteristics debug directory entry
37+
PEMemoryBlock data = reader.GetSectionData(entry.DataRelativeVirtualAddress);
38+
ushort dllCharacteristics = data.GetReader().ReadUInt16();
39+
40+
// Check for the CET compat bit
41+
// https://learn.microsoft.com/windows/win32/debug/pe-format#extended-dll-characteristics
42+
const ushort IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT = 0x1;
43+
return (dllCharacteristics & IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT) != 0;
44+
}
45+
46+
// Not marked compatible - no debug directory entry for extended DLL characteristics
47+
return false;
48+
}
49+
}
50+
2551
public static string GetAssemblyAttributeValue(string assemblyPath, string attributeName)
2652
{
2753
if (!File.Exists(assemblyPath))

0 commit comments

Comments
 (0)