Skip to content

fix(shim): rewrite x64 only system program path for x86 shim#6620

Open
silver886 wants to merge 9 commits intoScoopInstaller:developfrom
silver886:6619
Open

fix(shim): rewrite x64 only system program path for x86 shim#6620
silver886 wants to merge 9 commits intoScoopInstaller:developfrom
silver886:6619

Conversation

@silver886
Copy link
Copy Markdown

@silver886 silver886 commented Mar 14, 2026

Description

This PR rewrite system program path if the shim is x86 program.
Therefore, the shim can execute x64 only system program correctly.

Motivation and Context

Closes #6619

How Has This Been Tested?

Add the following bin block in any manifest, install, then run the shim.

"bin": [
  [
    "conhost.exe",
    "background-process",
    "--headless powershell -noprofile -command \"echo 'hello'\""
  ]
]

Checklist:

  • I have read the Contributing Guide.
  • I have ensured that I am targeting the develop branch.
  • I have updated the documentation accordingly.
  • I have updated the tests accordingly.
  • I have added an entry in the CHANGELOG.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed WoW64 file system redirection so 32-bit shims on 64-bit Windows resolve and record corrected system paths (System32/SysWOW64/Sysnative), ensuring proper execution and file access.
  • Tests

    • Added unit tests for PE machine detection and WoW64 path-rewrite behavior (including non-rewrite cases and OS-conditional behavior).

fix x64 system program path resolving
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds PE machine detection and WoW64 file-system redirection when emitting shim targets: new Get-PEMachine reads PE headers; shim emission rewrites System32/SysWOW64 paths for x86 shim executables on 64-bit Windows before writing the .shim entry. (≤50 words)

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Added Unreleased Bug Fixes entry documenting WoW64 file system redirection fix for shim path rewrites in .shim config.
Core PE detection & shim logic
lib/core.ps1
Added exported Get-PEMachine($filePath) to read PE Machine (UInt16) from PE headers; shim creation now computes the shim executable's machine and, on 64-bit Windows for x86 shims (0x014c), rewrites System32Sysnative and SysWOW64System32 in the recorded path and emits the rewritten path in the .shim file.
Tests
test/Scoop-Core.Tests.ps1
Added tests for Get-PEMachine() (valid PE, missing file, non-PE) and tests covering WoW64 path rewriting (System32→Sysnative, SysWOW64→System32, and ensuring non-System32 paths remain unchanged).

Sequence Diagram(s)

sequenceDiagram
    participant ShimCreator
    participant Filesystem
    participant PEReader
    participant OSChecker
    ShimCreator->>Filesystem: copy shim executable to app dir
    ShimCreator->>PEReader: Get-PEMachine(shim.exe)
    PEReader-->>ShimCreator: machine (e.g., 0x014c)
    ShimCreator->>OSChecker: Is64BitWindows?
    OSChecker-->>ShimCreator: true
    alt shim is x86 (0x014c) on x64
        ShimCreator->>ShimCreator: rewrite path: System32→Sysnative, SysWOW64→System32
    end
    ShimCreator->>Filesystem: write .shim file with rewritten path
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I peeked at PE headers, small and neat,

Found x86 footprints on a x64 street,
I nibbled System32 and hopped to Sysnative,
Mended paths so shims now run quite adaptive,
Carrots for fixes, crisp and sweet. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: fixing x86 shims to rewrite paths for x64-only system programs, which aligns with the primary objective of addressing issue #6619.
Linked Issues check ✅ Passed The PR fully implements the solution to address issue #6619: a new Get-PEMachine function detects x86 shims, and the shim function rewrites System32/SysWOW64 paths to enable x86 shims to execute x64-only system programs correctly.
Out of Scope Changes check ✅ Passed All changes are directly related to the PR's objective: CHANGELOG documentation, the core path-rewriting logic in lib/core.ps1, and comprehensive tests in the test suite.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/Scoop-Core.Tests.ps1 (1)

383-423: Prefer asserting actual .shim output instead of only string transforms.

