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
+
+
+
+
+