Skip to content

Fix crash from stale TrustedInstaller PID on elevated tweaks#130

Merged
Greedeks merged 1 commit into
Greedeks:mainfrom
YayoRazo:fix/trusted-installer-stale-pid
Jul 13, 2026
Merged

Fix crash from stale TrustedInstaller PID on elevated tweaks#130
Greedeks merged 1 commit into
Greedeks:mainfrom
YayoRazo:fix/trusted-installer-stale-pid

Conversation

@YayoRazo

@YayoRazo YayoRazo commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

CreateProcessAsTrustedInstaller caches the TrustedInstaller service PID once at app startup (TrustedInstaller.StartTrustedInstallerService(), called only from LoadingWindow.xaml.cs) and never refreshes it. TrustedInstaller is a demand-start Windows service that stops itself when idle, so any elevated tweak run after that happens calls OpenProcess on a PID that no longer exists, which fails with ERROR_INVALID_PARAMETER (87) and crashes the app with an unhandled Win32Exception. This matches #127 exactly (same exception type/class/method, different call site).

Why this isn't already fixed

#127's own thread shows a fix already shipped in 5.5.2 (f757319, "fix buffer size overflow in CreateProcessAsTrustedInstaller" - same change as 17b49be on main, just a different SHA from the squash-merge). That's a real fix, but for a different bug in the same function (a marshaling/buffer-size mismatch), not this one. After that release shipped, DkmS1953 hit the identical crash from a third call site (ServicesTweaks.ApplyTweaks), on top of the original report's ConfidentialityTweaks and this PR's WindowsDefender.Deactivate. Three independent callers hitting the same stale-PID failure through the shared CreateProcessAsTrustedInstaller helper is why the fix belongs there, not in any one caller.

Fix

Before opening the parent process handle, check whether the cached PID still refers to a running process (Process.GetProcessById + HasExited). If not, restart the TrustedInstaller service and use the refreshed PID for that attempt. This fixes the shared helper, so it covers every caller (Defender, Confidentiality, Services, etc.), not just one tweak.

Verification

No test project exists in this repo, so here's a runnable repro proving the exact failure mode this fix addresses:

Add-Type -Namespace Win32 -Name Native -MemberDefinition @"
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
public static extern System.IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
"@
$usedPids = (Get-Process).Id
$candidate = 999999
while ($usedPids -contains $candidate) { $candidate++ }
$access = 0x0080 -bor 0x0040 -bor 0x100000  # CREATE_PROCESS | DUP_HANDLE | SYNCHRONIZE
$handle = [Win32.Native]::OpenProcess($access, $false, $candidate)
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
"$handle / $err"   # -> 0 / 87, exactly this app's crash signature

Also verified:

  • msbuild GTweak.sln /t:Rebuild /p:Configuration=Release succeeds both before and after this change (0 errors).
  • The new IsProcessRunning guard correctly returns false for a PID with no live process (confirmed via Process.GetProcessById throwing ArgumentException), the exact condition that triggers the refresh path.

Fixes #127

@Greedeks Greedeks merged commit fc9167a into Greedeks:main Jul 13, 2026
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.

GTweak has crashed!

2 participants