Pool host-approved restricted code with scoped and fair graph execution

This commit is contained in:
Timothy J. Aveni
2026-10-07 16:47:19 -07:00
parent 929fdb4a28
commit d6e66b537f
2 changed files with 91 additions and 12 deletions
+65 -11
View File
@@ -14,6 +14,46 @@ const absolute = (value) =>
value.startsWith("/") && value.startsWith("/") &&
!/[\s\u0000-\u001f]/.test(value) && !/[\s\u0000-\u001f]/.test(value) &&
!value.split("/").includes(".."); !value.split("/").includes("..");
/** Host-wide engine reservations. Only trusted adapters hold these callbacks;
* workers cannot alter admission or evict another invocation's engine. */
export class RestrictedEngineBudget {
#leases = new Set();
constructor(limit = 8, reserve = async () => ({})) {
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 64) throw fixedError("INVALID_ENGINE_BUDGET");
this.limit = limit;
this.reserve = reserve;
}
async acquire(idle, evict) {
while (this.#leases.size >= this.limit) {
const victim = [...this.#leases].find((entry) => entry.idle());
if (!victim) throw fixedError("RESTRICTED_ENGINE_LIMIT");
await victim.evict();
}
const lease = { idle, evict };
this.#leases.add(lease);
try {
const resource = await this.reserve();
let releasing;
const release = () =>
(releasing ??= (async () => {
try {
await resource.release?.();
} finally {
this.#leases.delete(lease);
}
})());
release.attach = resource.attach;
return release;
} catch (error) {
this.#leases.delete(lease);
throw error;
}
}
stats() {
return { engines: this.#leases.size, limit: this.limit };
}
}
async function boundedSource(path, maximum, code) { async function boundedSource(path, maximum, code) {
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK); const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
try { try {
@@ -101,12 +141,14 @@ export async function serveRestrictedRegistry(config, io = process) {
const sdk = []; const sdk = [];
for (const m of config.sdk) for (const m of config.sdk)
sdk.push({ ...m, source: await boundedSource(m.path, 2 * 1024 * 1024, "RESTRICTED_SDK_LIMIT") }); sdk.push({ ...m, source: await boundedSource(m.path, 2 * 1024 * 1024, "RESTRICTED_SDK_LIMIT") });
const engineBudget = config.engineBudget ?? new RestrictedEngineBudget(limits.maxActive + limits.maxNested);
const active = new Map(), const active = new Map(),
engines = new Set(), engines = new Set(),
idleEngines = []; idleEngines = [];
const metrics = { starts: 0, reused: 0, completed: 0, peakRSSBytes: 0 }; const metrics = { starts: 0, reused: 0, completed: 0, peakRSSBytes: 0 };
let buffered = Buffer.alloc(0), let buffered = Buffer.alloc(0),
stopped = false; stopped = false,
failure;
const queued = []; const queued = [];
const send = (value) => { const send = (value) => {
if (stopped) return; if (stopped) return;
@@ -191,15 +233,17 @@ export async function serveRestrictedRegistry(config, io = process) {
})(); })();
return call.retiring; return call.retiring;
}; };
const fail = async (error) => { const fail = (error) => {
if (stopped) return; if (failure) return failure;
stopped = true; stopped = true;
io.stdin.destroy(); return (failure = (async () => {
for (const call of [...queued]) await retire(call, error); io.stdin.destroy();
await Promise.all([...active.values()].map((call) => retire(call, error))); for (const call of [...queued]) await retire(call, error);
await Promise.all([...engines].map(destroyEngine)); await Promise.all([...active.values()].map((call) => retire(call, error)));
io.stdout.end(); await Promise.all([...engines].map(destroyEngine));
io.exitCode = 1; io.stdout.end();
io.exitCode = 1;
})());
}; };
const body = async (stream) => { const body = async (stream) => {
const chunks = []; const chunks = [];
@@ -228,8 +272,12 @@ export async function serveRestrictedRegistry(config, io = process) {
} }
engine.server?.closeAllConnections(); engine.server?.closeAllConnections();
if (engine.server) await new Promise((resolve) => engine.server.close(resolve)); if (engine.server) await new Promise((resolve) => engine.server.close(resolve));
if (engine.directory) await rm(engine.directory, { recursive: true, force: true }); try {
engines.delete(engine); if (engine.directory) await rm(engine.directory, { recursive: true, force: true });
} finally {
engines.delete(engine);
await engine.release?.();
}
})(); })();
return engine.destroying; return engine.destroying;
}; };
@@ -248,6 +296,11 @@ export async function serveRestrictedRegistry(config, io = process) {
const engine = { current: call, cpuTotal: 0 }; const engine = { current: call, cpuTotal: 0 };
engines.add(engine); engines.add(engine);
call.engine = engine; call.engine = engine;
engine.release = await engineBudget.acquire(
() => !engine.current && !engine.destroying,
() => destroyEngine(engine),
);
if (!call.open) return;
engine.directory = await mkdtemp(join(tmpdir(), "qx-engine-")); engine.directory = await mkdtemp(join(tmpdir(), "qx-engine-"));
if (!call.open) return; if (!call.open) return;
const broker = join(engine.directory, "broker.sock"); const broker = join(engine.directory, "broker.sock");
@@ -339,6 +392,7 @@ export async function serveRestrictedRegistry(config, io = process) {
engine.child.once("error", resolve); engine.child.once("error", resolve);
}); });
call.child = engine.child; call.child = engine.child;
await engine.release.attach?.(engine.child.pid);
watchCPU(call); watchCPU(call);
let diagnostics = 0; let diagnostics = 0;
engine.child.stderr.on("data", (chunk) => { engine.child.stderr.on("data", (chunk) => {
+26 -1
View File
@@ -5,7 +5,7 @@ import { mkdtemp, writeFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { serveRestrictedRegistry } from "./service.mjs"; import { serveRestrictedRegistry, RestrictedEngineBudget } from "./service.mjs";
const workerd = process.env.QUIXOS_TEST_WORKERD; const workerd = process.env.QUIXOS_TEST_WORKERD;
if (!workerd) throw Error("Set QUIXOS_TEST_WORKERD to the pinned engine"); if (!workerd) throw Error("Set QUIXOS_TEST_WORKERD to the pinned engine");
@@ -571,3 +571,28 @@ test("detached digest callbacks cannot use a completed frame or contaminate reus
await f.close(); await f.close();
} }
}); });
test("the shared engine budget evicts idle code families and rejects busy oversubscription", async () => {
const engineBudget = new RestrictedEngineBudget(1);
const a = await fixture(registry("return request.input;"), { engineBudget });
const b = await fixture(
registry("return await channel({portId:'read',action:{kind:'state',primitive:'read'},input:null});"),
{ engineBudget },
);
try {
a.start("first family");
assert.equal((await a.receive()).output, "first family");
const busy = b.start();
const port = await b.receive();
assert.equal(a.stats().engines, 0, "Idle foreign code is evicted before allocating another engine");
assert.equal(engineBudget.stats().engines, 1);
a.start("overload");
assert.equal((await a.receive()).error.code, "RESTRICTED_ENGINE_LIMIT");
assert.equal(engineBudget.stats().engines, 1);
b.send({ kind: "port-result", ...busy, sequence: port.sequence, status: "returned", output: "survived" });
assert.equal((await b.receive()).output, "survived");
} finally {
await Promise.all([a.close(), b.close()]);
}
assert.equal(engineBudget.stats().engines, 0);
});