Skip to content

Commit 7315722

Browse files
authored
Fix absolute path check when loading hostfxr/hostpolicy/coreclr (#116776)
On Windows, we were incorrectly only checking for <letter>: and incorrectly determining that UNC and device paths were not absolute. This updates pal::is_path_rooted to match the managed Path.IsPathRooted and adds a pal::is_path_absolute with the logic of Path.IsPartiallyQualified This also adds an error message when we can't resolve hostfxr and tests for device paths.
1 parent 7af93f5 commit 7315722

File tree

11 files changed

+87
-8
lines changed

11 files changed

+87
-8
lines changed

src/installer/tests/HostActivation.Tests/PortableAppActivation.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,33 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere
216216
}
217217
}
218218

219+
[Fact]
220+
[PlatformSpecific(TestPlatforms.Windows)]
221+
public void AppHost_DotNetRoot_DevicePath()
222+
{
223+
string appExe = sharedTestState.PortableAppFixture_Published.TestProject.AppExe;
224+
225+
string dotnetPath = $@"\\?\{sharedTestState.PortableAppFixture_Published.BuiltDotnet.BinPath}";
226+
Command.Create(appExe)
227+
.CaptureStdErr()
228+
.CaptureStdOut()
229+
.DotNetRoot(dotnetPath, sharedTestState.RepoDirectories.BuildArchitecture)
230+
.Execute()
231+
.Should().Pass()
232+
.And.HaveStdOutContaining("Hello World")
233+
.And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion);
234+
235+
dotnetPath = $@"\\.\{sharedTestState.PortableAppFixture_Published.BuiltDotnet.BinPath}";
236+
Command.Create(appExe)
237+
.CaptureStdErr()
238+
.CaptureStdOut()
239+
.DotNetRoot(dotnetPath, sharedTestState.RepoDirectories.BuildArchitecture)
240+
.Execute()
241+
.Should().Pass()
242+
.And.HaveStdOutContaining("Hello World")
243+
.And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion);
244+
}
245+
219246
[Fact]
220247
public void RuntimeConfig_FilePath_Breaks_MAX_PATH_Threshold()
221248
{

src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,29 @@ public void Running_Publish_Output_Standalone_EXE_with_Bound_AppHost_Succeeds()
254254
}
255255
}
256256

257+
[Fact]
258+
[PlatformSpecific(TestPlatforms.Windows)]
259+
public void DevicePath()
260+
{
261+
string appExe = $@"\\?\{sharedTestState.StandaloneAppFixture_Published.TestProject.AppExe}";
262+
Command.Create(appExe)
263+
.CaptureStdErr()
264+
.CaptureStdOut()
265+
.Execute()
266+
.Should().Pass()
267+
.And.HaveStdOutContaining("Hello World")
268+
.And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion);
269+
270+
appExe = $@"\\.\{sharedTestState.StandaloneAppFixture_Published.TestProject.AppExe}";
271+
Command.Create(appExe)
272+
.CaptureStdErr()
273+
.CaptureStdOut()
274+
.Execute()
275+
.Should().Pass()
276+
.And.HaveStdOutContaining("Hello World")
277+
.And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion);
278+
}
279+
257280
[Fact]
258281
[PlatformSpecific(TestPlatforms.Windows)] // GUI app host is only supported on Windows.
259282
public void Running_AppHost_with_GUI_No_Console()

