fix(windows): make the pre-window ACL gate act on the poison evidence it fired on

The gate fired on a poison marker — an earlier launch's DACL reading that nothing has
retired — but withheld `probeConfirmedPoisoned` from the repair, so a repair marker
recording an older success still short-circuited it. On the three-launch shape the gate
exists for (repair succeeds; tree is re-poisoned; the next launch's probe records the
poison but dies before writing its repair marker) the gate ran no icacls, deleted the
poison marker that arms every later gate, un-suspected the tree so --in-process-gpu could
engage, and told the user "Orca repaired the permissions." `applyInstallDirAclProbeVerdict`
then swallowed that launch's own reading behind `if (poison) return`.

Both callers of `startRepair` hold outstanding poison evidence, so the flag is now
unconditional (renamed `poisonEvidenceOutstanding`) and `marker-hit` means only that the
attempt budget is spent. The probe guard is narrowed to an in-flight gate repair: a reading
taken after the gate finished re-arms the poison marker and downgrades a claimed repair.

Also: withholding safe graphics now ends with the repair budget. A machine whose attempts
are spent while the signature persists was denied safe graphics on every launch for the
life of that appVersion — and had its marker deleted each time — including the healthy
installs the probe's flag-blind ACE match over-matches, where the driver really is broken.

Non-blocking, same lane: re-read `isQuitting` after the up-to-15s verdict wait, and skip
the recovered-launch prompt when the ACL gate retired the marker read before whenReady.
This commit is contained in:
Neil
2026-09-03 04:18:42 -07:00
parent 7e6a1f1c07
commit 2e0c1ca85b
5 changed files with 303 additions and 65 deletions
@@ -1,4 +1,4 @@
import { mkdtempSync } from 'node:fs'
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
@@ -38,20 +38,30 @@ import {
DEFAULT_GPU_CRASH_FALLBACK_WINDOW_MS,
GpuCrashFallbackTracker
} from '../crash-reporting/gpu-crash-fallback-decision'
import { readGpuFallbackMarker } from './gpu-fallback-marker'
import { handleGpuChildCrash } from './gpu-lifecycle'
import { mainProcessState as state } from './main-process-state'
import {
readGpuFallbackMarker,
writeGpuFallbackMarker,
type GpuFallbackMarker
} from './gpu-fallback-marker'
import { handleGpuChildCrash, presentGpuFallbackRecoveredLaunchPrompt } from './gpu-lifecycle'
import { gpuFallbackEnvironment, mainProcessState as state } from './main-process-state'
import { writeInstallDirAclPoisonMarker } from './windows-install-dir-acl-poison-marker'
import {
isInstallDirAclRepairPending,
noteWindowsInstallDirAclProbePending,
repairKnownPoisonedInstallDirBeforeWindow,
resetWindowsInstallDirAclRecoveryForTest,
startWindowsInstallDirAclRepairIfPoisoned
} from './windows-install-dir-acl-recovery'
import { resetWindowsInstallDirAclRepairForTest } from './windows-install-dir-package-acl-repair'
import {
resetWindowsInstallDirAclRepairForTest,
WINDOWS_INSTALL_DIR_ACL_REPAIR_MARKER_FILE,
WINDOWS_INSTALL_DIR_ACL_REPAIR_SCHEME_VERSION
} from './windows-install-dir-package-acl-repair'
const INSTALL_DIR = 'C:\\Users\\neil\\AppData\\Local\\Programs\\orca'
function recoveryOptions(): {
function recoveryOptions(userDataPath?: string): {
platform: 'win32'
installDir: string
appVersion: string
@@ -62,7 +72,7 @@ function recoveryOptions(): {
platform: 'win32',
installDir: INSTALL_DIR,
appVersion: '1.4.184',
userDataPath: mkdtempSync(join(tmpdir(), 'orca-acl-gpu-guard-')),
userDataPath: userDataPath ?? mkdtempSync(join(tmpdir(), 'orca-acl-gpu-guard-')),
recordBreadcrumb: () => undefined
}
}
@@ -100,11 +110,14 @@ function reportProbePoisoned(): { finishRepair: () => Promise<void> } {
}
/** A repair that settles, so `poison.stage` leaves 'pending' for a terminal verdict. */
async function reportProbePoisonedWithSettledRepair(exitCode: number): Promise<void> {
async function reportProbePoisonedWithSettledRepair(
exitCode: number,
userDataPath?: string
): Promise<void> {
startWindowsInstallDirAclRepairIfPoisoned(
{ status: 'ok', matchesPoisonSignature: true, wellKnownNameCheckReliable: true },
{
...recoveryOptions(),
...recoveryOptions(userDataPath),
runProcessFn: (async () => ({
code: exitCode,
signal: null,
@@ -119,6 +132,33 @@ async function reportProbePoisonedWithSettledRepair(exitCode: number): Promise<v
}
}
/**
* The pre-window gate meeting a spent repair budget: the tree is still marked poisoned and
* Orca has no repair left to try. icacls must never be reached, so the runner throws.
*/
async function gateFindsRepairBudgetSpent(): Promise<void> {
const options = recoveryOptions()
writeFileSync(
join(options.userDataPath, WINDOWS_INSTALL_DIR_ACL_REPAIR_MARKER_FILE),
JSON.stringify({
schemeVersion: WINDOWS_INSTALL_DIR_ACL_REPAIR_SCHEME_VERSION,
installDir: INSTALL_DIR,
appVersion: options.appVersion,
attemptedAt: Date.now(),
outcome: 'failed',
attempts: 3
})
)
writeInstallDirAclPoisonMarker(options.userDataPath, INSTALL_DIR, options.appVersion)
const mode = await repairKnownPoisonedInstallDirBeforeWindow({
...options,
runProcessFn: (() => {
throw new Error('the spent budget must not spawn icacls')
}) as never
})
expect(mode).toBe('marker-hit')
}
function reportProbeClean(): void {
startWindowsInstallDirAclRepairIfPoisoned(
{ status: 'ok', matchesPoisonSignature: false },
@@ -246,6 +286,50 @@ describe('handleGpuChildCrash vs the install-dir ACL verdict', () => {
expect(readGpuFallbackMarker(userData.path)).toBeNull()
})
// The verdict wait can span the probe's whole 15s grace window, and the entry guard was
// read before it. A quit that starts inside the wait must not be answered with a modal.
it('does not prompt when the user quits during the verdict wait', async () => {
noteWindowsInstallDirAclProbePending()
await crashUpToThreshold()
const decisive = handleGpuChildCrash('crashed', null, 600)
state.isQuitting = true
reportProbeClean()
await decisive
expect(showMessageBox).not.toHaveBeenCalled()
})
// Withholding is a bounded delay, not a permanent suppression. Once the repair budget is
// spent no repair is coming on this launch or any later one, so pinning the tree as the
// suspect forever denied safe graphics on EVERY launch for the life of that version — and
// deleted the marker each time, so the machine also relaunched hardware accelerated. The
// victims are a standard-user install icacls can never fix and, via the probe's flag-blind
// ACE match, healthy installs whose driver genuinely is broken.
it('offers safe graphics on every launch once the ACL repair budget is spent', async () => {
for (let launch = 1; launch <= 3; launch += 1) {
resetWindowsInstallDirAclRepairForTest()
resetWindowsInstallDirAclRecoveryForTest()
showMessageBox.mockClear()
state.gpuCrashFallbackTracker = new GpuCrashFallbackTracker({
windowMs: DEFAULT_GPU_CRASH_FALLBACK_WINDOW_MS,
threshold: DEFAULT_GPU_CRASH_FALLBACK_THRESHOLD
})
await gateFindsRepairBudgetSpent()
await crashUpToThreshold()
await handleGpuChildCrash('crashed', null, 600)
expect(showMessageBox).toHaveBeenCalledTimes(1)
}
})
// Still withheld while the budget has an attempt left: the repair is the better answer,
// and this is the launch a next one can be rescued on.
it('still withholds while the repair has an attempt left to spend', async () => {
await reportProbePoisonedWithSettledRepair(1)
await crashUpToThreshold()
await handleGpuChildCrash('crashed', null, 600)
expect(showMessageBox).not.toHaveBeenCalled()
})
// recordGpuCrash reports the threshold crossing once and latches. Withholding consumes
// that one report, so without a re-arm the same process could never engage again — a
// machine whose tree is repaired and whose driver is genuinely broken would be stuck
@@ -266,3 +350,49 @@ describe('handleGpuChildCrash vs the install-dir ACL verdict', () => {
expect(showMessageBox).toHaveBeenCalledTimes(1)
})
})
// The safe-graphics marker is read before whenReady, and the pre-window ACL gate runs after
// that read. Asking "keep safe graphics?" on a machine Orca has just repaired invites a
// `userConfirmed: true` marker that pins software rendering on healthy hardware.
describe('presentGpuFallbackRecoveredLaunchPrompt vs a marker retired since it was read', () => {
const realPlatform = process.platform
const window = { isDestroyed: () => false } as unknown as Parameters<
typeof presentGpuFallbackRecoveredLaunchPrompt
>[0]
beforeAll(() => {
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true })
})
afterAll(() => {
Object.defineProperty(process, 'platform', { value: realPlatform, configurable: true })
})
beforeEach(() => {
userData.path = mkdtempSync(join(tmpdir(), 'orca-acl-gpu-recovered-'))
resetWindowsInstallDirAclRepairForTest()
resetWindowsInstallDirAclRecoveryForTest()
showMessageBox.mockClear()
state.isQuitting = false
const info = { engagedAt: Date.now(), crashesInWindow: 3, userConfirmed: false }
writeGpuFallbackMarker(userData.path, info, {
...gpuFallbackEnvironment(),
platform: 'win32'
})
state.activeGpuFallbackMarker = readGpuFallbackMarker(userData.path) as GpuFallbackMarker
})
it('asks while the marker is still on disk', async () => {
showMessageBox.mockResolvedValueOnce({ response: 0 })
await presentGpuFallbackRecoveredLaunchPrompt(window)
expect(showMessageBox).toHaveBeenCalledTimes(1)
})
it('stays silent once the install-DACL repair has cleared it', async () => {
await reportProbePoisonedWithSettledRepair(0, userData.path)
expect(readGpuFallbackMarker(userData.path)).toBeNull()
await presentGpuFallbackRecoveredLaunchPrompt(window)
expect(showMessageBox).not.toHaveBeenCalled()
})
})
+25 -2
View File
@@ -17,6 +17,7 @@ import { engageGpuFallbackAfterCrashBurst } from '../crash-reporting/gpu-fallbac
import { recordCrashBreadcrumb } from '../crash-reporting/crash-breadcrumb-store'
import { recordDurableCrashBreadcrumb } from '../crash-reporting/durable-crash-breadcrumb'
import {
isInstallDirAclRepairExhausted,
isInstallDirAclRepairPending,
isInstallDirAclSuspect,
waitForInstallDirAclVerdict
@@ -90,6 +91,11 @@ export async function presentGpuFallbackRecoveredLaunchPrompt(
// One prompt per process. A failure leaves the on-disk marker unconfirmed so the next launch retries.
state.activeGpuFallbackMarker = null
const userDataPath = app.getPath('userData')
// The marker was read before whenReady; the pre-window ACL gate can have retired it since.
// Asking then would let a "keep it" answer pin software rendering on a machine Orca just fixed.
if (!readActiveGpuFallbackMarker(userDataPath, gpuFallbackEnvironment())) {
return
}
await handleGpuFallbackRecoveredLaunch({
isQuitting: () => state.isQuitting,
prompt: () => promptForGpuFallbackRecoveredLaunch(window),
@@ -119,6 +125,18 @@ export async function presentGpuFallbackRecoveredLaunchPrompt(
})
}
/**
* Why withholding ends with the repair budget: withholding only buys the ACL repair the
* chance to land first. Once its attempts are spent no repair is coming on this launch or
* any later one, so holding safe graphics back forever would deny the only recovery left —
* on a genuinely poisoned tree Orca has already told the user the admin commands, and the
* probe's flag-blind ACE match also over-matches healthy installs whose driver really is
* the fault. It is a bounded delay, not a permanent suppression.
*/
function installDirAclWithholdsGpuFallback(): boolean {
return isInstallDirAclSuspect() && !isInstallDirAclRepairExhausted()
}
/**
* Why: a poisoned install DACL kills the GPU child exactly like a bad driver, but safe
* graphics does not rescue it and --in-process-gpu removes the GPU child, erasing the
@@ -133,7 +151,7 @@ async function installDirAclClearsGpuFallback(
userDataPath: string,
crashesInWindow: number
): Promise<boolean> {
if (!isInstallDirAclSuspect()) {
if (!installDirAclWithholdsGpuFallback()) {
return true
}
const persisted = persistGpuFallbackMarker(userDataPath, {
@@ -142,7 +160,7 @@ async function installDirAclClearsGpuFallback(
userConfirmed: false
})
await waitForInstallDirAclVerdict()
if (!isInstallDirAclSuspect()) {
if (!installDirAclWithholdsGpuFallback()) {
return true
}
// Why the marker survives a pending repair: withdrawing it here left a machine that
@@ -189,6 +207,11 @@ export async function handleGpuChildCrash(
if (!(await installDirAclClearsGpuFallback(userDataPath, result.crashesInWindow))) {
return
}
// Re-read after that wait: it can span the probe's whole grace window, and a quit that
// started inside it must not be answered with a modal and a relaunch.
if (state.isQuitting) {
return
}
await engageGpuFallbackAfterCrashBurst(
{ reason, exitCode, crashesInWindow: result.crashesInWindow, engagedAt: Date.now() },
{
@@ -16,6 +16,7 @@ import {
import {
describeInstallDirAclPoison,
isBlockingInstallDirAclRepairInFlight,
isInstallDirAclRepairExhausted,
isInstallDirAclSuspect,
noteWindowsInstallDirAclProbePending,
repairKnownPoisonedInstallDirBeforeWindow,
@@ -475,39 +476,115 @@ describe('repairKnownPoisonedInstallDirBeforeWindow', () => {
})
})
// hasMarkerFor also matches a marker written by a SUCCESSFUL repair, so 'marker-hit'
// on its own cannot tell a finished tree from one Orca gave up on.
describe('a marker-hit on a tree a previous launch already repaired', () => {
// The repair marker matches whatever the outcome, so on its own 'marker-hit' cannot tell a
// finished tree from one Orca gave up on. Both callers hold outstanding poison evidence —
// this launch's probe reading, or the persisted marker that armed the gate — so a recorded
// success never stands in for the repair, and 'marker-hit' only ever means budget spent.
describe('a repair marker recording a completed repair', () => {
beforeEach(() => {
resetWindowsInstallDirAclProbeForTest()
resetWindowsInstallDirAclRepairForTest()
resetWindowsInstallDirAclRecoveryForTest()
})
it('reads as repaired, not as a repair Orca could not do', async () => {
/** One launch: fresh module latches, then the gate runs against the userData on disk. */
async function gateLaunch(
userDataPath: string,
run: Runner
): Promise<Awaited<ReturnType<typeof repairKnownPoisonedInstallDirBeforeWindow>>> {
resetWindowsInstallDirAclProbeForTest()
resetWindowsInstallDirAclRepairForTest()
resetWindowsInstallDirAclRecoveryForTest()
return repairKnownPoisonedInstallDirBeforeWindow(recoveryOptions(userDataPath, run))
}
// The three-launch shape the gate exists for, and the one it used to disarm itself on:
// launch 1 repairs; the tree is re-poisoned (an installer, AV, or an icacls run that
// silently no-opped); launch 2's probe records the poison but Chromium FATALs before the
// repair can write its marker. Launch 3's gate then meets a poison marker and a repair
// marker claiming success. Treating that as 'repaired' ran no icacls, deleted the poison
// marker so no later gate ever fires again, un-suspected the tree so --in-process-gpu
// could engage, and told the user their permissions were fixed.
it('re-runs icacls when a poison marker outlives it', async () => {
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-acl-repaired-hit-'))
// Launch 1: the gate repairs the tree and retires the poison marker.
writeInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)
expect(await gateLaunch(userDataPath, okRun)).toBe('repaired')
expect(hasInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)).toBe(false)
// Launch 2: the probe reads the tree as poisoned again; the process dies mid-repair,
// so the repair marker still records launch 1's success.
resetWindowsInstallDirAclRecoveryForTest()
resetWindowsInstallDirAclRepairForTest()
startWindowsInstallDirAclRepairIfPoisoned(
POISON_VERDICT,
recoveryOptions(userDataPath, (() => new Promise<never>(() => undefined)) as Runner)
)
expect(hasInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)).toBe(true)
// Launch 3: the gate must repair, not congratulate itself on launch 1's work.
const spent: ProcessSpec[] = []
const mode = await gateLaunch(userDataPath, async (spec) => {
spent.push(spec)
return { code: 5, signal: null, stdout: '', stderr: 'Access is denied.', timedOut: false }
})
expect(mode).toBe('failed')
expect(spent.map((spec) => spec.args?.[2])).toEqual([
'*S-1-15-2-2:(OI)(CI)(RX)',
'*S-1-15-2-2:(RX)'
])
expect(isInstallDirAclSuspect()).toBe(true)
expect(describeInstallDirAclPoison()?.detail).toContain('could not repair them')
expect(hasInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)).toBe(true)
})
// The budget is what stops the retry above running forever; a spent one must still read
// as "Orca could not fix this", never as a repair it never made.
it('does not let the gate report a spent budget as a repair', async () => {
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-acl-gate-budget-'))
writeFileSync(
join(userDataPath, WINDOWS_INSTALL_DIR_ACL_REPAIR_MARKER_FILE),
JSON.stringify({
schemeVersion: WINDOWS_INSTALL_DIR_ACL_REPAIR_SCHEME_VERSION,
installDir: INSTALL_DIR,
appVersion: APP_VERSION,
attemptedAt: Date.now(),
outcome: 'repaired',
attempts: 3
})
)
writeInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)
const spent: ProcessSpec[] = []
const mode = await gateLaunch(userDataPath, async (spec) => {
spent.push(spec)
return okRun(spec)
})
expect(mode).toBe('marker-hit')
expect(spent).toHaveLength(0)
expect(isInstallDirAclSuspect()).toBe(true)
expect(isInstallDirAclRepairExhausted()).toBe(true)
expect(describeInstallDirAclPoison()?.detail).toContain('could not repair them')
// Still armed: nothing has proven this tree healthy, so a later launch still gates.
expect(hasInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)).toBe(true)
})
// The probe reads the tree AFTER the pre-window gate has finished with it, so a signature
// still matching means the repair never landed however icacls exited.
it('is overruled by a probe that reads the tree poisoned after the gate repaired it', async () => {
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-acl-noop-icacls-'))
writeInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)
expect(
await repairKnownPoisonedInstallDirBeforeWindow(recoveryOptions(userDataPath, okRun))
).toBe('repaired')
// The state a launch killed between the repair and the marker clear leaves behind.
writeInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)
resetWindowsInstallDirAclRecoveryForTest()
resetWindowsInstallDirAclRepairForTest()
const spent: ProcessSpec[] = []
expect(
await repairKnownPoisonedInstallDirBeforeWindow(
recoveryOptions(userDataPath, async (spec) => {
spent.push(spec)
return okRun(spec)
})
)
).toBe('marker-hit')
expect(spent).toHaveLength(0)
expect(isInstallDirAclSuspect()).toBe(false)
expect(describeInstallDirAclPoison()?.detail).toContain('Orca repaired the permissions')
expect(hasInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)).toBe(false)
startWindowsInstallDirAclRepairIfPoisoned(POISON_VERDICT, recoveryOptions(userDataPath, okRun))
expect(isInstallDirAclSuspect()).toBe(true)
expect(isInstallDirAclRepairExhausted()).toBe(false)
expect(describeInstallDirAclPoison()?.detail).toContain('could not repair them')
// Re-armed: the next launch gates before it opens a window it cannot render.
expect(hasInstallDirAclPoisonMarker(userDataPath, INSTALL_DIR, APP_VERSION)).toBe(true)
})
// The opposite evidence: the probe has just READ this tree and found it poisoned, so a
@@ -115,6 +115,16 @@ export function isInstallDirAclRepairPending(): boolean {
return poison?.stage === 'pending'
}
/**
* True once the repair has nothing left to try for this install and version: `marker-hit`
* is reachable only through the spent attempt budget. The suspicion itself stands — the
* dialog still names the cause and the admin commands — but a caller that was *withholding*
* a recovery to give the repair first go has nothing left to wait for.
*/
export function isInstallDirAclRepairExhausted(): boolean {
return poison?.stage === 'marker-hit'
}
/** True while the pre-window gate is rewriting the very files a new renderer would load. */
export function isBlockingInstallDirAclRepairInFlight(): boolean {
return blockingRepairInFlight
@@ -124,31 +134,24 @@ export function isBlockingInstallDirAclRepairInFlight(): boolean {
function startRepair(
installDir: string,
options: WindowsInstallDirAclRecoveryOptions,
{
probeConfirmedPoisoned = false,
onDone
}: {
probeConfirmedPoisoned?: boolean
onDone?: (result: WindowsInstallDirAclRepairResult) => void
} = {}
onDone?: (result: WindowsInstallDirAclRepairResult) => void
): boolean {
writeInstallDirAclPoisonMarker(options.userDataPath, installDir, options.appVersion)
const started = repairWindowsInstallDirPackageAcl({
...options,
installDir,
probeConfirmedPoisoned,
// Every caller here holds outstanding poison evidence — this launch's probe reading, or
// the persisted marker that armed the gate — so a marker recording a completed repair
// describes a re-poisoned tree, or an icacls run that silently no-opped. It must not
// stand in for a repair. `marker-hit` therefore only ever means the budget is spent.
poisonEvidenceOutstanding: true,
onDone: (result) => {
// A marker recording a completed repair is not a failure to repair: this tree is done.
// `probeConfirmedPoisoned` keeps it off a tree the probe just read as poisoned:
// there the marker stops claiming `alreadyRepaired`, so icacls runs again.
const stage: RepairStage =
result.mode === 'marker-hit' && result.alreadyRepaired ? 'repaired' : result.mode
// A clean reading of the tree outranks this: there was nothing left to repair.
if (!installDirReadClean) {
poison = { installDir, stage }
poison = { installDir, stage: result.mode }
}
logStartupMilestone('install-dir-acl-repair-done', { mode: result.mode })
if (stage === 'repaired') {
if (result.mode === 'repaired') {
clearInstallDirAclPoisonMarker(options.userDataPath)
// The GPU child deaths were never a driver fault, so safe graphics — and the
// --in-process-gpu launch that hides the next crash's evidence — must not outlive the repair.
@@ -200,14 +203,19 @@ function applyInstallDirAclProbeVerdict(
}
return
}
// The blocking pre-window gate may already own this launch's repair; restarting it
// would reset the verdict to 'pending' against a repair that can no longer report.
if (poison) {
// The blocking pre-window gate still owns this launch's repair; restarting it would
// reset the verdict to 'pending' against a repair that can no longer report.
if (poison?.stage === 'pending') {
return
}
startRepair(options.installDir ?? dirname(process.execPath), options, {
probeConfirmedPoisoned: true
})
// This reading was taken after the gate finished, so it outranks the gate's own verdict:
// a tree that still matches the signature was never repaired, whatever icacls exited.
if (poison?.stage === 'repaired') {
poison = { installDir: poison.installDir, stage: 'failed' }
}
// Re-writes the poison marker — re-arming the next launch's gate — even when the
// once-per-process latch means no icacls can run again this launch.
startRepair(options.installDir ?? dirname(process.execPath), options)
}
/**
@@ -238,13 +246,11 @@ export async function repairKnownPoisonedInstallDirBeforeWindow(
options.timeoutMs ?? BLOCKING_REPAIR_BUDGET_MS
)
timer.unref?.()
// No `probeConfirmedPoisoned`: the gate acts on a marker from an earlier launch,
// not on a DACL reading of its own, so a recorded repair still outranks it.
const started = startRepair(installDir, options, {
onDone: (result) => {
clearTimeout(timer)
resolve(result.mode)
}
// The marker is an earlier launch's DACL reading that nothing has retired, so a
// repair marker claiming success cannot stand in for the repair this launch owes.
const started = startRepair(installDir, options, (result) => {
clearTimeout(timer)
resolve(result.mode)
})
// No dispatch means no `onDone`, so waiting out the whole budget would buy nothing.
if (!started) {
@@ -64,11 +64,13 @@ export type WindowsInstallDirAclRepairOptions = {
platform?: NodeJS.Platform
isServeMode?: boolean
/**
* The DACL was read as poisoned just now, so a marker claiming a completed repair
* describes a tree that has since been re-poisoned — or an icacls run that silently
* no-opped. It stops outranking the evidence; the attempt budget still bounds retries.
* A DACL reading found this tree poisoned and nothing has read it clean since — this
* launch's probe, or a persisted poison marker from an earlier one. A marker claiming a
* completed repair therefore describes a tree that has since been re-poisoned, or an
* icacls run that silently no-opped: it stops outranking the reading. The attempt
* budget still bounds retries.
*/
probeConfirmedPoisoned?: boolean
poisonEvidenceOutstanding?: boolean
/** Test seams. */
runProcessFn?: typeof runProcess
recordBreadcrumb?: typeof recordDurableCrashBreadcrumb
@@ -146,7 +148,7 @@ function markerHitFor(args: WindowsInstallDirAclRepairArgs): { alreadyRepaired:
if (!marker) {
return null
}
if (marker.outcome === 'repaired' && args.probeConfirmedPoisoned !== true) {
if (marker.outcome === 'repaired' && args.poisonEvidenceOutstanding !== true) {
return { alreadyRepaired: true }
}
return (marker.attempts ?? 0) >= MAX_REPAIR_ATTEMPTS ? { alreadyRepaired: false } : null