Skip to content
1 change: 1 addition & 0 deletions scripts/test-layout/layout-codex-collision.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"codex-cli-windows-child.test.ts": "codex-integration",
"codex-cli-windows-plan.test.ts": "codex-integration",
"codex-cli-update-artifacts.test.ts": "codex-integration",
"codex-cli-update-apply.test.ts": "codex-integration",
Expand Down
113 changes: 113 additions & 0 deletions src/codex/windows-cli-child-native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { dlopen, ptr, read, type Library, type Symbols } from "bun:ffi";

// Windows x64 ABI only. No native binary, compiler, worker URL, or shell dependency.
export const WIN64_CHILD_ABI = Object.freeze({ securityAttributes: 24, securityDescriptor: 8, inheritHandle: 16,
startupInfo: 104, startupInfoEx: 112, startupFlags: 60, stdin: 80, stdout: 88, stderr: 96, attributes: 104,
processInformation: 24, process: 0, thread: 8, pid: 16, tid: 20,
overlapped: 32, event: 24, jobAccounting: 48, activeProcesses: 40 });
const KERNEL = {
GetLastError: { args: [], returns: "u32" },
GetCurrentProcess: { args: [], returns: "u64" },
CompareStringOrdinal: { args: ["ptr", "i32", "ptr", "i32", "i32"], returns: "i32" },
CreateJobObjectW: { args: ["ptr", "ptr"], returns: "u64" },
AssignProcessToJobObject: { args: ["u64", "u64"], returns: "i32" },
QueryInformationJobObject: { args: ["u64", "i32", "ptr", "u32", "ptr"], returns: "i32" },
TerminateJobObject: { args: ["u64", "u32"], returns: "i32" },
TerminateProcess: { args: ["u64", "u32"], returns: "i32" },
CreateProcessW: { args: ["ptr", "ptr", "ptr", "ptr", "i32", "u32", "ptr", "ptr", "ptr", "ptr"], returns: "i32" },
ResumeThread: { args: ["u64"], returns: "u32" },
WaitForSingleObject: { args: ["u64", "u32"], returns: "u32" },
GetExitCodeProcess: { args: ["u64", "ptr"], returns: "i32" },
GetProcessTimes: { args: ["u64", "ptr", "ptr", "ptr", "ptr"], returns: "i32" },
InitializeProcThreadAttributeList: { args: ["ptr", "u32", "u32", "ptr"], returns: "i32" },
UpdateProcThreadAttribute: { args: ["ptr", "u32", "u64", "ptr", "u64", "ptr", "ptr"], returns: "i32" },
DeleteProcThreadAttributeList: { args: ["ptr"], returns: "void" },
CreateNamedPipeW: { args: ["ptr", "u32", "u32", "u32", "u32", "u32", "u32", "ptr"], returns: "u64" },
ConnectNamedPipe: { args: ["u64", "ptr"], returns: "i32" },
CreateFileW: { args: ["ptr", "u32", "u32", "ptr", "u32", "u32", "u64"], returns: "u64" },
CreateEventW: { args: ["ptr", "i32", "i32", "ptr"], returns: "u64" },
ResetEvent: { args: ["u64"], returns: "i32" },
ReadFile: { args: ["u64", "ptr", "u32", "ptr", "ptr"], returns: "i32" },
GetOverlappedResult: { args: ["u64", "ptr", "ptr", "i32"], returns: "i32" },
CancelIoEx: { args: ["u64", "ptr"], returns: "i32" },
CloseHandle: { args: ["u64"], returns: "i32" },
LocalFree: { args: ["ptr"], returns: "ptr" },
} as const satisfies Symbols;
const ADVAPI = {
OpenProcessToken: { args: ["u64", "u32", "ptr"], returns: "i32" },
GetTokenInformation: { args: ["u64", "i32", "ptr", "u32", "ptr"], returns: "i32" },
ConvertSidToStringSidW: { args: ["ptr", "ptr"], returns: "i32" },
ConvertStringSecurityDescriptorToSecurityDescriptorW: { args: ["ptr", "u32", "ptr", "ptr"], returns: "i32" },
} as const satisfies Symbols;

export const wide = (value: string): Buffer => Buffer.from(`${value}\0`, "utf16le");
export function securityAttributes(inherit: boolean, descriptor = 0n): Buffer {
const bytes = Buffer.alloc(WIN64_CHILD_ABI.securityAttributes);
bytes.writeUInt32LE(bytes.length, 0); bytes.writeBigUInt64LE(descriptor, 8); bytes.writeUInt32LE(+inherit, 16);
return bytes;
}

/** Owns only handles returned by this invocation, never PID-reopened handles. */
export class WindowsChildNative {
readonly k: Library<typeof KERNEL>["symbols"];
readonly a: Library<typeof ADVAPI>["symbols"];
readonly handles = new Set<bigint>();
/** Explicit GC roots for pointers stored inside native attribute lists. */
readonly borrowedMemory = new Set<Buffer>();
readonly closeErrors: bigint[] = [];
private readonly kernel: Library<typeof KERNEL>;
private readonly advapi: Library<typeof ADVAPI>;
private closed = false;
constructor() {
if (process.platform !== "win32" || process.arch !== "x64") throw new Error("windows_child_unsupported_platform");
this.kernel = dlopen("kernel32.dll", KERNEL);
try { this.advapi = dlopen("advapi32.dll", ADVAPI); }
catch (error) { this.kernel.close(); throw error; }
// Own mutable dispatch tables permit per-instance fault injection in native fixtures.
this.k = { ...this.kernel.symbols }; this.a = { ...this.advapi.symbols };
}
keep(value: number | bigint): bigint {
const handle = BigInt(value);
if (!handle || handle === 0xffffffffffffffffn) throw new Error(`windows_child_handle_${this.k.GetLastError!()}`);
this.handles.add(handle); return handle;
}
close(handle: bigint): void {
if (!this.handles.has(handle)) return;
if (!this.k.CloseHandle!(handle)) { this.closeErrors.push(handle); return; }
this.handles.delete(handle);
}
dispose(): boolean {
if (this.handles.size || this.borrowedMemory.size) return false;
if (!this.closed) { this.closed = true; this.advapi.close(); this.kernel.close(); }
return true;
}
/** Protected DACL grants only the current token user. No default/world pipe ACL. */
privateDescriptor(): bigint {
const output = Buffer.alloc(8), size = Buffer.alloc(4);
if (!this.a.OpenProcessToken!(this.k.GetCurrentProcess!(), 8, ptr(output))) throw new Error("windows_child_token");
const token = this.keep(output.readBigUInt64LE());
let sidText = 0n;
try {
this.a.GetTokenInformation!(token, 1, null, 0, ptr(size));
const length = size.readUInt32LE();
if (length < 8 || length > 65536) throw new Error("windows_child_token_size");
const user = Buffer.alloc(length);
if (!this.a.GetTokenInformation!(token, 1, ptr(user), length, ptr(size))) throw new Error("windows_child_token_user");
const sid = user.readBigUInt64LE();
if (!this.a.ConvertSidToStringSidW!(sid, ptr(output))) throw new Error("windows_child_token_sid");
sidText = output.readBigUInt64LE();
let sidString = "";
for (let index = 0; index < 184; index++) {
const code = read.u16(sidText, index * 2);
if (!code) break;
sidString += String.fromCharCode(code);
}
if (!/^S-1-(?:\d+-)+\d+$/.test(sidString)) throw new Error("windows_child_sid_shape");
const sddl = wide(`D:P(A;;GA;;;${sidString})`);
if (!this.a.ConvertStringSecurityDescriptorToSecurityDescriptorW!(ptr(sddl), 1, ptr(output), null)) {
throw new Error("windows_child_pipe_acl");
}
return output.readBigUInt64LE();
} finally { if (sidText) this.k.LocalFree!(sidText); this.close(token); }
}
}
87 changes: 87 additions & 0 deletions src/codex/windows-cli-child-pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { randomUUID } from "node:crypto";
import { ptr } from "bun:ffi";
import { securityAttributes, wide, WIN64_CHILD_ABI, WindowsChildNative } from "./windows-cli-child-native";

/** One overlapped operation and one retained buffer per pipe. Never PeekNamedPipe. */
export class WindowsChildPipe {
readonly overlapped = Buffer.alloc(WIN64_CHILD_ABI.overlapped);
readonly buffer = Buffer.alloc(64 * 1024);
readonly count = Buffer.alloc(4);
handle = 0n;
child = 0n;
event = 0n;
pending = false;
eof = false;
error = false;
private connecting = false;
private disposed = false;
constructor(readonly native: WindowsChildNative) {}
open(): void {
const n = this.native, k = n.k;
const descriptor = n.privateDescriptor();
try {
const sa = securityAttributes(false, descriptor), childSa = securityAttributes(true, descriptor);
const name = wide(`\\\\.\\pipe\\ocx-child-${randomUUID()}`);
// Inbound, OVERLAPPED, FIRST_PIPE_INSTANCE; byte stream, REJECT_REMOTE_CLIENTS.
this.handle = n.keep(k.CreateNamedPipeW!(ptr(name), 0x40080001, 8, 1, 0, this.buffer.length, 0, ptr(sa)));
this.event = n.keep(k.CreateEventW!(null, 1, 0, null));
this.overlapped.writeBigUInt64LE(this.event, 24);
// A known local client connects before ConnectNamedPipe: ERROR_PIPE_CONNECTED is success.
this.child = n.keep(k.CreateFileW!(ptr(name), 0x40000000, 0, ptr(childSa), 3, 0, 0n));
if (!k.ConnectNamedPipe!(this.handle, ptr(this.overlapped))) {
const error = Number(k.GetLastError!());
if (error === 997) { this.pending = true; this.connecting = true; }
else if (error !== 535) throw new Error(`windows_child_pipe_connect_${error}`);
}
} finally { k.LocalFree!(descriptor); }
}
closeChild(): void { if (this.child) { this.native.close(this.child); this.child = 0n; } }
/** Bounded synchronous work; pending overlapped operations always return immediately. */
pump(consume: (bytes: Buffer) => void): void {
if (this.disposed) return;
if (this.eof) return;
const k = this.native.k;
for (let i = 0; i < 4 && !this.eof; i++) {
let done: number;
if (this.pending) {
if (!k.GetOverlappedResult!(this.handle, ptr(this.overlapped), ptr(this.count), 0)) {
const error = Number(k.GetLastError!());
if (error === 996) return; // ERROR_IO_INCOMPLETE: retain buffers and operation.
if (this.overlapped.readBigUInt64LE(0) === 0x103n) { this.error = true; return; }
this.pending = false; this.finish(error); return;
}
this.pending = false;
if (this.connecting) { this.connecting = false; continue; }
done = this.count.readUInt32LE();
} else {
this.overlapped.fill(0); this.overlapped.writeBigUInt64LE(this.event, 24);
if (!k.ResetEvent!(this.event)) { this.finish(-1); return; }
if (!k.ReadFile!(this.handle, ptr(this.buffer), this.buffer.length, ptr(this.count), ptr(this.overlapped))) {
const error = Number(k.GetLastError!());
if (error === 997) { this.pending = true; return; }
this.finish(error); return;
}
done = this.count.readUInt32LE();
}
if (!done) { this.eof = true; return; }
consume(this.buffer.subarray(0, done));
}
}
private finish(error: number): void { this.eof = true; this.error = error !== 109; }
/** Cancel is only a request. Keep the OVERLAPPED, event and bytes until settlement. */
async dispose(): Promise<void> {
if (this.disposed) return;
this.closeChild();
if (this.pending) {
this.native.k.CancelIoEx!(this.handle, ptr(this.overlapped));
while (this.pending) {
if (this.native.k.GetOverlappedResult!(this.handle, ptr(this.overlapped), ptr(this.count), 0)
|| (Number(this.native.k.GetLastError!()) !== 996 && this.overlapped.readBigUInt64LE(0) !== 0x103n)) this.pending = false;
else await Bun.sleep(5);
}
}
if (this.handle) this.native.close(this.handle);
if (this.event) this.native.close(this.event);
this.disposed = true;
}
}
Loading
Loading