src/native/corehost/apphost/standalone/hostfxr_resolver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ hostfxr_resolver_t::hostfxr_resolver_t(const pal::string_t& app_root)
3838
{
3939
m_status_code = StatusCode::CoreHostLibMissingFailure;
4040
}
41-
else if (!pal::is_path_rooted(m_fxr_path))
41+
else if (!pal::is_path_fully_qualified(m_fxr_path))
4242
{
4343
// We should always be loading hostfxr from an absolute path
4444
m_status_code = StatusCode::CoreHostLibMissingFailure;

src/native/corehost/corehost.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ int exe_start(const int argc, const pal::char_t* argv[])
187187
int rc = fxr.status_code();
188188
if (rc != StatusCode::Success)
189189
{
190+
trace::error(_X("Failed to resolve %s [%s]. Error code: 0x%x"), LIBFXR_NAME, fxr.fxr_path().empty() ? _X("not found") : fxr.fxr_path().c_str(), rc);
190191
return rc;
191192
}
192193

src/native/corehost/fxr/standalone/hostpolicy_resolver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ int hostpolicy_resolver::load(
181181
}
182182

183183
// We should always be loading hostpolicy from an absolute path
184-
if (!pal::is_path_rooted(host_path))
184+
if (!pal::is_path_fully_qualified(host_path))
185185
return StatusCode::CoreHostLibMissingFailure;
186186

187187
// Load library

src/native/corehost/fxr_resolver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ int load_fxr_and_get_delegate(hostfxr_delegate_type type, THostPathToConfigCallb
4545
}
4646

4747
// We should always be loading hostfxr from an absolute path
48-
if (!pal::is_path_rooted(fxr_path))
48+
if (!pal::is_path_fully_qualified(fxr_path))
4949
return StatusCode::CoreHostLibMissingFailure;
5050

5151
// Load library

src/native/corehost/hostmisc/pal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ namespace pal
328328

329329
bool get_default_breadcrumb_store(string_t* recv);
330330
bool is_path_rooted(const string_t& path);
331+
bool is_path_fully_qualified(const string_t& path);
331332

332333
// Returns a platform-specific, user-private directory
333334
// that can be used for extracting out components of a single-file app.

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,15 @@ bool pal::get_loaded_library(
192192
{
193193
pal::string_t library_name_local;
194194
#if defined(TARGET_OSX)
195-
if (!pal::is_path_rooted(library_name))
195+
if (!pal::is_path_fully_qualified(library_name))
196196
library_name_local.append("@rpath/");
197197
#endif
198198
library_name_local.append(library_name);
199199

200200
dll_t dll_maybe = dlopen(library_name_local.c_str(), RTLD_LAZY | RTLD_NOLOAD);
201201
if (dll_maybe == nullptr)
202202
{
203-
if (pal::is_path_rooted(library_name))
203+
if (pal::is_path_fully_qualified(library_name))
204204
return false;
205205

206206
// dlopen on some systems only finds loaded libraries when given the full path
@@ -265,6 +265,11 @@ bool pal::is_path_rooted(const pal::string_t& path)
265265
return path.front() == '/';
266266
}
267267

268+
bool pal::is_path_fully_qualified(const pal::string_t& path)
269+
{
270+
return is_path_rooted(path);
271+
}
272+
268273
bool pal::get_default_breadcrumb_store(string_t* recv)
269274
{
270275
recv->clear();

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,9 +585,31 @@ pal::string_t pal::get_current_os_rid_platform()
585585
return ridOS;
586586
}
587587

588+
namespace
589+
{
590+
bool is_directory_separator(pal::char_t c)
591+
{
592+
return c == DIR_SEPARATOR || c == L'/';
593+
}
594+
}
595+
588596
bool pal::is_path_rooted(const string_t& path)
589597
{
590-
return path.length() >= 2 && path[1] == L':';
598+
return (path.length() >= 1 && is_directory_separator(path[0])) // UNC or device paths
599+
|| (path.length() >= 2 && path[1] == L':'); // Drive letter paths
600+
}
601+
602+
bool pal::is_path_fully_qualified(const string_t& path)
603+
{
604+
if (path.length() < 2)
605+
return false;
606+
607+
// Check for UNC and DOS device paths
608+
if (is_directory_separator(path[0]))
609+
return path[1] == L'?' || is_directory_separator(path[1]);
610+
611+
// Check for drive absolute path - for example C:\.
612+
return path.length() >= 3 && path[1] == L':' && is_directory_separator(path[2]);
591613
}
592614

593615
// Returns true only if an env variable can be read successfully to be non-empty.

src/native/corehost/hostpolicy/deps_resolver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ void deps_resolver_t::init_known_entry_path(const deps_entry_t& entry, const pal
601601
return;
602602
}
603603

604-
assert(pal::is_path_rooted(path));
604+
assert(pal::is_path_fully_qualified(path));
605605
if (m_coreclr_path.empty() && ends_with(path, DIR_SEPARATOR + pal::string_t(LIBCORECLR_NAME), false))
606606
{
607607
m_coreclr_path = path;

0 commit comments

Comments
 (0)