···11+diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp
22+index 5edc2fbf5d5..1b3f5b1a23a 100644
33+--- a/src/runtime/src/native/corehost/corehost.cpp
44++++ b/src/runtime/src/native/corehost/corehost.cpp
55+@@ -40,14 +40,27 @@
66+ #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
77+ #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
88+99++// This avoids compiler optimization which cause EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8
1010++// to be placed adjacent causing them to match EMBED_HASH_FULL_UTF8 when searched for replacing.
1111++// See https://github.com/dotnet/runtime/issues/109611 for more details.
1212++static bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length)
1313++{
1414++ for (size_t i = 0; i < length; i++)
1515++ {
1616++ if (*a++ != *b++)
1717++ return false;
1818++ }
1919++ return true;
2020++}
2121++
2222+ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
2323+ {
2424+ constexpr int EMBED_SZ = sizeof(EMBED_HASH_FULL_UTF8) / sizeof(EMBED_HASH_FULL_UTF8[0]);
2525+ constexpr int EMBED_MAX = (EMBED_SZ > 1025 ? EMBED_SZ : 1025); // 1024 DLL name length, 1 NUL
2626+2727+ // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build".
2828+- // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length
2929+- // where length is determined at compile time (=64) instead of the actual length of the string at runtime.
3030++ // Must not be 'const' because strlen below could be determined at compile time (=64) instead of the actual
3131++ // length of the string at runtime.
3232+ static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
3333+3434+ static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
3535+@@ -64,10 +77,10 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
3636+ size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1;
3737+ size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1;
3838+3939+- std::string binding(&embed[0]);
4040+- if ((binding.size() >= (hi_len + lo_len)) &&
4141+- binding.compare(0, hi_len, &hi_part[0]) == 0 &&
4242+- binding.compare(hi_len, lo_len, &lo_part[0]) == 0)
4343++ size_t binding_len = strlen(&embed[0]);
4444++ if ((binding_len >= (hi_len + lo_len))
4545++ && compare_memory_nooptimization(&embed[0], hi_part, hi_len) == 0
4646++ && compare_memory_nooptimization(&embed[hi_len], lo_part, lo_len))
4747+ {
4848+ trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), app_dll->c_str());
4949+ return false;
···11+From 4e333377f97ab8f0f47ba7606844c34cb61d1db0 Mon Sep 17 00:00:00 2001
22+From: Omair Majid <omajid@redhat.com>
33+Date: Mon, 9 Dec 2024 17:44:10 -0500
44+Subject: [PATCH 1/4] Avoid all compiler optimization on embedded apphost hash
55+66+We assume that there is a single copy of the apphost hash in the apphost
77+binary. And that it hasn't been modified by the compiler. However, the
88+compiler can optimize the hash multiple ways, including re-ordering
99+elements of the hash or duplicating the contents of the hash. This can
1010+currently happen under certain compiler versions and optimization flags.
1111+1212+Try and avoid that by marking the hash as a volatile string and
1313+implementing comparisons/copying/initialization that respects that.
1414+1515+Fixes: #109611
1616+---
1717+ src/runtime/src/native/corehost/corehost.cpp | 31 ++++++++++++++++++++++++++-----
1818+ 1 file changed, 26 insertions(+), 5 deletions(-)
1919+2020+diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp
2121+index 6de7acfbd08576..6d40a337d574a2 100644
2222+--- a/src/runtime/src/native/corehost/corehost.cpp
2323++++ b/src/runtime/src/native/corehost/corehost.cpp
2424+@@ -40,6 +40,24 @@
2525+ #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
2626+ #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
2727+2828++void to_non_volatile(volatile const char* cstr, char* output, size_t length)
2929++{
3030++ for (size_t i = 0; i < length; i++)
3131++ {
3232++ output[i] = cstr[i];
3333++ }
3434++}
3535++
3636++bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length)
3737++{
3838++ for (size_t i = 0; i < length; i++)
3939++ {
4040++ if (*a++ != *b++)
4141++ return false;
4242++ }
4343++ return true;
4444++}
4545++
4646+ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
4747+ {
4848+ constexpr int EMBED_SZ = sizeof(EMBED_HASH_FULL_UTF8) / sizeof(EMBED_HASH_FULL_UTF8[0]);
4949+@@ -48,18 +66,21 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
5050+ // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build".
5151+ // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length
5252+ // where length is determined at compile time (=64) instead of the actual length of the string at runtime.
5353+- static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
5454++ volatile static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
5555+5656+ static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
5757+ static const char lo_part[] = EMBED_HASH_LO_PART_UTF8;
5858+5959+- if (!pal::clr_palstring(embed, app_dll))
6060++ char working_copy_embed[EMBED_MAX];
6161++ to_non_volatile(embed, working_copy_embed, EMBED_MAX);
6262++
6363++ if (!pal::clr_palstring(&working_copy_embed[0], app_dll))
6464+ {
6565+ trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image."));
6666+ return false;
6767+ }
6868+6969+- std::string binding(&embed[0]);
7070++ std::string binding(&working_copy_embed[0]);
7171+7272+ // Check if the path exceeds the max allowed size
7373+ if (binding.size() > EMBED_MAX - 1) // -1 for null terminator
7474+@@ -74,8 +95,8 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
7575+ size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1;
7676+ size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1;
7777+ if (binding.size() >= (hi_len + lo_len)
7878+- && binding.compare(0, hi_len, &hi_part[0]) == 0
7979+- && binding.compare(hi_len, lo_len, &lo_part[0]) == 0)
8080++ && compare_memory_nooptimization(binding.c_str(), hi_part, hi_len)
8181++ && compare_memory_nooptimization(binding.substr(hi_len).c_str(), lo_part, lo_len))
8282+ {
8383+ trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), app_dll->c_str());
8484+ return false;
8585+8686+From 2c67debff3f84519b7b5cba49232aaa2396a9f3e Mon Sep 17 00:00:00 2001
8787+From: Aaron R Robinson <arobins@microsoft.com>
8888+Date: Wed, 26 Mar 2025 20:40:51 -0700
8989+Subject: [PATCH 2/4] Apply feedback
9090+9191+---
9292+ src/runtime/src/native/corehost/corehost.cpp | 20 ++++++--------------
9393+ 1 file changed, 6 insertions(+), 14 deletions(-)
9494+9595+diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp
9696+index 6d40a337d574a2..9d2648c0ba84fa 100644
9797+--- a/src/runtime/src/native/corehost/corehost.cpp
9898++++ b/src/runtime/src/native/corehost/corehost.cpp
9999+@@ -40,14 +40,9 @@
100100+ #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
101101+ #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
102102+103103+-void to_non_volatile(volatile const char* cstr, char* output, size_t length)
104104+-{
105105+- for (size_t i = 0; i < length; i++)
106106+- {
107107+- output[i] = cstr[i];
108108+- }
109109+-}
110110+-
111111++// This is a workaround for a compiler workaround that
112112++// causes issues with inserting multiple static strings.
113113++// See https://github.com/dotnet/runtime/issues/109611 for more details.
114114+ bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length)
115115+ {
116116+ for (size_t i = 0; i < length; i++)
117117+@@ -66,21 +61,18 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
118118+ // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build".
119119+ // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length
120120+ // where length is determined at compile time (=64) instead of the actual length of the string at runtime.
121121+- volatile static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
122122++ static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
123123+124124+ static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
125125+ static const char lo_part[] = EMBED_HASH_LO_PART_UTF8;
126126+127127+- char working_copy_embed[EMBED_MAX];
128128+- to_non_volatile(embed, working_copy_embed, EMBED_MAX);
129129+-
130130+- if (!pal::clr_palstring(&working_copy_embed[0], app_dll))
131131++ if (!pal::clr_palstring(embed, app_dll))
132132+ {
133133+ trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image."));
134134+ return false;
135135+ }
136136+137137+- std::string binding(&working_copy_embed[0]);
138138++ std::string binding(&embed[0]);
139139+140140+ // Check if the path exceeds the max allowed size
141141+ if (binding.size() > EMBED_MAX - 1) // -1 for null terminator
142142+143143+From 854143d39e7725d82547032f1ab47ea5da062b9f Mon Sep 17 00:00:00 2001
144144+From: Aaron R Robinson <arobins@microsoft.com>
145145+Date: Thu, 27 Mar 2025 19:04:09 -0700
146146+Subject: [PATCH 3/4] Feedback
147147+148148+---
149149+ src/runtime/src/native/corehost/corehost.cpp | 16 ++++++++--------
150150+ 1 file changed, 8 insertions(+), 8 deletions(-)
151151+152152+diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp
153153+index 9d2648c0ba84fa..36902ccfa56c04 100644
154154+--- a/src/runtime/src/native/corehost/corehost.cpp
155155++++ b/src/runtime/src/native/corehost/corehost.cpp
156156+@@ -40,10 +40,10 @@
157157+ #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
158158+ #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
159159+160160+-// This is a workaround for a compiler workaround that
161161+-// causes issues with inserting multiple static strings.
162162++// This avoids compiler optimization which cause EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8
163163++// to be placed adjacent causing them to match EMBED_HASH_FULL_UTF8 when searched for replacing.
164164+ // See https://github.com/dotnet/runtime/issues/109611 for more details.
165165+-bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length)
166166++static bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length)
167167+ {
168168+ for (size_t i = 0; i < length; i++)
169169+ {
170170+@@ -72,10 +72,10 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
171171+ return false;
172172+ }
173173+174174+- std::string binding(&embed[0]);
175175++ size_t binding_len = strlen(&embed[0]);
176176+177177+ // Check if the path exceeds the max allowed size
178178+- if (binding.size() > EMBED_MAX - 1) // -1 for null terminator
179179++ if (binding_len > EMBED_MAX - 1) // -1 for null terminator
180180+ {
181181+ trace::error(_X("The managed DLL bound to this executable is longer than the max allowed length (%d)"), EMBED_MAX - 1);
182182+ return false;
183183+@@ -86,9 +86,9 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
184184+ // So use two parts of the string that will be unaffected by the edit.
185185+ size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1;
186186+ size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1;
187187+- if (binding.size() >= (hi_len + lo_len)
188188+- && compare_memory_nooptimization(binding.c_str(), hi_part, hi_len)
189189+- && compare_memory_nooptimization(binding.substr(hi_len).c_str(), lo_part, lo_len))
190190++ if (binding_len >= (hi_len + lo_len)
191191++ && compare_memory_nooptimization(&embed[0], hi_part, hi_len)
192192++ && compare_memory_nooptimization(&embed[hi_len], lo_part, lo_len))
193193+ {
194194+ trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), app_dll->c_str());
195195+ return false;
196196+197197+From 842d62e499ce6511abf948cf5da8023cc6be8212 Mon Sep 17 00:00:00 2001
198198+From: Aaron R Robinson <arobins@microsoft.com>
199199+Date: Fri, 28 Mar 2025 15:44:47 -0700
200200+Subject: [PATCH 4/4] Feedback
201201+202202+---
203203+ src/runtime/src/native/corehost/corehost.cpp | 4 ++--
204204+ 1 file changed, 2 insertions(+), 2 deletions(-)
205205+206206+diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp
207207+index 36902ccfa56c04..54eb128cb486bb 100644
208208+--- a/src/runtime/src/native/corehost/corehost.cpp
209209++++ b/src/runtime/src/native/corehost/corehost.cpp
210210+@@ -59,8 +59,8 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
211211+ constexpr int EMBED_MAX = (EMBED_SZ > 1025 ? EMBED_SZ : 1025); // 1024 DLL name length, 1 NUL
212212+213213+ // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build".
214214+- // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length
215215+- // where length is determined at compile time (=64) instead of the actual length of the string at runtime.
216216++ // Must not be 'const' because strlen below could be determined at compile time (=64) instead of the actual
217217++ // length of the string at runtime.
218218+ static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
219219+220220+ static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
+2
pkgs/development/compilers/dotnet/vmr.nix
···136136 patches =
137137 lib.optionals (lib.versionAtLeast version "9" && lib.versionOlder version "10") [
138138 ./UpdateNuGetConfigPackageSourcesMappings-don-t-add-em.patch
139139+ ./vmr-compiler-opt-v9.patch
139140 ]
140141 ++ lib.optionals (lib.versionOlder version "9") [
141142 ./fix-aspnetcore-portable-build.patch
143143+ ./vmr-compiler-opt-v8.patch
142144 ]
143145 ++ lib.optionals (lib.versionAtLeast version "10") [
144146 # src/repos/projects/Directory.Build.targets(106,5): error MSB4018: The "AddSourceToNuGetConfig" task failed unexpectedly.