Skip to content
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

Dll Search Path Behavior for Windows System Dlls for certain dotnet apps (WPF?) #101857

Open
jlamhp opened this issue May 3, 2024 · 4 comments
Open
Milestone

Comments

@jlamhp
Copy link

jlamhp commented May 3, 2024

Background

When running a self-contained WPF app, it is observed that on startup in windows the exe makes calls to what looks like to load windows DLLs. However later on it attempts to load the same windows dll from the running directory of the exe. Example for one such DLL below (there are more, probably depending on includes).

procmon

callstack1

Steps to repo

  1. Create a new WPF app (I target .net 8, but also observed this in .net 6)
  2. Create a new publish target, make it single file self contained
  3. Publish the project
  4. Use procmon to observe the dll it searches for

Expected behavior: for critical window DLLs it should only be looking in c:\windows\system32 or other system directories
Actual behavior: it looks in the current working directory (or same directory?) as the executable.

Attempt to use things like SetDefaultDllDirectories via PInvoke does not work as the dlls are searched for before main or even static constructors are executed; suspect the dll's are being loaded at a lower level.

Ideas for fixes

  • Have a way to really only search system32 (or other system dirs) for loading windows dlls.
  • If not possible universally at least for single file deployment (maybe this behavior could extend to beyond windows dlls as well?)

Notes

  • This behavior was not seen in a blank console app; did not test to see what would happen if I added references that may trigger such calls.
  • In a non-single file scenario, I do see it searching for hostfxr.dll (and others including windows dlls like ntdll.dll) in both the exe dir and in the shared location. I am not sure if it is possible to restrict the searching of such dlls for framework or self contained (non-single file) deployment for dotnet dependencies.
  • As such I understand it may be impossible (or not even desired) to restrict dll search paths too much, especially if it impacts how shared installs of dotnet works. (I would think such locations would be protected by default). Maybe if there was a way to control it as needed by the dev?
  • I do realize certain files may be extracted and then referenced; ideally the search path should only target that directory.
  • I understand this may not be fixed (or that there is a design reason for this behavior), but still would like to put this out here.
Copy link
Contributor

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 3, 2024
@agocke
Copy link
Member

agocke commented May 13, 2024

You may need to build a custom host for this: https://learn.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting

@elinor-fung any other option for configuring the default DLL search directories?

@elinor-fung
Copy link
Member

The startup path resulting in this is a p/invoke into kernel32. I can see this specifically for LocalAlloc as part of RuntimeEventSource.Initialize (so it is before any user code can run), but any other p/invoke into kernel32 would hit this as well.

Because the application is deployed as self-contained, the folder containing the app is added to NATIVE_DLL_SEARCH_DIRECTORIES, since that has all the unmanaged libraries for the runtime itself. For single-file, the executable's directory is explicitly added to it (#42876). The p/invoke library load will then go through unmananged library probing, which looks in directories specified by NATIVE_DLL_SEARCH_DIRECTORIES.

I don't think there is any way a user could change this for self-contained + single-file right now, since custom hosting is non-single-file.

We could a make single-file not add the executable directory and handle bundled assembly p/invoke resolution in the runtime (option proposed, but not taken, in #42772). It would be a breaking change at this point though.

@jlamhp
Copy link
Author

jlamhp commented May 13, 2024

The startup path resulting in this is a p/invoke into kernel32. I can see this specifically for LocalAlloc as part of RuntimeEventSource.Initialize (so it is before any user code can run), but any other p/invoke into kernel32 would hit this as well.

Because the application is deployed as self-contained, the folder containing the app is added to NATIVE_DLL_SEARCH_DIRECTORIES, since that has all the unmanaged libraries for the runtime itself. For single-file, the executable's directory is explicitly added to it (#42876). The p/invoke library load will then go through unmananged library probing, which looks in directories specified by NATIVE_DLL_SEARCH_DIRECTORIES.

I don't think there is any way a user could change this for self-contained + single-file right now, since custom hosting is non-single-file.

We could a make single-file not add the executable directory and handle bundled assembly p/invoke resolution in the runtime (option proposed, but not taken, in #42772). It would be a breaking change at this point though.

Thanks for the input.

It does appear upon playing with self-contained vs framework dependent that the directory being searched is affected, in that the directory containing the dotnet library is being searched instead of the directory of the exe (in the case of framework dependent). To me in a single file scenario I would expect the exe containing the directory to not be searched as it is a single exe after all, instead the temp directory where anything that is extracted should be look at.

Understanding that searching the current directory could be breaking change, perhaps it could become a build/runtime config that one would have to enable, though it sounds like there may be other side effects of making such a change (since unmanaged could be force included, that location could be searched instead of current dir?)

For us at least for us the scope of the issue seems to be limited to win32 libs (or things that should only be loaded from %WINDIR%\system32 and maybe certain other dirs), if the scope was limited to such libs maybe that could be a possibility (and help other app configurations in the process).

@agocke agocke added this to the Future milestone May 17, 2024
@elinor-fung elinor-fung removed the untriaged New issue has not been triaged by the area owner label May 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

3 participants