Skip to content

Commit 15d227c

Browse files
authored
Don't use incompatible LOAD_LIBRARY_SEARCH flags when using LOAD_WITH_ALTERED_SEARCH_PATH (#111990)
LoadLibraryEx documents that the `LOAD_WITH_ALTERED_SEARCH_PATH` flag is incompatible with `LOAD_LIBRARY_SEARCH` flags. In cases where we explicitly add this flag (absolute path or looking in p/invoke assembly directory), we could end up specifying an incompatible combination if other `DllImportSearchPath` flags corresponding to the `LOAD_LIBRARY_SEARCH` flags were also specified. This change avoids using those flags in when the runtime specifies loading with an altered search path. Fixes #111825
1 parent 850b0ba commit 15d227c

File tree

9 files changed

+105
-19
lines changed

9 files changed

+105
-19
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl
6767
IntPtr ret;
6868

6969
int loadWithAlteredPathFlags = LoadWithAlteredSearchPathFlag;
70+
const int loadLibrarySearchFlags = (int)DllImportSearchPath.UseDllDirectoryForDependencies
71+
| (int)DllImportSearchPath.ApplicationDirectory
72+
| (int)DllImportSearchPath.UserDirectories
73+
| (int)DllImportSearchPath.System32
74+
| (int)DllImportSearchPath.SafeDirectories;
7075
bool libNameIsRelativePath = !Path.IsPathFullyQualified(libraryName);
7176

7277
// P/Invokes are often declared with variations on the actual library name.
@@ -80,14 +85,8 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl
8085

8186
if (!libNameIsRelativePath)
8287
{
83-
int flags = loadWithAlteredPathFlags;
84-
if ((dllImportSearchPathFlags & (int)DllImportSearchPath.UseDllDirectoryForDependencies) != 0)
85-
{
86-
// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
87-
// unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
88-
flags |= dllImportSearchPathFlags;
89-
}
90-
88+
// LOAD_WITH_ALTERED_SEARCH_PATH is incompatible with LOAD_LIBRARY_SEARCH flags. Remove those flags if they are set.
89+
int flags = loadWithAlteredPathFlags | (dllImportSearchPathFlags & ~loadLibrarySearchFlags);
9190
ret = LoadLibraryHelper(currLibNameVariation, flags, ref errorTracker);
9291
if (ret != IntPtr.Zero)
9392
{
@@ -96,9 +95,12 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl
9695
}
9796
else if ((callingAssembly != null) && searchAssemblyDirectory)
9897
{
98+
// LOAD_WITH_ALTERED_SEARCH_PATH is incompatible with LOAD_LIBRARY_SEARCH flags. Remove those flags if they are set.
99+
int flags = loadWithAlteredPathFlags | (dllImportSearchPathFlags & ~loadLibrarySearchFlags);
100+
99101
// Try to load the module alongside the assembly where the PInvoke was declared.
100102
// For PInvokes where the DllImportSearchPath.AssemblyDirectory is specified, look next to the application.
101-
ret = LoadLibraryHelper(Path.Combine(AppContext.BaseDirectory, currLibNameVariation), loadWithAlteredPathFlags | dllImportSearchPathFlags, ref errorTracker);
103+
ret = LoadLibraryHelper(Path.Combine(AppContext.BaseDirectory, currLibNameVariation), flags, ref errorTracker);
102104
if (ret != IntPtr.Zero)
103105
{
104106
return ret;

src/coreclr/vm/nativelibrary.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ namespace
476476
NATIVE_LIBRARY_HANDLE hmod = NULL;
477477

478478
SString path{ pAssembly->GetPEAssembly()->GetPath() };
479+
_ASSERTE(!Path::IsRelative(path));
479480

480481
SString::Iterator lastPathSeparatorIter = path.End();
481482
if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter))
@@ -656,6 +657,14 @@ namespace
656657

657658
AppDomain* pDomain = GetAppDomain();
658659
DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag();
660+
DWORD loadLibrarySearchFlags = 0;
661+
#ifdef TARGET_WINDOWS
662+
loadLibrarySearchFlags = LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
663+
| LOAD_LIBRARY_SEARCH_APPLICATION_DIR
664+
| LOAD_LIBRARY_SEARCH_USER_DIRS
665+
| LOAD_LIBRARY_SEARCH_SYSTEM32
666+
| LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
667+
#endif
659668
bool libNameIsRelativePath = Path::IsRelative(wszLibName);
660669

661670
// P/Invokes are often declared with variations on the actual library name.
@@ -689,14 +698,8 @@ namespace
689698

690699
if (!libNameIsRelativePath)
691700
{
692-
DWORD flags = loadWithAlteredPathFlags;
693-
if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
694-
{
695-
// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
696-
// unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
697-
flags |= dllImportSearchPathFlags;
698-
}
699-
701+
// LOAD_WITH_ALTERED_SEARCH_PATH is incompatible with LOAD_LIBRARY_SEARCH flags. Remove those flags if they are set.
702+
DWORD flags = loadWithAlteredPathFlags | (dllImportSearchPathFlags & ~loadLibrarySearchFlags);
700703
hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker);
701704
if (hmod != NULL)
702705
{
@@ -705,7 +708,9 @@ namespace
705708
}
706709
else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
707710
{
708-
hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
711+
// LOAD_WITH_ALTERED_SEARCH_PATH is incompatible with LOAD_LIBRARY_SEARCH flags. Remove those flags if they are set.
712+
DWORD flags = loadWithAlteredPathFlags | (dllImportSearchPathFlags & ~loadLibrarySearchFlags);
713+
hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, flags, pErrorTracker);
709714
if (hmod != NULL)
710715
{
711716
return hmod;

src/mono/mono/metadata/native-library.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,22 @@ convert_dllimport_flags (int flags)
275275
#endif
276276
}
277277

278+
static int
279+
add_load_with_altered_search_path_flags (int flags)
280+
{
281+
#ifdef HOST_WIN32
282+
// LOAD_WITH_ALTERED_SEARCH_PATH is incompatible with LOAD_LIBRARY_SEARCH flags. Remove those flags if they are set.
283+
int load_library_search_flags = LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
284+
| LOAD_LIBRARY_SEARCH_APPLICATION_DIR
285+
| LOAD_LIBRARY_SEARCH_USER_DIRS
286+
| LOAD_LIBRARY_SEARCH_SYSTEM32
287+
| LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
288+
return LOAD_WITH_ALTERED_SEARCH_PATH | (flags & ~load_library_search_flags);
289+
#else
290+
return flags;
291+
#endif
292+
}
293+
278294
static MonoDl *
279295
netcore_probe_for_module_variations (const char *mdirname, const char *file_name, int raw_flags, MonoError *error)
280296
{
@@ -352,7 +368,7 @@ netcore_probe_for_module (MonoImage *image, const char *file_name, int flags, Mo
352368
error_init_reuse (error);
353369
char *mdirname = g_path_get_dirname (image->filename);
354370
if (mdirname)
355-
module = netcore_probe_for_module_variations (mdirname, file_name, lflags, error);
371+
module = netcore_probe_for_module_variations (mdirname, file_name, add_load_with_altered_search_path_flags(lflags), error);
356372
g_free (mdirname);
357373
}
358374

src/tests/Interop/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ add_subdirectory(MarshalAPI/FunctionPointer)
5757
add_subdirectory(NativeLibrary/NativeLibraryToLoad)
5858
add_subdirectory(DllImportAttribute/DllImportPath)
5959
add_subdirectory(DllImportAttribute/ExactSpelling)
60+
add_subdirectory(DllImportSearchPaths/NativeLibraryWithDependency)
6061
add_subdirectory(ICustomMarshaler/ConflictingNames)
6162
add_subdirectory(ICustomMarshaler/Primitives)
6263
add_subdirectory(LayoutClass)

src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ public static void AssemblyDirectory_Fallback_Found()
6565
Environment.CurrentDirectory = currentDirectory;
6666
}
6767
}
68+
69+
[ConditionalFact(nameof(CanLoadAssemblyInSubdirectory))]
70+
[PlatformSpecific(TestPlatforms.Windows)]
71+
public static void AssemblyDirectory_SearchFlags_WithDependency_Found()
72+
{
73+
// Library and its dependency should be found in the assembly directory.
74+
var assembly = Assembly.LoadFile(Path.Combine(Subdirectory, $"{nameof(DllImportSearchPathsTest)}.dll"));
75+
var type = assembly.GetType(nameof(NativeLibraryWithDependency));
76+
var method = type.GetMethod(nameof(NativeLibraryWithDependency.Sum));
77+
78+
int sum = (int)method.Invoke(null, new object[] { 1, 2 });
79+
Assert.Equal(3, sum);
80+
Console.WriteLine("NativeLibraryWithDependency.Sum returned {0}", sum);
81+
}
6882
}
6983