Current tests validate replacement snippets, but they don’t verify that shim() emits rewritten path = ... under the x86-shim gate. An integration assertion on generated .shim content would better protect against regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/Scoop-Core.Tests.ps1` around lines 383 - 423, Update the tests to
exercise the actual shim() generator and assert on the produced .shim file
content rather than only string replace behavior: call shim(...) (or the
function that produces the .shim text under the x86-shim gate) with an input
that would contain $sysdir or $syswow (use the same $sysdir/$syswow/testPath
setup) and assert the returned/generated .shim text contains a "path = ..." line
with the expected rewritten value (e.g. Sysnative\notepad.exe for
System32->Sysnative and System32\notepad.exe for SysWOW64->System32), and also
add a test that feeding a non-system path leaves the "path = ..." line
unchanged; reference shim() and the ".shim" output when locating the code to
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/core.ps1`:
- Around line 24-42: Get-PEMachine can throw before $binaryReader/$fileStream
are assigned, so the finally block's unconditional $binaryReader.Close() and
$fileStream.Close() produce null method-call errors; fix by declaring and
initializing $binaryReader and $fileStream to $null before the try and guard
their Close() calls in the finally (e.g. if ($binaryReader) {
$binaryReader.Close() } and if ($fileStream) { $fileStream.Close() }) so Close()
is only invoked when the objects exist.

---

Nitpick comments:
In `@test/Scoop-Core.Tests.ps1`:
- Around line 383-423: Update the tests to exercise the actual shim() generator
and assert on the produced .shim file content rather than only string replace
behavior: call shim(...) (or the function that produces the .shim text under the
x86-shim gate) with an input that would contain $sysdir or $syswow (use the same
$sysdir/$syswow/testPath setup) and assert the returned/generated .shim text
contains a "path = ..." line with the expected rewritten value (e.g.
Sysnative\notepad.exe for System32->Sysnative and System32\notepad.exe for
SysWOW64->System32), and also add a test that feeding a non-system path leaves
the "path = ..." line unchanged; reference shim() and the ".shim" output when
locating the code to change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 918915e0-63b6-4a32-b776-fd163a42f579

📥 Commits

Reviewing files that changed from the base of the PR and between 0d0334c and 279f700.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • lib/core.ps1
  • test/Scoop-Core.Tests.ps1

@silver886 silver886 changed the title update core.ps1 fix(shim): rewrite x64 only system program path for x86 shim Mar 14, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
lib/core.ps1 (1)

24-42: ⚠️ Potential issue | 🟠 Major

Guard resource cleanup in Get-PEMachine's finally block.

If open/read fails before assignment, Line 39 and Line 40 invoke methods on $null, producing avoidable runtime errors.

