Skip to content

[release/9.0-staging] Fix absolute path check when loading hostfxr/hostpolicy/coreclr #116775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: release/9.0-staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,33 @@ public void AppHost_DisableCetCompat()
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public void AppHost_DotNetRoot_DevicePath()
{
string appExe = sharedTestState.App.AppExe;

string dotnetPath = $@"\\?\{TestContext.BuiltDotNet.BinPath}";
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.DotNetRoot(dotnetPath, TestContext.BuildArchitecture)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);

dotnetPath = $@"\\.\{TestContext.BuiltDotNet.BinPath}";
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.DotNetRoot(dotnetPath, TestContext.BuildArchitecture)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
}

[Fact]
public void RuntimeConfig_FilePath_Breaks_MAX_PATH_Threshold()
{
Expand Down
23 changes: 23 additions & 0 deletions src/installer/tests/HostActivation.Tests/SelfContainedAppLaunch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,29 @@ public void DotNetRoot_IncorrectLayout_Fails()
.And.HaveStdErrContaining($"The required library {Binaries.HostFxr.FileName} could not be found.");
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public void DevicePath()
{
string appExe = $@"\\?\{sharedTestState.App.AppExe}";
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);

appExe = $@"\\.\{sharedTestState.App.AppExe}";
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
}

public class SharedTestState : IDisposable
{
public TestApp App { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ hostfxr_resolver_t::hostfxr_resolver_t(const pal::string_t& app_root)
{
m_status_code = StatusCode::CoreHostLibMissingFailure;
}
else if (!pal::is_path_rooted(m_fxr_path))
else if (!pal::is_path_fully_qualified(m_fxr_path))
{
// We should always be loading hostfxr from an absolute path
m_status_code = StatusCode::CoreHostLibMissingFailure;
Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/corehost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ int exe_start(const int argc, const pal::char_t* argv[])
int rc = fxr.status_code();
if (rc != StatusCode::Success)
{
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);
return rc;
}

Expand Down
2 changes: 1 addition & 1 deletion src/native/corehost/fxr/standalone/hostpolicy_resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ int hostpolicy_resolver::load(
}

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

// Load library
Expand Down
2 changes: 1 addition & 1 deletion src/native/corehost/fxr_resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ int load_fxr_and_get_delegate(hostfxr_delegate_type type, THostPathToConfigCallb
}

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

// Load library
Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/hostmisc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ namespace pal

bool get_default_breadcrumb_store(string_t* recv);
bool is_path_rooted(const string_t& path);
bool is_path_fully_qualified(const string_t& path);

// Returns a platform-specific, user-private directory
// that can be used for extracting out components of a single-file app.
Expand Down
9 changes: 7 additions & 2 deletions src/native/corehost/hostmisc/pal.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,15 @@ bool pal::get_loaded_library(
{
pal::string_t library_name_local;
#if defined(TARGET_OSX)
if (!pal::is_path_rooted(library_name))
if (!pal::is_path_fully_qualified(library_name))
library_name_local.append("@rpath/");
#endif
library_name_local.append(library_name);

dll_t dll_maybe = dlopen(library_name_local.c_str(), RTLD_LAZY | RTLD_NOLOAD);
if (dll_maybe == nullptr)
{
if (pal::is_path_rooted(library_name))
if (pal::is_path_fully_qualified(library_name))
return false;

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

bool pal::is_path_fully_qualified(const pal::string_t& path)
{
return is_path_rooted(path);
}

bool pal::get_default_breadcrumb_store(string_t* recv)
{
recv->clear();
Expand Down
24 changes: 23 additions & 1 deletion src/native/corehost/hostmisc/pal.windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,9 +641,31 @@ pal::string_t pal::get_current_os_rid_platform()
return ridOS;
}

namespace
{
bool is_directory_separator(pal::char_t c)
{
return c == DIR_SEPARATOR || c == L'/';
}
}

bool pal::is_path_rooted(const string_t& path)
{
return path.length() >= 2 && path[1] == L':';
return (path.length() >= 1 && is_directory_separator(path[0])) // UNC or device paths
|| (path.length() >= 2 && path[1] == L':'); // Drive letter paths
}

bool pal::is_path_fully_qualified(const string_t& path)
{
if (path.length() < 2)
return false;

// Check for UNC and DOS device paths
if (is_directory_separator(path[0]))
return path[1] == L'?' || is_directory_separator(path[1]);

// Check for drive absolute path - for example C:\.
return path.length() >= 3 && path[1] == L':' && is_directory_separator(path[2]);
}

// Returns true only if an env variable can be read successfully to be non-empty.
Expand Down
2 changes: 1 addition & 1 deletion src/native/corehost/hostpolicy/deps_resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ void deps_resolver_t::init_known_entry_path(const deps_entry_t& entry, const pal
return;
}

assert(pal::is_path_rooted(path));
assert(pal::is_path_fully_qualified(path));
if (m_coreclr_path.empty() && utils::ends_with(path, DIR_SEPARATOR_STR LIBCORECLR_NAME, false))
{
m_coreclr_path = path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ bool coreclr_resolver_t::resolve_coreclr(const pal::string_t& libcoreclr_path, c
append_path(&coreclr_dll_path, LIBCORECLR_NAME);

// We should always be loading coreclr from an absolute path
if (!pal::is_path_rooted(coreclr_dll_path))
if (!pal::is_path_fully_qualified(coreclr_dll_path))
return false;

if (!pal::load_library(&coreclr_dll_path, &coreclr_resolver_contract.coreclr))
Expand Down
Loading