7084
public class NativeLibraryPInvoke
@@ -93,3 +107,18 @@ public static int Sum(int a, int b)
93107
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)]
94108
static extern int NativeSum(int arg1, int arg2);
95109
}
110+
111+
public class NativeLibraryWithDependency
112+
{
113+
public static int Sum(int a, int b)
114+
{
115+
return CallDependencySum(a, b);
116+
}
117+
118+
// For LoadLibrary on Windows, search flags, like that represented by System32, are incompatible with
119+
// looking at a specific path (per AssemblyDirectory), so we specify both flags to validate that we do
120+
// not incorrectly use both when looking in the assembly directory.
121+
[DllImport(nameof(NativeLibraryWithDependency))]
122+
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)]
123+
static extern int CallDependencySum(int a, int b);
124+
}

src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<Compile Include="*.cs" />
1010
<Compile Include="../NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs" />
1111
<CMakeProjectReference Include="../NativeLibrary/NativeLibraryToLoad/CMakeLists.txt" />
12+
<CMakeProjectReference Include="NativeLibraryWithDependency/CMakeLists.txt" />
1213
</ItemGroup>
1314

1415
<PropertyGroup>
@@ -18,7 +19,9 @@
1819
<Target Name="SetUpSubdirectoryNative" AfterTargets="CopyNativeProjectBinaries">
1920
<ItemGroup>
2021
<NativeLibrariesToMove Include="$(OutDir)/libNativeLibrary.*" />
22+
<NativeLibrariesToMove Include="$(OutDir)/libNativeLibraryWithDependency.*" />
2123
<NativeLibrariesToMove Include="$(OutDir)/NativeLibrary.*" />
24+
<NativeLibrariesToMove Include="$(OutDir)/NativeLibraryWithDependency.*" />
2225
</ItemGroup>
2326
<Move SourceFiles="@(NativeLibrariesToMove)" DestinationFiles="@(NativeLibrariesToMove -> '$(LibrarySubdirectory)/%(Filename)%(Extension)')"/>
2427
</Target>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
project(NativeLibraryWithDependency)
2+
include("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
3+
4+
add_library(Dependency SHARED Dependency.cpp)
5+
target_link_libraries(Dependency PRIVATE ${LINK_LIBRARIES_ADDITIONAL})
6+
7+
add_library(NativeLibraryWithDependency SHARED NativeLibraryWithDependency.cpp)
8+
target_link_libraries(NativeLibraryWithDependency PRIVATE Dependency ${LINK_LIBRARIES_ADDITIONAL})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#include <platformdefines.h>
5+
6+
extern "C" DLL_EXPORT int STDMETHODCALLTYPE Sum(int a, int b)
7+
{
8+
return a + b;
9+
}
10+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#include <platformdefines.h>
5+
6+
extern "C" int STDMETHODCALLTYPE Sum(int a, int b);
7+
8+
extern "C" DLL_EXPORT int STDMETHODCALLTYPE CallDependencySum(int a, int b)
9+
{
10+
return Sum(a, b);
11+
}
12+

0 commit comments

Comments
 (0)