💡 Proposed fix
 function Get-PEMachine($filePath) {
+    $fileStream = $null
+    $binaryReader = $null
     try {
         $fileStream = [System.IO.FileStream]::new($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
         $binaryReader = [System.IO.BinaryReader]::new($fileStream)
@@
     } catch {
         return 0
     } finally {
-        $binaryReader.Close()
-        $fileStream.Close()
+        if ($null -ne $binaryReader) { $binaryReader.Close() }
+        if ($null -ne $fileStream) { $fileStream.Close() }
     }
 }
#!/bin/bash
# Verify unguarded Close() calls exist in Get-PEMachine
rg -n "function Get-PEMachine|finally|\\$binaryReader\\.Close\\(\\)|\\$fileStream\\.Close\\(\\)" lib/core.ps1 -A8 -B3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/core.ps1` around lines 24 - 42, Get-PEMachine currently always calls
$binaryReader.Close() and $fileStream.Close() in the finally block which will
throw if those variables were never assigned; update the finally to guard
disposal by checking each variable is not $null before calling Close()/Dispose()
(or use PowerShell's using / try { } finally { if ($binaryReader -ne $null) {
$binaryReader.Close() } if ($fileStream -ne $null) { $fileStream.Close() } }).
Reference the function name Get-PEMachine and the symbols $binaryReader.Close()
and $fileStream.Close() when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/Scoop-Core.Tests.ps1`:
- Around line 383-423: The Describe block "WoW64 path rewriting in shim" is
Windows-specific but not marked as such; update the Describe invocation
(Describe 'WoW64 path rewriting in shim' -Tag 'Scoop') to include a Windows tag
(for example -Tag 'Scoop','Windows') so the three It tests (the two rewrite
checks and the "does not rewrite" check) are scoped to Windows runners and won't
run on non-Windows CI; alternatively wrap the whole Describe in an OS check to
call Set-ItResult -Skipped on non-Windows, but prefer adding the 'Windows' tag
to the existing Describe to match the review request.

---

Duplicate comments:
In `@lib/core.ps1`:
- Around line 24-42: Get-PEMachine currently always calls $binaryReader.Close()
and $fileStream.Close() in the finally block which will throw if those variables
were never assigned; update the finally to guard disposal by checking each
variable is not $null before calling Close()/Dispose() (or use PowerShell's
using / try { } finally { if ($binaryReader -ne $null) { $binaryReader.Close() }
if ($fileStream -ne $null) { $fileStream.Close() } }). Reference the function
name Get-PEMachine and the symbols $binaryReader.Close() and $fileStream.Close()
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2a50196-09b1-4196-abb2-198d11c44f6e

📥 Commits

Reviewing files that changed from the base of the PR and between 279f700 and 84a2e99.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • lib/core.ps1
  • test/Scoop-Core.Tests.ps1

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/core.ps1`:
- Around line 934-952: Get-ShimTarget can fail to resolve persisted paths that
contain "\Sysnative\" because Convert-Path is run in 64-bit PowerShell with
-ErrorAction SilentlyContinue; update Get-ShimTarget so that after the initial
Convert-Path attempt (the call using Convert-Path on the shim's stored
path/$target) if it returns $null or empty and the path contains "\Sysnative\",
replace "\Sysnative\" with "\System32\" and retry Convert-Path (or
Test-Path+Get-Item) to produce the normalized target; keep existing behavior for
successful resolves so x86 shim execution still uses Sysnative while 64-bit
callers get a System32 fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 17306012-ad59-41c7-9f42-92e2877e4576

📥 Commits

Reviewing files that changed from the base of the PR and between ec1643d and 1f80824.

📒 Files selected for processing (1)
  • lib/core.ps1

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/Scoop-Core.Tests.ps1 (1)

383-423: Add a non-x86 guard test and align Sysnative replacement value.

This block validates replacements but doesn’t assert the x86-only gate ($shim_machine -eq 0x014c). Also, Line 417 uses a literal 'Sysnative' instead of the full $sysnative path used in production logic.

♻️ Minimal alignment patch
 Describe 'WoW64 path rewriting in shim' -Tag 'Scoop', 'Windows' {
+    It 'does not rewrite when shim machine is not x86' {
+        $sysdir = [System.IO.Path]::Combine($env:SystemRoot, 'System32')
+        $sysnative = [System.IO.Path]::Combine($env:SystemRoot, 'Sysnative')
+        $rewrote_path = "$sysdir\notepad.exe"
+        $shim_machine = 0x8664
+
+        if ([System.Environment]::Is64BitOperatingSystem) {
+            if ($shim_machine -eq 0x014c -and $rewrote_path -like "$sysdir\*") {
+                $rewrote_path = $rewrote_path -replace [regex]::Escape($sysdir), $sysnative
+            }
+            $rewrote_path | Should -Be "$sysdir\notepad.exe"
+        } else {
+            Set-ItResult -Skipped -Because 'not a x64 OS'
+        }
+    }
+
     It 'does not rewrite paths outside System32 and SysWOW64' {
         $sysdir = [System.IO.Path]::Combine($env:SystemRoot, 'System32')
+        $sysnative = [System.IO.Path]::Combine($env:SystemRoot, 'Sysnative')
         $syswow = [System.IO.Path]::Combine($env:SystemRoot, 'SysWOW64')
         $testPath = 'C:\Program Files\test\app.exe'
@@
         $result = $testPath
         if ($result -like "$sysdir\*") {
-            $result = $result -replace [regex]::Escape($sysdir), 'Sysnative'
+            $result = $result -replace [regex]::Escape($sysdir), $sysnative
         } elseif ($result -like "$syswow\*") {
             $result = $result -replace [regex]::Escape($syswow), $sysdir
         }
         $result | Should -Be $testPath
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/Scoop-Core.Tests.ps1` around lines 383 - 423, Add the missing x86-shim
guard and fix the Sysnative replacement: in the tests that assert WoW64
rewriting (the It blocks checking System32→Sysnative and SysWOW64→System32)
ensure you check the shim machine value ($shim_machine -eq 0x014c) and skip if
not x86 (use Set-ItResult -Skipped -Because 'not an x86 shim'); in the "does not
rewrite paths outside System32 and SysWOW64" test replace the literal
'Sysnative' with the actual $sysnative variable so the replacement mirrors
production logic and only perform replacements when $shim_machine indicates x86.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/Scoop-Core.Tests.ps1`:
- Around line 383-423: Add the missing x86-shim guard and fix the Sysnative
replacement: in the tests that assert WoW64 rewriting (the It blocks checking
System32→Sysnative and SysWOW64→System32) ensure you check the shim machine
value ($shim_machine -eq 0x014c) and skip if not x86 (use Set-ItResult -Skipped
-Because 'not an x86 shim'); in the "does not rewrite paths outside System32 and
SysWOW64" test replace the literal 'Sysnative' with the actual $sysnative
variable so the replacement mirrors production logic and only perform
replacements when $shim_machine indicates x86.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3be4905b-3eff-4ff9-b311-6858ad6417d6

📥 Commits

Reviewing files that changed from the base of the PR and between 1f80824 and a72c193.

📒 Files selected for processing (1)
  • test/Scoop-Core.Tests.ps1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant