Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,10 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
const resolveServiceHomes = deps.resolveServiceHomes ?? currentServiceHomes;
let startupOwnershipHomes: ReturnType<typeof currentServiceHomes> | null = null;
let startupOwnershipStatePaths: readonly string[] | null = null;
// #2923: both synchronous startup ownership decisions keep their fresh,
// race-sensitive targeted task query. Only the expensive fallback listing is
// shared, and only while that targeted result stays byte-for-byte unchanged.
// Runtime ownership retries below intentionally omit this startup-local memo.
// #2923: retain a successful fallback listing only within the first startup
// ownership decision. A targeted query's bytes are not a Task Scheduler state
// generation, so the later race-sensitive decision must take a fresh listing.
// Runtime ownership retries below intentionally omit this startup-local memo too.
const startupWindowsTaskListingCache = createWindowsTaskListingCache();
try {
const homes = resolveServiceHomes();
Expand Down Expand Up @@ -554,7 +554,6 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
deps,
startupOwnershipHomes,
startupOwnershipStatePaths,
startupWindowsTaskListingCache,
);
const preparedNativeMainLifecycle = nativeOwnership.ownership !== "foreign"
&& startupOwnershipHomes !== null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ describe("Windows ownership probe hardening regressions", () => {
expect(result.kind).toBe("unknown");
});

test("one startup keeps two targeted queries but shares one unchanged full listing (#2923)", async () => {
test("a startup refreshes the full listing when a task appears after the second targeted snapshot", async () => {
const codexHome = join(home, "codex");
mkdirSync(codexHome, { recursive: true });
process.env.CODEX_HOME = codexHome;
Expand All @@ -228,15 +228,22 @@ describe("Windows ownership probe hardening regressions", () => {

let targetedQueries = 0;
let fullListings = 0;
let taskRegistered = false;
const runRaw: RawProbeRunner = (file, args) => {
if (!file.toLowerCase().endsWith("schtasks.exe")) return raw(1, "", "unexpected executable");
if (args.includes("/xml")) {
targetedQueries += 1;
// Model a localized targeted query that took its absent snapshot before
// a concurrent installer committed the task, then returned unchanged
// opaque bytes. Only the following fresh listing can observe the task.
if (targetedQueries === 2) taskRegistered = true;
return { status: 1, stdout: Buffer.alloc(0), stderr: GBK_TASK_NOT_FOUND, timedOut: false, spawnFailed: false };
}
if (args.includes("/fo")) {
fullListings += 1;
return raw(0, '"\\SomeOtherTask","N/A","Ready"\r\n');
return raw(0, taskRegistered
? '"\\opencodex-proxy","N/A","Ready"\r\n'
: '"\\SomeOtherTask","N/A","Ready"\r\n');
}
return raw(1, "", "unexpected query");
};
Expand All @@ -262,9 +269,9 @@ describe("Windows ownership probe hardening regressions", () => {
},
});
try {
expect(ownerships.slice(0, 2)).toEqual(["owned", "owned"]);
expect(ownerships.slice(0, 2)).toEqual(["owned", "unknown"]);
expect(targetedQueries).toBe(2);
expect(fullListings).toBe(1);
expect(fullListings).toBe(2);
} finally {
await server.stop(true);
}
Expand Down
Loading