at master 11 kB view raw
1From 4e333377f97ab8f0f47ba7606844c34cb61d1db0 Mon Sep 17 00:00:00 2001 2From: Omair Majid <omajid@redhat.com> 3Date: Mon, 9 Dec 2024 17:44:10 -0500 4Subject: [PATCH 1/4] Avoid all compiler optimization on embedded apphost hash 5 6We assume that there is a single copy of the apphost hash in the apphost 7binary. And that it hasn't been modified by the compiler. However, the 8compiler can optimize the hash multiple ways, including re-ordering 9elements of the hash or duplicating the contents of the hash. This can 10currently happen under certain compiler versions and optimization flags. 11 12Try and avoid that by marking the hash as a volatile string and 13implementing comparisons/copying/initialization that respects that. 14 15Fixes: #109611 16--- 17 src/runtime/src/native/corehost/corehost.cpp | 31 ++++++++++++++++++++++++++----- 18 1 file changed, 26 insertions(+), 5 deletions(-) 19 20diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp 21index 6de7acfbd08576..6d40a337d574a2 100644 22--- a/src/runtime/src/native/corehost/corehost.cpp 23+++ b/src/runtime/src/native/corehost/corehost.cpp 24@@ -40,6 +40,24 @@ 25 #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2" 26 #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated 27 28+void to_non_volatile(volatile const char* cstr, char* output, size_t length) 29+{ 30+ for (size_t i = 0; i < length; i++) 31+ { 32+ output[i] = cstr[i]; 33+ } 34+} 35+ 36+bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length) 37+{ 38+ for (size_t i = 0; i < length; i++) 39+ { 40+ if (*a++ != *b++) 41+ return false; 42+ } 43+ return true; 44+} 45+ 46 bool is_exe_enabled_for_execution(pal::string_t* app_dll) 47 { 48 constexpr int EMBED_SZ = sizeof(EMBED_HASH_FULL_UTF8) / sizeof(EMBED_HASH_FULL_UTF8[0]); 49@@ -48,18 +66,21 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll) 50 // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build". 51 // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length 52 // where length is determined at compile time (=64) instead of the actual length of the string at runtime. 53- static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string 54+ volatile static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string 55 56 static const char hi_part[] = EMBED_HASH_HI_PART_UTF8; 57 static const char lo_part[] = EMBED_HASH_LO_PART_UTF8; 58 59- if (!pal::clr_palstring(embed, app_dll)) 60+ char working_copy_embed[EMBED_MAX]; 61+ to_non_volatile(embed, working_copy_embed, EMBED_MAX); 62+ 63+ if (!pal::clr_palstring(&working_copy_embed[0], app_dll)) 64 { 65 trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image.")); 66 return false; 67 } 68 69- std::string binding(&embed[0]); 70+ std::string binding(&working_copy_embed[0]); 71 72 // Check if the path exceeds the max allowed size 73 if (binding.size() > EMBED_MAX - 1) // -1 for null terminator 74@@ -74,8 +95,8 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll) 75 size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1; 76 size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1; 77 if (binding.size() >= (hi_len + lo_len) 78- && binding.compare(0, hi_len, &hi_part[0]) == 0 79- && binding.compare(hi_len, lo_len, &lo_part[0]) == 0) 80+ && compare_memory_nooptimization(binding.c_str(), hi_part, hi_len) 81+ && compare_memory_nooptimization(binding.substr(hi_len).c_str(), lo_part, lo_len)) 82 { 83 trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), app_dll->c_str()); 84 return false; 85 86From 2c67debff3f84519b7b5cba49232aaa2396a9f3e Mon Sep 17 00:00:00 2001 87From: Aaron R Robinson <arobins@microsoft.com> 88Date: Wed, 26 Mar 2025 20:40:51 -0700 89Subject: [PATCH 2/4] Apply feedback 90 91--- 92 src/runtime/src/native/corehost/corehost.cpp | 20 ++++++-------------- 93 1 file changed, 6 insertions(+), 14 deletions(-) 94 95diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp 96index 6d40a337d574a2..9d2648c0ba84fa 100644 97--- a/src/runtime/src/native/corehost/corehost.cpp 98+++ b/src/runtime/src/native/corehost/corehost.cpp 99@@ -40,14 +40,9 @@ 100 #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2" 101 #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated 102 103-void to_non_volatile(volatile const char* cstr, char* output, size_t length) 104-{ 105- for (size_t i = 0; i < length; i++) 106- { 107- output[i] = cstr[i]; 108- } 109-} 110- 111+// This is a workaround for a compiler workaround that 112+// causes issues with inserting multiple static strings. 113+// See https://github.com/dotnet/runtime/issues/109611 for more details. 114 bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length) 115 { 116 for (size_t i = 0; i < length; i++) 117@@ -66,21 +61,18 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll) 118 // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build". 119 // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length 120 // where length is determined at compile time (=64) instead of the actual length of the string at runtime. 121- volatile static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string 122+ static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string 123 124 static const char hi_part[] = EMBED_HASH_HI_PART_UTF8; 125 static const char lo_part[] = EMBED_HASH_LO_PART_UTF8; 126 127- char working_copy_embed[EMBED_MAX]; 128- to_non_volatile(embed, working_copy_embed, EMBED_MAX); 129- 130- if (!pal::clr_palstring(&working_copy_embed[0], app_dll)) 131+ if (!pal::clr_palstring(embed, app_dll)) 132 { 133 trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image.")); 134 return false; 135 } 136 137- std::string binding(&working_copy_embed[0]); 138+ std::string binding(&embed[0]); 139 140 // Check if the path exceeds the max allowed size 141 if (binding.size() > EMBED_MAX - 1) // -1 for null terminator 142 143From 854143d39e7725d82547032f1ab47ea5da062b9f Mon Sep 17 00:00:00 2001 144From: Aaron R Robinson <arobins@microsoft.com> 145Date: Thu, 27 Mar 2025 19:04:09 -0700 146Subject: [PATCH 3/4] Feedback 147 148--- 149 src/runtime/src/native/corehost/corehost.cpp | 16 ++++++++-------- 150 1 file changed, 8 insertions(+), 8 deletions(-) 151 152diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp 153index 9d2648c0ba84fa..36902ccfa56c04 100644 154--- a/src/runtime/src/native/corehost/corehost.cpp 155+++ b/src/runtime/src/native/corehost/corehost.cpp 156@@ -40,10 +40,10 @@ 157 #define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2" 158 #define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated 159 160-// This is a workaround for a compiler workaround that 161-// causes issues with inserting multiple static strings. 162+// This avoids compiler optimization which cause EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8 163+// to be placed adjacent causing them to match EMBED_HASH_FULL_UTF8 when searched for replacing. 164 // See https://github.com/dotnet/runtime/issues/109611 for more details. 165-bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length) 166+static bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length) 167 { 168 for (size_t i = 0; i < length; i++) 169 { 170@@ -72,10 +72,10 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll) 171 return false; 172 } 173 174- std::string binding(&embed[0]); 175+ size_t binding_len = strlen(&embed[0]); 176 177 // Check if the path exceeds the max allowed size 178- if (binding.size() > EMBED_MAX - 1) // -1 for null terminator 179+ if (binding_len > EMBED_MAX - 1) // -1 for null terminator 180 { 181 trace::error(_X("The managed DLL bound to this executable is longer than the max allowed length (%d)"), EMBED_MAX - 1); 182 return false; 183@@ -86,9 +86,9 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll) 184 // So use two parts of the string that will be unaffected by the edit. 185 size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1; 186 size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1; 187- if (binding.size() >= (hi_len + lo_len) 188- && compare_memory_nooptimization(binding.c_str(), hi_part, hi_len) 189- && compare_memory_nooptimization(binding.substr(hi_len).c_str(), lo_part, lo_len)) 190+ if (binding_len >= (hi_len + lo_len) 191+ && compare_memory_nooptimization(&embed[0], hi_part, hi_len) 192+ && compare_memory_nooptimization(&embed[hi_len], lo_part, lo_len)) 193 { 194 trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), app_dll->c_str()); 195 return false; 196 197From 842d62e499ce6511abf948cf5da8023cc6be8212 Mon Sep 17 00:00:00 2001 198From: Aaron R Robinson <arobins@microsoft.com> 199Date: Fri, 28 Mar 2025 15:44:47 -0700 200Subject: [PATCH 4/4] Feedback 201 202--- 203 src/runtime/src/native/corehost/corehost.cpp | 4 ++-- 204 1 file changed, 2 insertions(+), 2 deletions(-) 205 206diff --git a/src/runtime/src/native/corehost/corehost.cpp b/src/runtime/src/native/corehost/corehost.cpp 207index 36902ccfa56c04..54eb128cb486bb 100644 208--- a/src/runtime/src/native/corehost/corehost.cpp 209+++ b/src/runtime/src/native/corehost/corehost.cpp 210@@ -59,8 +59,8 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll) 211 constexpr int EMBED_MAX = (EMBED_SZ > 1025 ? EMBED_SZ : 1025); // 1024 DLL name length, 1 NUL 212 213 // Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build". 214- // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length 215- // where length is determined at compile time (=64) instead of the actual length of the string at runtime. 216+ // Must not be 'const' because strlen below could be determined at compile time (=64) instead of the actual 217+ // length of the string at runtime. 218 static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string 219 220 static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;