CHEF-37610: Fix error 126 by registering DLL search directory for Windows PowerShell (.NET 481) path - #364
Merged
Merged
Conversation
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <john.mccrae@progress.com>
Signed-off-by: John McCrae <mccrae@progress.com>
Signed-off-by: John McCrae <mccrae@progress.com>
Signed-off-by: John McCrae <mccrae@progress.com>
Signed-off-by: John McCrae <mccrae@progress.com>
Signed-off-by: John McCrae <mccrae@progress.com>
tpowell-progress
approved these changes
Sep 2, 2026
11 tasks
johnmccrae
added a commit
that referenced
this pull request
Sep 8, 2026
#366) * Fix error 126 by registering DLL search directory for Windows PowerShell paths Backport of main commit f9c0fb4 (CHEF-37610, #364). Windows' default LoadLibrary(Ex) search order used by FFI does not include the directory of the DLL being loaded, so native VC++ runtime dependencies (vcruntime140.dll, msvcp140.dll, etc.) living alongside Chef.PowerShell.Wrapper.dll / Chef.PowerShell.Wrapper.Core.dll could fail to resolve with error 126 even though the wrapper DLL itself loaded fine via its absolute path. Adds a shared ChefPowerShell::Kernel32 FFI module (SetDefaultDllDirectories/ AddDllDirectory/SetDllDirectoryA) and registers the resolved bin directories before executing, for both the Windows PowerShell (.NET Framework 4.8.1) and pwsh (.NET Core) code paths. Adapted to this branch's existing resolve_wrapper_dll/resolve_core_wrapper_dll DLL resolution instead of main's ChefPowerShell.bin_dir helper, which does not exist on 18-Stable. * Adding the URI gem in and tweaking linting Signed-off-by: John McCrae <mccrae@progress.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a critical DLL-loading regression (Windows error 126) affecting the Windows PowerShell (.NET Framework 4.8.1 / Desktop) execution path when run against real Chef-18 installations. Also adds regression tests that would have caught this bug, and closes the gap that let it slip through the existing test suite.
JIRA Issue
CHEF-37610
Root Cause
Windows' default DLL search order used by FFI (
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) does not include the directory containing the DLL being loaded, norPATH, nor the current working directory. It only searches the hosting EXE's directory,System32, and any directories explicitly registered viaAddDllDirectory/SetDllDirectory.Chef.PowerShell.Wrapper.dlldepends on native VC++ runtime DLLs (vcruntime140.dll,msvcp140.dll, etc.) that live alongside it in the gem'sbin_dir. ThePwsh(.NET 10 / PowerShell Core) execution path already registered this directory correctly inPwsh#exec. However, the basePowerShellclass's#initialize(.NET Framework 4.8.1 / Windows PowerShell Desktop path) never registeredbin_diras a search directory at all.On a target machine without the VC++ redistributable already present in
System32(such as the Chef-18 test environment where this was discovered), the wrapper DLL loaded fine via its absolute path, but its dependent native DLLs could not be resolved — producing error 126.Why Existing Tests Didn't Catch This
raw_powershell) usedChefPowerShell::PowerShell.allocateplus manual instance variable injection to bypass gem path resolution — which also bypassed#initializeentirely, the exact method containing the bug.AddDllDirectory/SetDefaultDllDirectoriesmutate global, process-wide Windows search-path state that is never unregistered. Because the whole rspec suite runs in a single Ruby process, earlierPwsh-path tests had already registered the same physical directory used by the Desktop-path DLLs, silently masking the missing registration.System32(verified on this workstation), which satisfies the dependency regardless of whether the directory was registered — so even a naive "does it run" test can pass while the underlying registration bug is still present.Changes Made
Kernel32FFI module (SetDllDirectoryA,SetDefaultDllDirectories,AddDllDirectory,register_search_directory) from being duplicated/nested underPwshto a single shared definition directly underChefPowerShellinlib/chef-powershell/powershell.rb.PowerShell#initialize(mirroring the pattern already used inPwsh#exec): callsSetDefaultDllDirectories, thenregister_search_directory(bin_dir)(raisingLoadErroron failure), thenSetDllDirectoryA(bin_dir)as defense-in-depth for older Windows versions — before invokingexec.Kernel32module fromlib/chef-powershell/pwsh.rb. All bareKernel32.*references throughout the codebase (pwsh.rb,smoke_test_dlls.rb,verify_hab_build.rb) continue to resolve correctly via Ruby lexical scoping with no further changes required.spec/integration/dll_integration_spec.rbfromChefPowerShell::Pwsh::Kernel32toChefPowerShell::Kernel32.New Regression Tests
Added two tests to
spec/integration/dll_integration_spec.rbunder"PowerShell#initialize DLL directory registration (process isolation)":Open3.capture3) that calls the realChefPowerShell::PowerShell.newconstructor — reproducing the exact single-purpose-process condition production code runs under, with no possibility of state bleed from other examples in the suite.ChefPowerShell::Kernel32withand_call_originaland assertsSetDefaultDllDirectories,register_search_directory(bin_dir), andSetDllDirectoryA(bin_dir)are actually invoked duringPowerShell.new. Unlike the subprocess test, this does not depend on whether the VC++ redistributable happens to already be present on the machine running the suite — verified by temporarily reverting the fix and confirming this test fails deterministically, then restoring the fix and confirming it passes.Testing
AI Assistance
This work was completed with AI assistance following Progress AI policies.