Skip to content

Commit 031a668

Browse files
committed
Fix absolute path check when loading hostfxr/hostpolicy/coreclr
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
1 parent b1ec39e commit 031a668

File tree

11 files changed

+87
-8
lines changed

11 files changed

+87
-8
lines changed

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,33 @@ public void AppHost_DisableCetCompat()
191191
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
192192
}
193193

194+
[Fact]
195+
[PlatformSpecific(TestPlatforms.Windows)]
196+
public void AppHost_DotNetRoot_DevicePath()
197+
{
198+
string appExe = sharedTestState.App.AppExe;
199+
200+
string dotnetPath = $@"\\?\{TestContext.BuiltDotNet.BinPath}";
201+
Command.Create(appExe)
202+
.CaptureStdErr()
203+
.CaptureStdOut()
204+
.DotNetRoot(dotnetPath, TestContext.BuildArchitecture)
205+
.Execute()
206+
.Should().Pass()
207+
.And.HaveStdOutContaining("Hello World")
208+
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
209+
210+
dotnetPath = $@"\\.\{TestContext.BuiltDotNet.BinPath}";
211+
Command.Create(appExe)
212+
.CaptureStdErr()
213+
.CaptureStdOut()
214+
.DotNetRoot(dotnetPath, TestContext.BuildArchitecture)
215+
.Execute()
216+
.Should().Pass()
217+
.And.HaveStdOutContaining("Hello World")
218+
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
219+
}
220+
194221
[Fact]
195222
public void RuntimeConfig_FilePath_Breaks_MAX_PATH_Threshold()
196223
{

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,29 @@ public void DotNetRoot_IncorrectLayout_Fails()
156156
.And.HaveStdErrContaining($"The required library {Binaries.HostFxr.FileName} could not be found.");
157157
}
158158

159+
[Fact]
160+
[PlatformSpecific(TestPlatforms.Windows)]
161+
public void DevicePath()
162+
{
163+
string appExe = $@"\\?\{sharedTestState.App.AppExe}";
164+
Command.Create(appExe)
165+
.CaptureStdErr()
166+
.CaptureStdOut()
167+
.Execute()
168+
.Should().Pass()
169+
.And.HaveStdOutContaining("Hello World")
170+
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
171+
172+
appExe = $@"\\.\{sharedTestState.App.AppExe}";
173+
Command.Create(appExe)
174+
.CaptureStdErr()
175+
.CaptureStdOut()
176+
.Execute()
177+
.Should().Pass()
178+
.And.HaveStdOutContaining("Hello World")
179+
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
180+
}
181+
159182
public class SharedTestState : IDisposable
160183
{
161184
public TestApp App { get; }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ hostfxr_resolver_t::hostfxr_resolver_t(const pal::string_t& app_root)
118118
{
119119
m_status_code = StatusCode::CoreHostLibMissingFailure;
120120
}
121-
else if (!pal::is_path_rooted(m_fxr_path))
121+
else if (!pal::is_path_fully_qualified(m_fxr_path))
122122
{
123123
// We should always be loading hostfxr from an absolute path
124124
m_status_code = StatusCode::CoreHostLibMissingFailure;

src/native/corehost/corehost.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ int exe_start(const int argc, const pal::char_t* argv[])
200200
int rc = fxr.status_code();
201201
if (rc != StatusCode::Success)
202202
{
203+
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);
203204
return rc;
204205
}
205206

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
@@ -56,7 +56,7 @@ int load_fxr_and_get_delegate(hostfxr_delegate_type type, THostPathToConfigCallb
5656
}
5757

5858
// We should always be loading hostfxr from an absolute path
59-
if (!pal::is_path_rooted(fxr_path))
59+
if (!pal::is_path_fully_qualified(fxr_path))
6060
return StatusCode::CoreHostLibMissingFailure;
6161

6262
// Load library

src/native/corehost/hostmisc/pal.h

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

336336
bool get_default_breadcrumb_store(string_t* recv);
337337
bool is_path_rooted(const string_t& path);
338+
bool is_path_fully_qualified(const string_t& path);
338339

339340
// Returns a platform-specific, user-private directory
340341
// 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
@@ -641,9 +641,31 @@ pal::string_t pal::get_current_os_rid_platform()
641641
return ridOS;
642642
}
643643

644+
namespace
645+
{
646+
bool is_directory_separator(pal::char_t c)
647+
{
648+
return c == DIR_SEPARATOR || c == L'/';
649+
}
650+
}
651+
644652
bool pal::is_path_rooted(const string_t& path)
645653
{
646-
return path.length() >= 2 && path[1] == L':';
654+
return (path.length() >= 1 && is_directory_separator(path[0])) // UNC or device paths
655+
|| (path.length() >= 2 && path[1] == L':'); // Drive letter paths
656+
}
657+
658+
bool pal::is_path_fully_qualified(const string_t& path)
659+
{
660+
if (path.length() < 2)
661+
return false;
662+
663+
// Check for UNC and DOS device paths
664+
if (is_directory_separator(path[0]))
665+
return path[1] == L'?' || is_directory_separator(path[1]);
666+
667+
// Check for drive absolute path - for example C:\.
668+
return path.length() >= 3 && path[1] == L':' && is_directory_separator(path[2]);
647669
}
648670

649671
// 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() && utils::ends_with(path, DIR_SEPARATOR_STR LIBCORECLR_NAME, false))
606606
{
607607
m_coreclr_path = path;

0 commit comments

Comments
 (0)