diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a5134ea83618a9..9f0e420032d725 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "11.0.0-prerelease.26168.1", + "version": "11.0.0-prerelease.26230.4", "commands": [ "xharness" ] diff --git a/NuGet.config b/NuGet.config index 73e4341a1899e6..f59515837df9a1 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,7 +9,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d60f5fd368b25d..2e459f11930c01 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,9 @@ + - + https://github.com/dotnet/icu - cda39b50d316241f6c9dea44ed241a83aaccdfac + 67d0db7e61ed6ba212622a174bed04ef47aa2118 https://github.com/dotnet/msquic @@ -12,37 +13,37 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b https://github.com/dotnet/command-line-api @@ -64,25 +65,25 @@ 4fe10b2349082f474928ac1b97ce207b70dc2307 - + https://github.com/dotnet/emsdk - e2909c00ead6fb5d18a5167ca78f259c639084e0 + b634e009d59f72e9254f984a6b89e685955e0eb8 - + https://github.com/dotnet/emsdk - e2909c00ead6fb5d18a5167ca78f259c639084e0 + b634e009d59f72e9254f984a6b89e685955e0eb8 - + https://github.com/dotnet/emsdk - e2909c00ead6fb5d18a5167ca78f259c639084e0 + b634e009d59f72e9254f984a6b89e685955e0eb8 - - https://github.com/dotnet/source-build-reference-packages - a9cadb09ddcc99b1e535efb0648047634f0c4f40 - + + https://github.com/dotnet/source-build-assets + 8e19a1b4f607fcbecc4edbd322d77a60d4e25c3c + @@ -92,195 +93,195 @@ - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b - + https://github.com/dotnet/llvm-project - e5e818c4965ecaba05ab3377a7ce7ab2bb74da52 + caaecce55bb1983ca568b834226fa498e04d4d5b https://github.com/dotnet/runtime @@ -320,21 +321,21 @@ https://github.com/dotnet/runtime b030c4dfdfa1bf287f10f96006619a06bc2000ae - + https://github.com/dotnet/xharness - 607b3de9cf2dbfec6734e686e68d2813b40b2b51 + 92962e5c46ac08a66ded4c5696209cc60f1a232f - + https://github.com/dotnet/xharness - 607b3de9cf2dbfec6734e686e68d2813b40b2b51 + 92962e5c46ac08a66ded4c5696209cc60f1a232f - + https://github.com/dotnet/xharness - 607b3de9cf2dbfec6734e686e68d2813b40b2b51 + 92962e5c46ac08a66ded4c5696209cc60f1a232f - + https://github.com/dotnet/arcade - 29a2184303379b9840b70e7cdb2faa0f39833b89 + 6c1a2a69259c3f66af6176c9c70021b3d9989504 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -356,9 +357,9 @@ https://github.com/dotnet/hotreload-utils 2a70fdb0e55455dc067a6f24582b5d9ed75ed373 - + https://github.com/dotnet/runtime-assets - 70d5fa7d7d041da17e6f3827f55d7d9ceaffdb12 + 45c55b97e4006bac84d19a382ace44ef3dcb8d94 https://github.com/dotnet/roslyn @@ -372,11 +373,11 @@ https://github.com/dotnet/roslyn dc344ef24932dcd53cdd24c15364a5996bc6a675 - + https://github.com/dotnet/roslyn-analyzers 5ef1abb57ce3df89eae65ecadeb1ddbab323ae05 - + https://github.com/dotnet/roslyn-analyzers 5ef1abb57ce3df89eae65ecadeb1ddbab323ae05 diff --git a/eng/Versions.props b/eng/Versions.props index 7221cec9c4c75d..aa06b49159d30d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,11 +1,11 @@ - 9.0.16 + 9.0.17 9 0 - 16 + 17 9.0.100 8.0.$([MSBuild]::Add($(PatchVersion),11)) 7.0.20 @@ -35,8 +35,8 @@ - 3.11.0-beta1.26057.1 - 9.0.0-preview.26057.1 + 3.11.0-beta1.26075.3 + 9.0.0-preview.26075.3 9.0.109 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 2.9.0-beta.26123.3 - 9.0.0-beta.26123.3 - 2.9.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 - 9.0.0-beta.26123.3 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 2.9.0-beta.26261.1 + 9.0.0-beta.26261.1 + 2.9.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 + 9.0.0-beta.26261.1 1.4.0 @@ -146,20 +146,20 @@ 8.0.0 4.5.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 - 9.0.0-beta.25625.4 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 + 9.0.0-beta.26071.2 1.0.0-prerelease.24462.2 1.0.0-prerelease.24462.2 @@ -190,9 +190,9 @@ 1.4.0 17.4.0-preview-20220707-01 - 11.0.0-prerelease.26168.1 - 11.0.0-prerelease.26168.1 - 11.0.0-prerelease.26168.1 + 11.0.0-prerelease.26230.4 + 11.0.0-prerelease.26230.4 + 11.0.0-prerelease.26230.4 9.0.0-alpha.0.26152.4 3.12.0 @@ -226,31 +226,30 @@ 9.0.0-rtm.24511.16 - 9.0.0-rtm.25627.1 - 9.0.0-rtm.24466.4 + 9.0.0-rtm.26261.1 2.4.18 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 - 9.0.16-servicing.26221.3 - 9.0.16 + 9.0.17-servicing.26258.3 + 9.0.17 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100Version) - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 - 19.1.0-alpha.1.26202.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 + 19.1.0-alpha.1.26256.3 3.1.7 1.0.406601 diff --git a/eng/pipelines/common/evaluate-default-paths.yml b/eng/pipelines/common/evaluate-default-paths.yml index 28991920c330d5..b950e3a923b60b 100644 --- a/eng/pipelines/common/evaluate-default-paths.yml +++ b/eng/pipelines/common/evaluate-default-paths.yml @@ -258,22 +258,6 @@ jobs: - ${{ parameters._const_paths._always_exclude }} - ${{ parameters._const_paths._perf_pipeline_specific_only }} - - subset: wasmdebuggertests - combined: true - include: - - ${{ parameters._const_paths._wasm_chrome }} - - src/libraries/System.Runtime.InteropServices/* - - src/libraries/System.Runtime.InteropServices.JavaScript/* - - src/mono/mono/* - - src/mono/browser/debugger/* - - src/mono/browser/runtime/* - - ${{ parameters._const_paths._wasm_src_native }} - - ${{ parameters._const_paths._wasm_pipelines }} - exclude: - - src/mono/nuget/* - - ${{ parameters._const_paths._always_exclude }} - - ${{ parameters._const_paths._perf_pipeline_specific_only }} - # wasm/runtimetests need to be run - subset: wasm_runtimetests combined: true diff --git a/eng/pipelines/common/templates/wasm-debugger-tests.yml b/eng/pipelines/common/templates/wasm-debugger-tests.yml deleted file mode 100644 index a61321809bc552..00000000000000 --- a/eng/pipelines/common/templates/wasm-debugger-tests.yml +++ /dev/null @@ -1,62 +0,0 @@ -parameters: - alwaysRun: false - isExtraPlatformsBuild: false - isWasmOnlyBuild: false - browser: 'chrome' - shouldContinueOnError: false - runOnlyOnWasmOnlyPipelines: false - extraBuildArgs: '' - nameSuffix: '' - platforms: [] - -jobs: - -# Wasm debugger tests - windows -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/common/global-build-job.yml - helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml - buildConfig: Release - runtimeFlavor: mono - platforms: ${{ parameters.platforms }} - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - variables: - # map dependencies variables to local variables - - name: alwaysRunVar - value: ${{ parameters.alwaysRun }} - - name: shouldRunOnDefaultPipelines - value: $[ - or( - eq(variables['wasmDarcDependenciesChanged'], true), - eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true), - eq(stageDependencies.EvaluatePaths.evaluate_paths_outputs['DarcDependenciesChanged.Microsoft_DotNet_HotReload_Utils_Generator_BuildTool'], true), - eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'], true)) - ] - jobParameters: - testGroup: innerloop - isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }} - ${{ if eq(parameters.nameSuffix, '') }}: - nameSuffix: Mono_DebuggerTests_${{ parameters.browser }} - ${{ else }}: - nameSuffix: ${{ parameters.nameSuffix }} - buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false /p:BrowserHost=$(_hostedOs) /p:DebuggerHost=${{ parameters.browser }} ${{ parameters.extraBuildArgs }} - timeoutInMinutes: 180 - # if !alwaysRun, then: - # if this is runtime-wasm (isWasmOnlyBuild): - # - then run only if it would not have run on default pipelines (based - # on path changes) - # - else run based on path changes - condition: >- - or( - eq(variables['alwaysRunVar'], true), - and( - eq(variables['isDefaultPipeline'], variables['shouldRunOnDefaultPipelines']), - eq(${{ parameters.isWasmOnlyBuild }}, ${{ parameters.runOnlyOnWasmOnlyPipelines }}))) - postBuildSteps: - - template: /eng/pipelines/libraries/helix.yml - parameters: - creator: dotnet-bot - testRunNamePrefixSuffix: Mono_${{ parameters.browser }}_$(_BuildConfig) - extraHelixArguments: /p:BrowserHost=$(_hostedOs) /p:_DebuggerHosts=${{ parameters.browser }} - scenarios: - - wasmdebuggertests diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml index 259982aff35451..32899b04862407 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml @@ -55,17 +55,6 @@ jobs: runAOT: true alwaysRun: true - # Wasm Debugger tests - firefox - - template: /eng/pipelines/common/templates/wasm-debugger-tests.yml - parameters: - platforms: - - browser_wasm_firefox - browser: firefox - extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) - ## ff tests are unstable currently - shouldContinueOnError: true - alwaysRun: true - # Disabled for now #- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml #parameters: @@ -240,42 +229,6 @@ jobs: isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} -- ${{ if and(ne(parameters.isRollingBuild, true), or(ne(parameters.excludeNonLibTests, true), eq(parameters.debuggerTestsOnly, true))) }}: - # Debugger tests - - template: /eng/pipelines/common/templates/wasm-debugger-tests.yml - parameters: - platforms: - - browser_wasm - - browser_wasm_win - extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) - isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} - isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} - - - template: /eng/pipelines/common/templates/wasm-debugger-tests.yml - parameters: - platforms: - - browser_wasm_firefox - browser: firefox - extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) - isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} - isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} - alwaysRun: ${{ parameters.isWasmOnlyBuild }} - # ff tests are unstable currently - shouldContinueOnError: true - - # Active Issue https://github.com/dotnet/runtime/issues/98771 - # - template: /eng/pipelines/common/templates/wasm-debugger-tests.yml - # parameters: - # platforms: - # - Browser_wasm - # - Browser_wasm_win - # extraBuildArgs: /p:WasmEnableThreads=true /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) - # nameSuffix: DebuggerTests_MultiThreaded - # alwaysRun: ${{ parameters.isWasmOnlyBuild }} - # isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} - # isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} - # runOnlyOnWasmOnlyPipelines: true - # Disable for now #- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml #parameters: diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 4f5a71dcd39c10..f94bd937525cd6 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -907,15 +907,6 @@ extends: alwaysRun: ${{ variables.isRollingBuild }} extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) - # Wasm Debugger tests - - template: /eng/pipelines/common/templates/wasm-debugger-tests.yml - parameters: - platforms: - - browser_wasm - - browser_wasm_win - alwaysRun: ${{ variables.isRollingBuild }} - extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) - # Wasm runtime tests - template: /eng/pipelines/common/templates/wasm-runtime-tests.yml parameters: diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index a3406b78f43686..44c7bc6805fa80 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -245,9 +245,8 @@ + Text="Only supported scenarios are WasmTestOnV8, WasmTestOnChrome, WasmTestOnFirefox and BuildWasmApps at the moment. It was $(Scenario)." /> diff --git a/global.json b/global.json index 782fb2436191bd..df7a579aeb2df1 100644 --- a/global.json +++ b/global.json @@ -1,16 +1,16 @@ { "sdk": { - "version": "9.0.113", + "version": "9.0.116", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "9.0.113" + "dotnet": "9.0.116" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.26123.3", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.26123.3", - "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.26123.3", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.26261.1", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.26261.1", + "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.26261.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "9.0.0-rtm.24511.16" diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 0a74288cb0b7e8..edf370e0fe2a42 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -1331,17 +1331,17 @@ bool Compiler::optDeriveLoopCloningConditions(FlowGraphNaturalLoop* loop, LoopCl // GT_LE loop test: (start <= end) ==> (end < arrLen) // // Decreasing loops - // GT_GT loop test: (end > start) ==> (end <= arrLen) - // GT_GE loop test: (end >= start) ==> (end < arrLen) + // Always check if iter var is less than array length. genTreeOps opLimitCondition; switch (iterInfo->TestOper()) { case GT_LT: - case GT_GT: + opLimitCondition = GT_LE; break; case GT_LE: case GT_GE: + case GT_GT: opLimitCondition = GT_LT; break; default: diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs index 6b246e4ee4567e..2fa9a65e6e1467 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs @@ -45,7 +45,9 @@ internal static unsafe void StringToByValAnsiString(string str, byte* pNative, i fixed (char* pManaged = str) { - PInvokeMarshal.StringToAnsiString(pManaged, lenUnicode, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar); + PInvokeMarshal.StringToAnsiString(pManaged, lenUnicode, pNative, + /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar, + nativeByteLength: charCount); } } else diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs index 81c9482a557580..3d5de64135b40b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -501,9 +501,14 @@ public static unsafe char AnsiCharToWideChar(byte nativeValue) } // c# string (UTF-16) to UTF-8 encoded byte array + // If specified, nativeByteLength represents the length of the output buffer and this function + // will not write more than that many bytes. If negative, this function writes as many bytes + // as needed to encode the string. internal static unsafe byte* StringToAnsiString(char* pManaged, int lenUnicode, byte* pNative, bool terminateWithNull, - bool bestFit, bool throwOnUnmappableChar) + bool bestFit, bool throwOnUnmappableChar, int nativeByteLength = -1) { + Debug.Assert(pNative != null || nativeByteLength == -1, "Native buffer should not be null when nativeByteLength is specified."); + bool allAscii = Ascii.IsValid(new ReadOnlySpan(pManaged, lenUnicode)); int length; @@ -516,6 +521,18 @@ public static unsafe char AnsiCharToWideChar(byte nativeValue) length = GetByteCount(pManaged, lenUnicode); } + // Clamp to nativeByteLength when caller provides a bounded output buffer (ByValTStr). + // For non-ASCII, ConvertWideCharToMultiByte will throw (Unix) or truncate (Windows) + // when the encoded bytes exceed the clamped length. This matches CoreCLR behavior. + if (nativeByteLength >= 0) + { + int maxBytesToWrite = terminateWithNull ? Math.Max(0, nativeByteLength - 1) : nativeByteLength; + if (length > maxBytesToWrite) + { + length = maxBytesToWrite; + } + } + if (pNative == null) { pNative = (byte*)Marshal.AllocCoTaskMem(checked(length + 1)); @@ -535,8 +552,8 @@ public static unsafe char AnsiCharToWideChar(byte nativeValue) throwOnUnmappableChar); } - // Zero terminate - if (terminateWithNull) + // Zero terminate if requested and the buffer is not specified to be size 0. + if (terminateWithNull && nativeByteLength != 0) *(pNative + length) = 0; return pNative; diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs index 8191f0bacd18fe..4883f9775e2d67 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs @@ -351,7 +351,8 @@ internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, b string? fileDestinationPath = GetFullDestinationPath( destinationDirectoryPath, Path.IsPathFullyQualified(name) ? name : Path.Join(destinationDirectoryPath, name)); - if (fileDestinationPath == null) + + if (fileDestinationPath is null || FilePathEscapesDirectory(destinationDirectoryPath, fileDestinationPath)) { throw new IOException(SR.Format(SR.TarExtractingResultsFileOutside, name, destinationDirectoryPath)); } @@ -372,7 +373,7 @@ internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, b string? linkDestination = GetFullDestinationPath( destinationDirectoryPath, Path.IsPathFullyQualified(linkName) ? linkName : Path.Join(Path.GetDirectoryName(fileDestinationPath), linkName)); - if (linkDestination is null) + if (linkDestination is null || FilePathEscapesDirectory(destinationDirectoryPath, linkDestination)) { throw new IOException(SR.Format(SR.TarExtractingResultsLinkOutside, linkName, destinationDirectoryPath)); } @@ -387,7 +388,7 @@ internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, b string? linkDestination = GetFullDestinationPath( destinationDirectoryPath, Path.Join(destinationDirectoryPath, linkName)); - if (linkDestination is null) + if (linkDestination is null || FilePathEscapesDirectory(destinationDirectoryPath, linkDestination)) { throw new IOException(SR.Format(SR.TarExtractingResultsLinkOutside, linkName, destinationDirectoryPath)); } @@ -398,6 +399,101 @@ internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, b return (fileDestinationPath, linkTargetPath); } + // Check if the file destination path or the link target path escapes the destination directory, by walking through the relative path components and resolving symlinks at each step. + private static bool FilePathEscapesDirectory(string destinationDirectoryPath, string fileDestinationPath) + { + // Windows is case insensitive while Linux is case sensitive + // This ensures the comparison is consistent with how the OS would resolve the paths + StringComparison pathComparison = OperatingSystem.IsWindows() + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + string resolvedDest = ResolvePhysicalPath(destinationDirectoryPath); + string destPrefix = resolvedDest.EndsWith(Path.DirectorySeparatorChar) + ? resolvedDest + : resolvedDest + Path.DirectorySeparatorChar; + + // Normalize file path (resolves .. and . but not symlinks) + string normalizedFile = Path.GetFullPath(fileDestinationPath); + + // Walk relative components, resolving symlinks at each step + string relative = normalizedFile.Substring(resolvedDest.Length) + .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + string[] components = relative.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, + StringSplitOptions.RemoveEmptyEntries); + + string current = resolvedDest; + + foreach (string component in components) + { + current = Path.Combine(current, component); + + if (Path.Exists(current)) + { + string? resolved = ResolveSymlink(current); + if (resolved is null) + { + return true; + } + current = resolved; + } + + string normalizedCurrent = Path.GetFullPath(current); + if (!normalizedCurrent.StartsWith(destPrefix, pathComparison) && + !normalizedCurrent.Equals(resolvedDest, pathComparison)) + { + return true; + } + } + + return false; + } + + private static string? ResolveSymlink(string path) + { + FileSystemInfo? target = new FileInfo(path).ResolveLinkTarget(returnFinalTarget: true); + + if (target is null) + { + return Path.GetFullPath(path); + } + + return target.FullName; + } + + // Resolves the full path of the specified path, resolving symlinks at each step. + // This is needed to mitigate malicious entries in the archive that could lead to writing files outside of the intended directory. + private static string ResolvePhysicalPath(string path) + { + string fullPath = Path.GetFullPath(path); + string? root = Path.GetPathRoot(fullPath); + + if (root is null) + { + return fullPath; + } + + string[] components = fullPath.Substring(root.Length) + .Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); + string current = root; + foreach (string component in components) + { + current = Path.Combine(current, component); + if (Path.Exists(current)) + { + string? resolved = ResolveSymlink(current); + if (resolved is null) + { + return current; + } + current = resolved; + } + } + + return current; + } + // Returns the full destination path if the path is the destinationDirectory or a subpath. Otherwise, returns null. private static string? GetFullDestinationPath(string destinationDirectoryFullPath, string qualifiedPath) { diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs index 9ffbcc00792703..d6483b8444b1c6 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; +using System.Text; using Xunit; namespace System.Formats.Tar.Tests @@ -368,5 +369,94 @@ public void LinkBeforeTarget() Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); Assert.True(File.Exists(linkPath), $"{linkPath}' does not exist."); } + + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] + public void ExtractToDirectory_RejectsSymlinkDirectoryTraversal_WithNestedFile() + { + using TempDirectory root = new TempDirectory(); + string destDir = Path.Combine(root.Path, "dest"); + Directory.CreateDirectory(destDir); + + // Absolute path outside destDir + string linkTarget = "/tmp/outside"; + + string tarPath = Path.Combine(root.Path, "symlink_dir_traversal.tar"); + using (FileStream stream = new FileStream(tarPath, FileMode.Create, FileAccess.Write)) + using (TarWriter writer = new TarWriter(stream, leaveOpen: false)) + { + // symlink: "link" -> "/tmp/outside" + writer.WriteEntry(new PaxTarEntry(TarEntryType.SymbolicLink, "link") + { + LinkName = linkTarget + }); + + // file: "link/test.txt" with "hello" + byte[] content = Encoding.UTF8.GetBytes("hello"); + var fileEntry = new PaxTarEntry(TarEntryType.RegularFile, "link/test.txt") + { + DataStream = new MemoryStream(content, writable: false) + }; + + fileEntry.DataStream.Position = 0; + writer.WriteEntry(fileEntry); + } + + Assert.Throws(() => TarFile.ExtractToDirectory(tarPath, destDir, overwriteFiles: true)); + + // Nothing should be created in dest + string linkPath = Path.Combine(destDir, "link"); + string outsideFilePath = Path.Combine(destDir, "link", "test.txt"); + Assert.False(File.Exists(linkPath) || Directory.Exists(linkPath), "link should not have been created."); + Assert.False(File.Exists(outsideFilePath) || Directory.Exists(linkPath), "traversal link should not have been created."); + } + + + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] + public void ExtractToDirectory_RejectsChainedSymlinkDirectoryTraversal_WithNestedFile() + { + // dir a/ + // symlink a/b → . + // symlink a/b/c → . + // symlink a/b/c/d → ../../outside + // file a/d/ pwned.txt escapes + + using TempDirectory root = new TempDirectory(); + string destDir = Path.Combine(root.Path, "dest"); + Directory.CreateDirectory(destDir); + + string tarPath = Path.Combine(root.Path, "chained_symlink_traversal.tar"); + using (FileStream stream = new FileStream(tarPath, FileMode.Create, FileAccess.Write)) + using (TarWriter writer = new TarWriter(stream, leaveOpen: false)) + { + writer.WriteEntry(new PaxTarEntry(TarEntryType.Directory, "a/")); + + writer.WriteEntry(new PaxTarEntry(TarEntryType.SymbolicLink, "a/b") { LinkName = "." }); + + writer.WriteEntry(new PaxTarEntry(TarEntryType.SymbolicLink, "a/b/c") { LinkName = "." }); + + writer.WriteEntry(new PaxTarEntry(TarEntryType.SymbolicLink, "a/b/c/d") { LinkName = "../../outside" }); + + var pwned = new PaxTarEntry(TarEntryType.RegularFile, "a/d/pwned.txt") + { + DataStream = new MemoryStream(Encoding.UTF8.GetBytes("pwned")) + }; + writer.WriteEntry(pwned); + } + + if (OperatingSystem.IsWindows()) + { + // Windows only creates file symlinks and trying to process a directory symlink will throw UnauthorizedAccessException instead of IOException + Assert.Throws(() => TarFile.ExtractToDirectory(tarPath, destDir, overwriteFiles: true)); + } + else + { + Assert.Throws(() => TarFile.ExtractToDirectory(tarPath, destDir, overwriteFiles: true)); + } + + string outsideDir = Path.Combine(root.Path, "outside"); + Assert.False(Directory.Exists(outsideDir), "outside/directory should not have been created."); + Assert.False(File.Exists(Path.Combine(outsideDir, "pwned.txt")), "pwned.txt should not have been written outside destination."); + + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs index 59cefc137cef81..4a906e0fc6be1a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs @@ -363,5 +363,58 @@ public struct InnerStruct public InnerStruct s; public byte b; } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct StructWithByValString + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] + public string? Name; + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public void StructureToPtr_ByValTStr_MultiByte_Overflow() + { + // ByValTStr uses UTF-8 on Unix. € is 3 bytes, so the string exceeds the specified SizeConst + var payload = new StructWithByValString { Name = "€€€" }; + + int size = Marshal.SizeOf(); + IntPtr buffer = Marshal.AllocHGlobal(size + 1); + byte sentinelValue = 0xFF; + try + { + Marshal.WriteByte(buffer, size, sentinelValue); + Assert.Throws(() => Marshal.StructureToPtr(payload, buffer, false)); + Assert.Equal(sentinelValue, Marshal.ReadByte(buffer, size)); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + [Fact] + public void StructureToPtr_ByValTStr_Ascii_TruncatesLongString() + { + var payload = new StructWithByValString { Name = "abcdef" }; + + int size = Marshal.SizeOf(); + IntPtr buffer = Marshal.AllocHGlobal(size + 1); + byte sentinelValue = 0xFF; + try + { + Marshal.WriteByte(buffer, size, sentinelValue); + Marshal.StructureToPtr(payload, buffer, false); + Assert.Equal((byte)'a', Marshal.ReadByte(buffer, 0)); + Assert.Equal((byte)'b', Marshal.ReadByte(buffer, 1)); + Assert.Equal((byte)'c', Marshal.ReadByte(buffer, 2)); + Assert.Equal((byte)0, Marshal.ReadByte(buffer, 3)); + Assert.Equal(sentinelValue, Marshal.ReadByte(buffer, size)); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCrlCache.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCrlCache.cs index 52b6b967c0640d..ff047a1e755863 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCrlCache.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCrlCache.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Formats.Asn1; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates.Asn1; +using System.Threading; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -23,6 +25,8 @@ internal static class OpenSslCrlCache X509Persistence.CryptographyFeatureName, X509Persistence.OcspSubFeatureName); + private static readonly MruCrlCache s_crlCache = new(); + private const ulong X509_R_CERT_ALREADY_IN_HASH_TABLE = 0x0B07D065; public static void AddCrlForCertificate( @@ -73,6 +77,95 @@ public static void AddCrlForCertificate( } private static bool AddCachedCrl(string crlFileName, SafeX509StoreHandle store, DateTime verificationTime) + { + // OpenSSL is going to convert our input time to universal, so we should be in Local or + // Unspecified (local-assumed). + Debug.Assert( + verificationTime.Kind != DateTimeKind.Utc, + "UTC verificationTime should have been normalized to Local"); + + if (s_crlCache.TryGetValueAndUpRef(crlFileName, out CachedCrlEntry? cacheEntry)) + { + try + { + Debug.Assert(cacheEntry is not null); + + if (verificationTime < cacheEntry.Expiration) + { + if (OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheInMemoryHit(cacheEntry.Expiration); + } + + AttachCrl(store, cacheEntry.CrlHandle); + return true; + } + + if (OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheInMemoryExpired(verificationTime, cacheEntry.Expiration); + } + } + finally + { + cacheEntry.CrlHandle.DangerousRelease(); + } + } + else if (OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheInMemoryMiss(); + } + + // Check the disk cache. + // For uncached this is the first load, for collected it's a reload, + // for expired it's checking to see if another process has updated the disk cache. + CachedCrlEntry? diskCacheEntry = CheckDiskCache(crlFileName, verificationTime); + + if (diskCacheEntry is null) + { + return false; + } + + UpdateCacheAndAttachCrl(crlFileName, store, diskCacheEntry); + return true; + } + + private static void UpdateCacheAndAttachCrl(string crlFileName, SafeX509StoreHandle store, CachedCrlEntry newEntry) + { + Debug.Assert(!newEntry.CrlHandle.IsInvalid); + CachedCrlEntry toAttach = s_crlCache.AddOrUpdateAndUpRef(crlFileName, newEntry); + + try + { + AttachCrl(store, toAttach.CrlHandle); + } + finally + { + toAttach.CrlHandle.DangerousRelease(); + } + } + + private static void AttachCrl(SafeX509StoreHandle store, SafeX509CrlHandle crl) + { + Debug.Assert(!crl.IsInvalid); + + // X509_STORE_add_crl will increase the refcount on the CRL object, + // so we don't need to worry about our copy getting cleaned up as a weak reference. + if (!Interop.Crypto.X509StoreAddCrl(store, crl)) + { + // Ignore error "cert already in store", throw on anything else. In any case the error queue will be cleared. + if (X509_R_CERT_ALREADY_IN_HASH_TABLE == Interop.Crypto.ErrPeekLastError()) + { + Interop.Crypto.ErrClearError(); + } + else + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + } + + private static CachedCrlEntry? CheckDiskCache(string crlFileName, DateTime verificationTime) { string crlFile = GetCachedCrlPath(crlFileName); @@ -83,7 +176,7 @@ private static bool AddCachedCrl(string crlFileName, SafeX509StoreHandle store, try { - return AddCachedCrlCore(crlFile, store, verificationTime); + return CheckDiskCacheCore(crlFile, verificationTime); } finally { @@ -94,7 +187,7 @@ private static bool AddCachedCrl(string crlFileName, SafeX509StoreHandle store, } } - private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, DateTime verificationTime) + private static CachedCrlEntry? CheckDiskCacheCore(string crlFile, DateTime verificationTime) { using (SafeBioHandle bio = Interop.Crypto.BioNewFile(crlFile, "rb")) { @@ -106,12 +199,11 @@ private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, } Interop.Crypto.ErrClearError(); - return false; + return null; } - // X509_STORE_add_crl will increase the refcount on the CRL object, so we should still - // dispose our copy. - using (SafeX509CrlHandle crl = Interop.Crypto.PemReadBioX509Crl(bio)) + SafeX509CrlHandle crl = Interop.Crypto.PemReadBioX509Crl(bio); + { if (crl.IsInvalid) { @@ -120,8 +212,9 @@ private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, OpenSslX509ChainEventSource.Log.CrlCacheDecodeError(); } + crl.Dispose(); Interop.Crypto.ErrClearError(); - return false; + return null; } // If crl.LastUpdate is in the past, downloading a new version isn't really going @@ -144,14 +237,15 @@ private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, try { - nextUpdate = File.GetLastWriteTime(crlFile).AddDays(3); + nextUpdate = ExpirationTimeFromCacheFileTime(File.GetLastWriteTime(crlFile)); } catch { // We couldn't determine when the CRL was last written to, // so consider it expired. Debug.Fail("Failed to get the last write time of the CRL file"); - return false; + crl.Dispose(); + return null; } } else @@ -159,12 +253,6 @@ private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, nextUpdate = OpenSslX509CertificateReader.ExtractValidityDateTime(nextUpdatePtr); } - // OpenSSL is going to convert our input time to universal, so we should be in Local or - // Unspecified (local-assumed). - Debug.Assert( - verificationTime.Kind != DateTimeKind.Utc, - "UTC verificationTime should have been normalized to Local"); - // In the event that we're to-the-second accurate on the match, OpenSSL will consider this // to be already expired. if (nextUpdate <= verificationTime) @@ -174,20 +262,8 @@ private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, OpenSslX509ChainEventSource.Log.CrlCacheExpired(nextUpdate, verificationTime); } - return false; - } - - if (!Interop.Crypto.X509StoreAddCrl(store, crl)) - { - // Ignore error "cert already in store", throw on anything else. In any case the error queue will be cleared. - if (X509_R_CERT_ALREADY_IN_HASH_TABLE == Interop.Crypto.ErrPeekLastError()) - { - Interop.Crypto.ErrClearError(); - } - else - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } + crl.Dispose(); + return null; } if (OpenSslX509ChainEventSource.Log.IsEnabled()) @@ -195,7 +271,7 @@ private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, OpenSslX509ChainEventSource.Log.CrlCacheAcceptedFile(nextUpdate); } - return true; + return new CachedCrlEntry(crl, nextUpdate); } } } @@ -206,57 +282,81 @@ private static void DownloadAndAddCrl( SafeX509StoreHandle store, TimeSpan downloadTimeout) { - // X509_STORE_add_crl will increase the refcount on the CRL object, so we should still - // dispose our copy. - using (SafeX509CrlHandle? crl = OpenSslCertificateAssetDownloader.DownloadCrl(url, downloadTimeout)) + CachedCrlEntry? newEntry = DownloadAndCacheCrl(url, crlFileName, downloadTimeout); + + if (newEntry is not null) + { + UpdateCacheAndAttachCrl(crlFileName, store, newEntry); + } + } + + private static CachedCrlEntry? DownloadAndCacheCrl( + string url, + string crlFileName, + TimeSpan downloadTimeout) + { + SafeX509CrlHandle? crl = OpenSslCertificateAssetDownloader.DownloadCrl(url, downloadTimeout); + + // null is a valid return (e.g. no remainingDownloadTime) + if (crl == null || crl.IsInvalid) { - // null is a valid return (e.g. no remainingDownloadTime) - if (crl != null && !crl.IsInvalid) - { - if (!Interop.Crypto.X509StoreAddCrl(store, crl)) - { - // Ignore error "cert already in store", throw on anything else. In any case the error queue will be cleared. - if (X509_R_CERT_ALREADY_IN_HASH_TABLE == Interop.Crypto.ErrPeekLastError()) - { - Interop.Crypto.ErrClearError(); - } - else - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } - } + crl?.Dispose(); + return null; + } - // Saving the CRL to the disk is just a performance optimization for later requests to not - // need to use the network again, so failure to save shouldn't throw an exception or mark - // the chain as invalid. - try - { - string crlFile = GetCachedCrlPath(crlFileName, mkDir: true); + IntPtr nextUpdatePtr = Interop.Crypto.GetX509CrlNextUpdate(crl); + DateTime expiryTime; - using (SafeBioHandle bio = Interop.Crypto.BioNewFile(crlFile, "wb")) - { - if (bio.IsInvalid || Interop.Crypto.PemWriteBioX509Crl(bio, crl) == 0) - { - // No bio, or write failed + // If there is no crl.NextUpdate, this indicates that the CA is not providing + // any more updates to the CRL, or they made a mistake not providing a NextUpdate. + // We'll cache it for a few days to cover the case it was a mistake. + if (nextUpdatePtr == IntPtr.Zero) + { + expiryTime = ExpirationTimeFromCacheFileTime(DateTime.Now); + } + else + { + expiryTime = OpenSslX509CertificateReader.ExtractValidityDateTime(nextUpdatePtr); + } - if (OpenSslX509ChainEventSource.Log.IsEnabled()) - { - OpenSslX509ChainEventSource.Log.CrlCacheWriteFailed(crlFile); - } + // Saving the CRL to the disk is just a performance optimization for later requests to not + // need to use the network again, so failure to save shouldn't throw an exception or mark + // the chain as invalid. + try + { + string crlFile = GetCachedCrlPath(crlFileName, mkDir: true); - Interop.Crypto.ErrClearError(); - } + using (SafeBioHandle bio = Interop.Crypto.BioNewFile(crlFile, "wb")) + { + if (bio.IsInvalid || Interop.Crypto.PemWriteBioX509Crl(bio, crl) == 0) + { + // No bio, or write failed + + if (OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheWriteFailed(crlFile); } - } - catch (UnauthorizedAccessException) { } - catch (IOException) { } - if (OpenSslX509ChainEventSource.Log.IsEnabled()) - { - OpenSslX509ChainEventSource.Log.CrlCacheWriteSucceeded(); + Interop.Crypto.ErrClearError(); } } } + catch (UnauthorizedAccessException) { } + catch (IOException) { } + + if (OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheWriteSucceeded(); + } + + return new CachedCrlEntry(crl, expiryTime); + } + + private static DateTime ExpirationTimeFromCacheFileTime(DateTime cacheFileTime) + { + // CA/Browser Forum says that CRLs should be updated every 4 to 7 days, + // so recheck any cached CRL, that doesn't have a NextUpdate, every 3 days. + return cacheFileTime.AddDays(3); } internal static string GetCachedOcspResponseDirectory() @@ -379,5 +479,300 @@ private static string GetCachedCrlPath(string localFileName, bool mkDir = false) return null; } + + // The MRU CRL cache always does a DangerousAddReference before returning the value, + // so that neither cooperative GC pruning nor a cache-value refresh trigger ReleaseHandle + // on a CRL entry in use. + private sealed class MruCrlCache + { + // Each CRL is only a SafeHandle to the GC, but represents a non-trivial amount of + // native memory, so keep the cache small. + private const int MaxItems = 30; + + private readonly Lock _lock = new(); + + private int _count = -1; + private Node? _head; + private Node? _expire; + + internal CachedCrlEntry AddOrUpdateAndUpRef(string key, CachedCrlEntry value) + { + Debug.Assert(key is not null); + Debug.Assert(value is not null); + Debug.Assert(value.CrlHandle is not null && !value.CrlHandle.IsInvalid); + // Don't assert/enforce anything about expiration, because a) clock-skew, or b) + // the caller might have a verification time that's in the past. + + int hashCode = key.GetHashCode(); + CachedCrlEntry ret = value; + string? fullMemberKey = null; + SafeX509CrlHandle? toDispose = null; + + lock (_lock) + { + // The first time we add something, create the object to monitor for GC events. + if (_count < 0) + { + new GCWatcher(this); + _count = 0; + } + + bool ignore = false; + + if (TryGetNode(hashCode, key, out Node? current)) + { + Debug.Assert(current is not null); + + if (current.Value.Expiration >= value.Expiration) + { + toDispose = value.CrlHandle; + ret = current.Value; + } + else + { + toDispose = current.Value.CrlHandle; + current.Value = value; + } + } + else + { + Node node = new Node(hashCode, key, value); + node.Next = _head; + + if (_count < MaxItems) + { + _count++; + } + else + { + // Because MaxItems is small, it's better to just iterate from head + // instead of using a doubly-linked list. + + Node? previous = null; + Node? cur = _head; + Node? next = cur?.Next; + + while (next is not null) + { + previous = cur; + cur = next; + next = cur.Next; + } + + Debug.Assert(previous is not null); + Debug.Assert(cur is not null); + + previous.Next = null; + toDispose = cur.Value.CrlHandle; + fullMemberKey = cur.Key; + if (cur == _expire) + { + _expire = null; + } + } + + _head = node; + } + + ret.CrlHandle.DangerousAddRef(ref ignore); + } + + toDispose?.Dispose(); + + if (fullMemberKey is not null && OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheInMemoryFull(fullMemberKey); + } + + return ret; + } + + internal bool TryGetValueAndUpRef(string key, [NotNullWhen(true)] out CachedCrlEntry? value) + { + int hashCode = key.GetHashCode(); + + lock (_lock) + { + if (TryGetNode(hashCode, key, out Node? node)) + { + bool ignore = false; + node.Value.CrlHandle.DangerousAddRef(ref ignore); + value = node.Value; + return true; + } + } + + value = null; + return false; + } + + private bool TryGetNode(int hashCode, string key, [NotNullWhen(true)] out Node? value) + { + Debug.Assert(_lock.IsHeldByCurrentThread); + + Node? previous = null; + Node? current = _head; + + while (current is not null) + { + if (current.MatchesKey(hashCode, key)) + { + // If we find the expire node, move expiration to after it, so that promoting it to + // most recent doesn't prune the whole list. + // + // This might, of course, make _expire null. + if (current == _expire) + { + _expire = current.Next; + } + + // Move the found node to the head of the list, maintaining MRU ordering. + if (previous != null) + { + previous.Next = current.Next; + current.Next = _head; + _head = current; + } + + value = current; + return true; + } + + previous = current; + current = current.Next; + } + + value = null; + return false; + } + + private void PruneForGC() + { + // The general flow: + // * The current head is where we expire next time. + // * Under the lock: If there is an expire node, determine the new count by walking to it, + // and unlink it from the previous node. + // * After the lock: Dispose all the values from the prune node onward. + + Node? prune; + int countStart; + int countEnd; + + lock (_lock) + { + prune = _expire; + _expire = _head; + countStart = _count; + + if (prune is null) + { + return; + } + + if (prune == _head) + { + _count = 0; + _head = null; + _expire = null; + } + else + { + Debug.Assert(_head is not null); + int count = 1; + Node current = _head; + + while (current.Next != prune && current.Next is not null) + { + count++; + current = current.Next; + } + + Debug.Assert(current.Next == prune, "The prune node should be in the list"); + current.Next = null; + _count = count; + } + + countEnd = _count; + } + + // `prune` and beyond are now unlinked from the list, so we can dispose its values without holding the lock. + while (prune is not null) + { + prune.Value.CrlHandle.Dispose(); + prune = prune.Next; + } + + if (OpenSslX509ChainEventSource.Log.IsEnabled()) + { + OpenSslX509ChainEventSource.Log.CrlCacheInMemoryPruned(countStart - countEnd, countEnd); + } + } + + private sealed class Node + { + private readonly int _keyHashCode; + + internal string Key { get; } + internal CachedCrlEntry Value { get; set; } + internal Node? Next { get; set; } + + internal Node(int hashCode, string key, CachedCrlEntry value) + { + Debug.Assert(key.GetHashCode() == hashCode); + + Key = key; + _keyHashCode = hashCode; + Value = value; + } + + internal bool MatchesKey(int hashCode, string key) + { + return _keyHashCode == hashCode && Key.Equals(key, StringComparison.Ordinal); + } + } + + private sealed class GCWatcher + { + private readonly MruCrlCache _owner; + + internal GCWatcher(MruCrlCache owner) + { + _owner = owner; + } + + ~GCWatcher() + { + GC.ReRegisterForFinalize(this); + + if (GC.GetGeneration(this) == GC.MaxGeneration) + { + try + { + _owner.PruneForGC(); + } + catch + { + // Eat any exception so we don't terminate the finalizer thread. +#if DEBUG + // Except in DEBUG, as we really shouldn't be hitting any exceptions here. + throw; +#endif + } + } + } + } + } + + private sealed class CachedCrlEntry + { + internal SafeX509CrlHandle CrlHandle { get; } + internal DateTime Expiration { get; } + + internal CachedCrlEntry(SafeX509CrlHandle crlHandle, DateTime expiration) + { + CrlHandle = crlHandle; + Expiration = expiration; + } + } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs index 0e1cdc25efb832..8b2fb68dd5c7d1 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainEventSource.cs @@ -60,6 +60,11 @@ internal sealed class OpenSslX509ChainEventSource : EventSource private const int EventId_RevocationCheckStop = 46; private const int EventId_CrlIdentifiersDetermined = 47; private const int EventId_StapledOcspPresent = 48; + private const int EventId_CrlCacheInMemoryHit = 49; + private const int EventId_CrlCacheInMemoryExpired = 50; + private const int EventId_CrlCacheInMemoryMiss = 51; + private const int EventId_CrlCacheInMemoryPruned = 52; + private const int EventId_CrlCacheInMemoryFull = 53; private static string GetCertificateSubject(SafeX509Handle certHandle) { @@ -429,7 +434,7 @@ internal void NoMatchingCdpEntry() EventId_CrlCacheCheckStart, Level = EventLevel.Verbose, Opcode = EventOpcode.Start, - Message = "Checking for a cached CRL.")] + Message = "Checking for a CRL cached on disk.")] internal void CrlCacheCheckStart() { if (IsEnabled()) @@ -477,7 +482,7 @@ internal void CrlCacheDecodeError() [Event( EventId_CrlCacheExpired, Level = EventLevel.Verbose, - Message = "The cached CRL's nextUpdate value ({1:O}) is not after the verification time ({0:O}).")] + Message = "The CRL cached on disk has a nextUpdate value ({1:O}) that is before the verification time ({0:O}).")] internal void CrlCacheExpired(DateTime verificationTime, DateTime nextUpdate) { if (IsEnabled()) @@ -489,7 +494,7 @@ internal void CrlCacheExpired(DateTime verificationTime, DateTime nextUpdate) [Event( EventId_CrlCacheFileBasedExpiry, Level = EventLevel.Verbose, - Message = "The cached crl has no nextUpdate value, basing nextUpdate on the file write time.")] + Message = "The CRL cached on disk has no nextUpdate value, basing nextUpdate on the file write time.")] internal void CrlCacheFileBasedExpiry() { if (IsEnabled()) @@ -501,7 +506,7 @@ internal void CrlCacheFileBasedExpiry() [Event( EventId_CrlCacheAcceptedFile, Level = EventLevel.Verbose, - Message = "The cached crl nextUpdate value ({0:O}) is acceptable, using the cached file.")] + Message = "The CRL cached on disk has a nextUpdate value ({0:O}) that is acceptable, using the cached file.")] internal void CrlCacheAcceptedFile(DateTime nextUpdate) { if (IsEnabled()) @@ -525,7 +530,7 @@ internal void CrlCacheWriteFailed(string cacheFile) [Event( EventId_CrlCacheWriteSucceeded, Level = EventLevel.Verbose, - Message = "The downloaded CRL was successfully written to the cache.")] + Message = "The downloaded CRL was successfully written to the disk cache.")] internal void CrlCacheWriteSucceeded() { if (IsEnabled()) @@ -752,5 +757,65 @@ internal void StapledOcspPresent() WriteEvent(EventId_StapledOcspPresent); } } + + [Event( + EventId_CrlCacheInMemoryHit, + Level = EventLevel.Verbose, + Message = "The in-memory CRL cache has a valid entry for the requested CRL, expiration at {0:O}.")] + internal void CrlCacheInMemoryHit(DateTime expiration) + { + if (IsEnabled()) + { + WriteEvent(EventId_CrlCacheInMemoryHit, expiration); + } + } + + [Event( + EventId_CrlCacheInMemoryExpired, + Level = EventLevel.Verbose, + Message = "The in-memory cached CRL's expiration time ({1:O}) is before the verification time ({0:O}).")] + internal void CrlCacheInMemoryExpired(DateTime verificationTime, DateTime expirationTime) + { + if (IsEnabled()) + { + WriteEvent(EventId_CrlCacheInMemoryExpired, verificationTime, expirationTime); + } + } + + [Event( + EventId_CrlCacheInMemoryPruned, + Level = EventLevel.Verbose, + Message = "The in-memory CRL cache was pruned. {0} entries removed, {1} entries remain.")] + internal void CrlCacheInMemoryPruned(int prunedCount, int remainingCount) + { + if (IsEnabled()) + { + WriteEvent(EventId_CrlCacheInMemoryPruned, prunedCount, remainingCount); + } + } + + [Event( + EventId_CrlCacheInMemoryMiss, + Level = EventLevel.Verbose, + Message = "The in-memory CRL cache has no entry for the requested CRL.")] + internal void CrlCacheInMemoryMiss() + { + if (IsEnabled()) + { + WriteEvent(EventId_CrlCacheInMemoryMiss); + } + } + + [Event( + EventId_CrlCacheInMemoryFull, + Level = EventLevel.Verbose, + Message = "The in-memory CRL cache is full, dismissing {0}.")] + internal void CrlCacheInMemoryFull(string cacheFileName) + { + if (IsEnabled()) + { + WriteEvent(EventId_CrlCacheInMemoryFull, cacheFileName); + } + } } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509FilesystemTests.Unix.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509FilesystemTests.Unix.cs index ae532f1d3bb8af..5895a58e432694 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509FilesystemTests.Unix.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509FilesystemTests.Unix.cs @@ -1,9 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.Tracing; using System.IO; -using System.Linq; +using System.Net.Http; +using System.Net.Security; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests @@ -80,6 +85,76 @@ public static void VerifyCrlCache() } } + [OuterLoop] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static async Task CrlDiskCacheRecovers() + { + using X509Certificate2 getDotNetCert = await GetGetDotNetCert(); + string crlFileName; + + using (CrlCacheNameFinderEventListener listener = new(getDotNetCert.Subject)) + using (CancellationTokenSource tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10))) + using (ChainHolder chainHolder = new ChainHolder()) + { + Task nameTask = listener.GetCacheFileNameAsync(tokenSource.Token); + + _ = chainHolder.Chain.Build(getDotNetCert); + crlFileName = await nameTask.ConfigureAwait(false); + } + + string crlDirectory = PersistedFiles.GetUserFeatureDirectory("cryptography", "crls"); + string crlFile = Path.Combine(crlDirectory, crlFileName); + string crlPem = await File.ReadAllTextAsync(crlFile).ConfigureAwait(false); + + await File.WriteAllTextAsync(crlFile, crlPem.AsMemory(0, crlPem.Length / 2)).ConfigureAwait(false); + + RemoteExecutor.Invoke( + static base64Cert => + { + using (X509Certificate2 cert = X509CertificateLoader.LoadCertificate(Convert.FromBase64String(base64Cert))) + using (ChainHolder chainHolder = new ChainHolder()) + { + bool valid = chainHolder.Chain.Build(cert); + + return valid ? RemoteExecutor.SuccessExitCode : 0; + } + }, + Convert.ToBase64String(getDotNetCert.RawDataMemory.Span)) + .Dispose(); + + string pem2 = await File.ReadAllTextAsync(crlFile).ConfigureAwait(false); + + // Rather than assert the CRL didn't change, just check that it's a valid CRL: + CertificateRevocationListBuilder.LoadPem(pem2, out _); + + static async Task GetGetDotNetCert() + { + X509Certificate2 getDotNetCert = null; + + SocketsHttpHandler handler = new SocketsHttpHandler + { + SslOptions = + { + RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => + { + getDotNetCert = X509CertificateLoader.LoadCertificate(((X509Certificate2)certificate).RawData); + return errors == SslPolicyErrors.None; + } + } + }; + + using (HttpClient client = new HttpClient(handler)) + using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))) + { + using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Head, "https://get.dot.net/"); + using HttpResponseMessage response = await client.SendAsync(req, cts.Token).ConfigureAwait(false); + } + + Assert.NotNull(getDotNetCert); + return getDotNetCert; + } + } + [Fact] public static void X509Store_OpenExisting_Fails() { @@ -672,6 +747,53 @@ private static void RunX509StoreTest(Action testAction) } } + private class CrlCacheNameFinderEventListener : EventListener + { + private readonly string _certificateName; + private string _cacheName; + + internal CrlCacheNameFinderEventListener(string certificateName) + { + _certificateName = certificateName; + } + + protected override void OnEventSourceCreated(EventSource eventSource) + { + if (eventSource.Name.Equals("System.Security.Cryptography.X509Certificates.X509Chain.OpenSsl")) + { + EnableEvents(eventSource, EventLevel.Verbose); + } + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + if (eventData.EventName == "CrlIdentifiersDetermined") + { + if (eventData.Payload?.Count == 3) + { + if (eventData.Payload[0] is string certName && + certName == _certificateName && + eventData.Payload[1] is string cdp && + eventData.Payload[2] is string cacheName) + { + _cacheName = cacheName; + } + } + } + } + + internal async Task GetCacheFileNameAsync(CancellationToken cancellationToken) + { + while (_cacheName == null) + { + await Task.Delay(100, cancellationToken).ConfigureAwait(false); + } + + Dispose(); + return _cacheName; + } + } + // `openssl crl -in [MicrosoftDotComRootCrlPem] -noout -hash`.[SHA-256(CDPURL)[0..4].ToHex()].crl private const string MicrosoftDotComRootCrlFilename = "b204d74a.daa2bce5.crl"; diff --git a/src/libraries/sendtohelix-browser.targets b/src/libraries/sendtohelix-browser.targets index 6d0b3c40e74608..8f4aa0bf526755 100644 --- a/src/libraries/sendtohelix-browser.targets +++ b/src/libraries/sendtohelix-browser.targets @@ -35,7 +35,6 @@ $(Scenario)-ST- $(Scenario)-MT- - true true @@ -177,9 +176,8 @@ + Text="Only supported scenarios are WasmTestOnV8, WasmTestOnChrome, WasmTestOnFirefox and BuildWasmApps at the moment. It was $(Scenario)." /> - <_BaseProjectsToBuild Include="$(PerScenarioProjectFile)" Condition="'%(_Scenarios.Identity)' != 'buildwasmapps' and '%(_Scenarios.Identity)' != 'buildiosapps' and '%(_Scenarios.Identity)' != 'wasmdebuggertests'"> + <_BaseProjectsToBuild Include="$(PerScenarioProjectFile)" Condition="'%(_Scenarios.Identity)' != 'buildwasmapps' and '%(_Scenarios.Identity)' != 'buildiosapps'"> $(_PropertiesToPass);Scenario=%(_Scenarios.Identity);TestArchiveRuntimeFile=$(TestArchiveRuntimeFile) %(_BaseProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) @@ -109,14 +109,6 @@ - - <_DebuggerHostsItem Include="$(_DebuggerHosts.Split('/'))" /> - - <_WasmDebuggerTestsProjectsToBuild Include="$(PerScenarioProjectFile)"> - $(_PropertiesToPass);Scenario=WasmDebuggerTests;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);DebuggerHost=%(_DebuggerHostsItem.Identity) - - - <_TestUsingWorkloadsValues Include="false" /> @@ -128,7 +120,7 @@ - <_ProjectsToBuild Include="@(_BuildWasmAppsProjectsToBuild);@(_WasmDebuggerTestsProjectsToBuild);@(_BuildiOSAppsProjectsToBuild);@(_BaseProjectsToBuild)" /> + <_ProjectsToBuild Include="@(_BuildWasmAppsProjectsToBuild);@(_BuildiOSAppsProjectsToBuild);@(_BaseProjectsToBuild)" /> diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index c52212e3a18e48..8b6f00768d38e5 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -25,7 +25,7 @@ false - true + true @@ -681,13 +681,6 @@ (('$(ContinuousIntegrationBuild)' == 'true' and '$(TestWasmBuildTests)' == 'true') or ('$(ContinuousIntegrationBuild)' != 'true' and '$(TestAssemblies)' == 'true'))" BuildInParallel="false" /> - - diff --git a/src/mono/browser/Makefile b/src/mono/browser/Makefile index 1401a1b36a431f..3abcd3541657cd 100644 --- a/src/mono/browser/Makefile +++ b/src/mono/browser/Makefile @@ -113,22 +113,6 @@ run-browser-tests-%: build-runtime-tests: $(TOP)/src/tests/build.sh -mono os browser wasm $(CONFIG) $(MSBUILD_ARGS) -build-debugger-tests-helix: - $(DOTNET) build -restore -bl:$(LOG_PATH)/Wasm.Debugger.Tests.binlog \ - /p:ContinuousIntegrationBuild=true /p:ArchiveTests=true \ - $(TOP)/src/mono/browser/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj \ - $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) - -submit-debugger-tests-helix: build-debugger-tests-helix - EMSDK_PATH=$(EMSDK_PATH) BUILD_REASON=wasm-test SYSTEM_TEAMPROJECT=public BUILD_REPOSITORY_NAME=dotnet/runtime BUILD_SOURCEBRANCH=main \ - $(TOP)/eng/common/msbuild.sh --ci -restore $(TOP)/src/libraries/sendtohelix.proj \ - /p:TestRunNamePrefixSuffix=WasmDebugger /p:HelixBuild=`date "+%Y%m%d.%H%M"` /p:Creator=`whoami` \ - /bl:$(LOG_PATH)/SendToHelix.binlog -p:HelixTargetQueue=$(HELIX_TARGET_QUEUE) \ - /p:RuntimeFlavor=mono /p:TargetRuntimeIdentifier= /p:MonoForceInterpreter= /p:TestScope=innerloop \ - /p:_Scenarios=wasmdebuggertests \ - $(_MSBUILD_WASM_BUILD_ARGS) \ - $(MSBUILD_ARGS) - submit-wbt-helix: PATH="$(JSVU):$(PATH)" \ $(DOTNET) build $(TOP)/src/mono/wasm/Wasm.Build.Tests/ /v:m /p:ArchiveTests=true /t:ArchiveTests $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) && \ diff --git a/src/mono/browser/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj b/src/mono/browser/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj index 008a633de76263..3183d12fb9d7bc 100644 --- a/src/mono/browser/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj +++ b/src/mono/browser/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj @@ -16,7 +16,6 @@ BundleDebuggerTestsForHelix true $(Configuration) - wasm.helix.targets diff --git a/src/mono/browser/debugger/Wasm.Debugger.Tests/wasm.helix.targets b/src/mono/browser/debugger/Wasm.Debugger.Tests/wasm.helix.targets deleted file mode 100644 index 4c36a8937fc1fd..00000000000000 --- a/src/mono/browser/debugger/Wasm.Debugger.Tests/wasm.helix.targets +++ /dev/null @@ -1,31 +0,0 @@ - - - true - true - $(DebuggerHost)- - true - <_DebuggerTestsWorkItemTimeout Condition="'$(Scenario)' == 'WasmDebuggerTests'">00:50:00 - <_DebuggerTestsWorkItemTimeout Condition="'$(Scenario)' == 'WasmDebuggerTests' and '$(BrowserHost)' == 'windows'">00:50:00 - - $(HelixExtensionTargets);_AddWorkItemsForWasmDebuggerTests - - - - - - - - - - - - - $(TestArchiveTestsDir)Wasm.Debugger.Tests.zip - $(HelixCommand) - $(_DebuggerTestsWorkItemTimeout) - set "TEST_ARGS=--filter category^^!=failing^&FullyQualifiedName~%(Identity)" - export "TEST_ARGS=--filter category!=failing&FullyQualifiedName~%(Identity)" - - - - diff --git a/src/mono/wasi/Makefile b/src/mono/wasi/Makefile index 13e2d9cb613ea1..327bac18216baa 100644 --- a/src/mono/wasi/Makefile +++ b/src/mono/wasi/Makefile @@ -73,16 +73,6 @@ build-debugger-tests-helix: $(TOP)/src/mono/wasm/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj \ $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) -submit-debugger-tests-helix: build-debugger-tests-helix - BUILD_REASON=wasm-test SYSTEM_TEAMPROJECT=public BUILD_REPOSITORY_NAME=dotnet/runtime BUILD_SOURCEBRANCH=main \ - $(TOP)/eng/common/msbuild.sh --ci -restore $(TOP)/src/libraries/sendtohelix.proj \ - /p:TestRunNamePrefixSuffix=WasmDebugger /p:HelixBuild=`date "+%Y%m%d.%H%M"` /p:Creator=`whoami` \ - /bl:$(TOP)/artifacts/log/$(CONFIG)/SendToHelix.binlog -p:HelixTargetQueue=$(HELIX_TARGET_QUEUE) \ - /p:RuntimeFlavor=mono /p:TargetRuntimeIdentifier= /p:MonoForceInterpreter= /p:TestScope=innerloop \ - /p:_Scenarios=wasmdebuggertests \ - $(_MSBUILD_WASM_BUILD_ARGS) \ - $(MSBUILD_ARGS) - submit-wbt-helix: PATH="$(JSVU):$(PATH)" \ $(DOTNET) build $(TOP)/src/mono/wasi/Wasi.Build.Tests/ /v:m /p:ArchiveTests=true /t:ArchiveTests $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) && \ diff --git a/src/tests/JIT/opt/Cloning/DownCounted.cs b/src/tests/JIT/opt/Cloning/DownCounted.cs new file mode 100644 index 00000000000000..50d14e99d39b9f --- /dev/null +++ b/src/tests/JIT/opt/Cloning/DownCounted.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class DownCounted +{ + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static bool ArrayProblem(int[] a, int n) + { + bool result = false; + for (int i = n; i > 0; i--) + { + a[i] = 44; + result |= (i == n); + } + return result; + } + + [Fact] + public static int ArrayTest() + { + int[] a = new int[100]; + int result = -1; + try + { + bool hasProblem = ArrayProblem(a, 100); + Console.WriteLine($"failed, has problem={hasProblem}"); + } + catch (IndexOutOfRangeException e) + { + Console.WriteLine("passed"); + result = 100; + } + return result; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static bool SpanProblem(Span a, int n) + { + bool result = false; + for (int i = n; i > 0; i--) + { + a[i] = 44; + result |= (i == n); + } + return result; + } + + [Fact] + public static int SpanTest() + { + int[] a = new int[100]; + int result = -1; + try + { + bool hasProblem = SpanProblem(a, 100); + Console.WriteLine($"failed, has problem={hasProblem}"); + } + catch (IndexOutOfRangeException e) + { + Console.WriteLine("passed"); + result = 100; + } + return result; + } + + +} + diff --git a/src/tests/JIT/opt/Cloning/DownCounted.csproj b/src/tests/JIT/opt/Cloning/DownCounted.csproj new file mode 100644 index 00000000000000..de6d5e08882e86 --- /dev/null +++ b/src/tests/JIT/opt/Cloning/DownCounted.csproj @@ -0,0 +1,8 @@ + + + True + + + + +