Skip to content

Commit aa103bb

Browse files
committed
Avoid all compiler optimization on embedded apphost hash
We assume that there is a single copy of the apphost hash in the apphost binary. And that it hasn't been modified by the compiler. However, the compiler can optimize the hash multiple ways, including re-ordering elements of the hash or duplicating the contents of the hash. This can currently happen under certain compiler versions and optimization flags. Try and avoid that by marking the hash as a volatile string and implementing comparisons/copying/initialization that respects that. Fixes: dotnet#109611
1 parent 016d356 commit aa103bb

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

src/native/corehost/corehost.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,26 @@
4040
#define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
4141
#define EMBED_HASH_FULL_UTF8 (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
4242

43+
std::string string_from_volatile(volatile const char* cstr)
44+
{
45+
std::string result;
46+
for (volatile const char* ptr = cstr; *ptr != 0; ++ptr)
47+
{
48+
result.push_back(*ptr);
49+
}
50+
return result;
51+
}
52+
53+
bool compare_memory_nooptimization(volatile const char* a, volatile const char* b, size_t length)
54+
{
55+
for (size_t i = 0; i < length; i++)
56+
{
57+
if (*a++ != *b++)
58+
return false;
59+
}
60+
return true;
61+
}
62+
4363
bool is_exe_enabled_for_execution(pal::string_t* app_dll)
4464
{
4565
constexpr int EMBED_SZ = sizeof(EMBED_HASH_FULL_UTF8) / sizeof(EMBED_HASH_FULL_UTF8[0]);
@@ -48,18 +68,18 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
4868
// Contains the EMBED_HASH_FULL_UTF8 value at compile time or the managed DLL name replaced by "dotnet build".
4969
// Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length
5070
// where length is determined at compile time (=64) instead of the actual length of the string at runtime.
51-
static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
71+
volatile static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8; // series of NULs followed by embed hash string
5272

5373
static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
5474
static const char lo_part[] = EMBED_HASH_LO_PART_UTF8;
5575

56-
if (!pal::clr_palstring(embed, app_dll))
76+
if (!pal::clr_palstring(&embed[0], app_dll))
5777
{
5878
trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image."));
5979
return false;
6080
}
6181

62-
std::string binding(&embed[0]);
82+
std::string binding = string_from_volatile(&embed[0]);
6383

6484
// Check if the path exceeds the max allowed size
6585
if (binding.size() > EMBED_MAX - 1) // -1 for null terminator
@@ -74,8 +94,8 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
7494
size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1;
7595
size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1;
7696
if (binding.size() >= (hi_len + lo_len)
77-
&& binding.compare(0, hi_len, &hi_part[0]) == 0
78-
&& binding.compare(hi_len, lo_len, &lo_part[0]) == 0)
97+
&& compare_memory_nooptimization(binding.c_str(), hi_part, hi_len)
98+
&& compare_memory_nooptimization(binding.substr(hi_len).c_str(), lo_part, lo_len))
7999
{
80100
trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), app_dll->c_str());
81101
return false;

src/native/corehost/hostmisc/pal.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ namespace pal
182182
bool pal_utf8string(const string_t& str, std::vector<char>* out);
183183
bool pal_clrstring(const string_t& str, std::vector<char>* out);
184184
bool clr_palstring(const char* cstr, string_t* out);
185+
bool clr_palstring(volatile const char* cstr, string_t* out);
185186

186187
inline bool mkdir(const char_t* dir, int mode) { return CreateDirectoryW(dir, NULL) != 0; }
187188
inline bool rmdir(const char_t* path) { return RemoveDirectoryW(path) != 0; }
@@ -248,6 +249,15 @@ namespace pal
248249
inline bool pal_utf8string(const string_t& str, std::vector<char>* out) { out->assign(str.begin(), str.end()); out->push_back('\0'); return true; }
249250
inline bool pal_clrstring(const string_t& str, std::vector<char>* out) { return pal_utf8string(str, out); }
250251
inline bool clr_palstring(const char* cstr, string_t* out) { out->assign(cstr); return true; }
252+
inline bool clr_palstring(volatile const char* cstr, string_t* out)
253+
{
254+
out->assign("");
255+
for (volatile const char* ptr = cstr; *ptr != 0; ++ptr)
256+
{
257+
out->push_back(*ptr);
258+
}
259+
return true;
260+
}
251261

252262
inline bool mkdir(const char_t* dir, int mode) { return ::mkdir(dir, mode) == 0; }
253263
inline bool rmdir(const char_t* path) { return ::rmdir(path) == 0; }

src/native/corehost/hostmisc/pal.windows.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,17 @@ bool pal::clr_palstring(const char* cstr, pal::string_t* out)
829829
return wchar_convert_helper(CP_UTF8, cstr, ::strlen(cstr), out);
830830
}
831831

832+
bool pal::clr_palstring(volatile const char* cstr, string_t* out)
833+
{
834+
string_t temp;
835+
for (volatile const char* ptr = cstr; *ptr != 0; ++ptr)
836+
{
837+
temp.push_back(*ptr);
838+
}
839+
840+
return wchar_convert_helper(CP_UTF8, temp.c_str(), temp.length(), out);
841+
}
842+
832843
typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)> SmartHandle;
833844

834845
// Like fullpath, but resolves file symlinks (note: not necessarily directory symlinks).

0 commit comments

Comments
 (0)