Files
bare-operating-system/packages/bare-os-booter/lib/bare-os-wasm-kernel.js
T
Raven Scott ae6f1cf8ac Implement the 20-task Bare OS POSIX + P2P roadmap: holepunch lockfile drift
reporting and audit:holepunch-clones docs; placeholder baseline automation;
maintainer kernel-image sync script; protomux schema coupling in protocol
tests; disk.os RPC hints (replication_operator_sketch, hyperblobs/blind v3);
union/mirror VFS + warm-cache selective eviction tests; extension resolver
coverage; curl/wget fall through PATH when BARE_OS_DELEGATE_ALLOW excludes
delegates (kernel-runner) with handbook/ctx docs; socket contract / Wasm /
boot budget strict path / hrpc allowlist tests; subprocess bridge meta;
shell until gate + tar stat metadata; POSIX profile triplet pretest verifier;
regenerate kernel bundle, seeder parity, and audit artifacts as needed.

Covers KERNEL_CONTRACT, POSIX_DECLARED_PROFILE, handbook, developer-guide,
scripts/README, and related reference docs.
2026-04-05 02:21:07 -04:00

216 lines
6.5 KiB
JavaScript

/**
* Optional WebAssembly compile / instantiate for feature-gated kernel extensions.
* Uses global WebAssembly when present (Bare/V8); no Node built-ins.
*/
const DEFAULT_MAX = 512 * 1024
/**
* @param {Uint8Array} source
* @param {{ maxBytes?: number }} [opts]
*/
export async function bareOsWasmKernelCompile(source, opts = {}) {
const maxB = Math.min(
DEFAULT_MAX,
Math.max(1024, Number(opts.maxBytes) || DEFAULT_MAX)
)
if (!(source instanceof Uint8Array) || source.byteLength === 0) {
return { ok: false, reason: 'invalid_source' }
}
if (source.byteLength > maxB) {
return { ok: false, reason: 'source_too_large' }
}
const W = globalThis.WebAssembly
if (!W || typeof W.compile !== 'function') {
return { ok: false, reason: 'webassembly_unavailable' }
}
try {
const mod = await W.compile(source)
/** @type {{ name: string }[]} */
let names = []
if (typeof W.Module.exports === 'function') {
names = W.Module.exports(mod).map((e) => String(e.name || ''))
}
return { ok: true, exportNames: names }
} catch (e) {
return { ok: false, reason: String(e && e.message ? e.message : e) }
}
}
/**
* Read bounded UTF-8 from Wasm linear memory (host view).
* @param {WebAssembly.Memory} memory
* @param {number} ptr
* @param {number} len
* @param {number} [maxLen]
*/
function readUtf8FromMemory(memory, ptr, len, maxLen = 4096) {
const buf = memory.buffer
const n = Math.min(len >>> 0, maxLen)
const p = ptr >>> 0
if (p + n > buf.byteLength) return ''
return new TextDecoder().decode(new Uint8Array(buf, p, n))
}
/**
* Write NUL-terminated UTF-8 into Wasm memory; returns bytes written (excl. NUL) or -1.
* @param {WebAssembly.Memory} memory
* @param {number} ptr
* @param {number} outCap
* @param {string} text
*/
function writeCStrToMemory(memory, ptr, outCap, text) {
const enc = new TextEncoder().encode(String(text || ''))
const cap = Math.max(0, outCap >>> 0)
if (cap < 2) return -1
const max = cap - 1
const n = Math.min(enc.length, max)
const p = ptr >>> 0
const buf = memory.buffer
if (p + cap > buf.byteLength) return -1
const u8 = new Uint8Array(buf, p, cap)
u8.set(enc.subarray(0, n))
u8[n] = 0
return n
}
/**
* Bounded `instantiate` with an isolated `Memory` and minimal `env` imports.
* Optional sync imports **`env.bare_os_pathconf`** / **`env.bare_os_umask_get`** when
* **`BARE_OS_WASM_KERNEL_SYSCALL=1`** (or **`opts.wasmSyscallImports: true`**) and **`ctx.bareOsPathconf`** exists.
* Also **`env.bare_os_wall_time_ms32`**: low 32 bits of wall time (`Date.now()`, signed i32 wrap) for bounded guest timing.
*
* @param {Uint8Array} source
* @param {Record<string, unknown>} ctx
* @param {{
* maxBytes?: number,
* memoryInitialPages?: number,
* memoryMaxPages?: number,
* timeoutMs?: number,
* wasmSyscallImports?: boolean,
* shellEnv?: Record<string, string | undefined>
* }} [opts]
*/
export async function bareOsWasmKernelInstantiate(source, ctx, opts = {}) {
const maxB = Math.min(
DEFAULT_MAX,
Math.max(1024, Number(opts.maxBytes) || DEFAULT_MAX)
)
if (!(source instanceof Uint8Array) || source.byteLength === 0) {
return { ok: false, reason: 'invalid_source' }
}
if (source.byteLength > maxB) {
return { ok: false, reason: 'source_too_large' }
}
const W = globalThis.WebAssembly
if (!W || typeof W.instantiate !== 'function' || typeof W.Memory !== 'function') {
return { ok: false, reason: 'webassembly_unavailable' }
}
const memInitial = Math.min(
256,
Math.max(1, Number(opts.memoryInitialPages) || 1)
)
const memMax = Math.min(
256,
Math.max(memInitial, Number(opts.memoryMaxPages) || 8)
)
const timeoutMs = Math.min(
60000,
Math.max(50, Number(opts.timeoutMs) || 5000)
)
try {
const mod = await W.compile(source)
/** @type {{ name: string }[]} */
let exportNames = []
if (typeof W.Module.exports === 'function') {
exportNames = W.Module.exports(mod).map((e) => String(e.name || ''))
}
const memory = new W.Memory({ initial: memInitial, maximum: memMax })
const shellEnv =
opts.shellEnv && typeof opts.shellEnv === 'object'
? /** @type {Record<string, string | undefined>} */ (opts.shellEnv)
: {}
const wantSyscall =
opts.wasmSyscallImports === true ||
shellEnv.BARE_OS_WASM_KERNEL_SYSCALL === '1' ||
shellEnv.BARE_OS_WASM_KERNEL_SYSCALL === 'true'
const pathconfFn = ctx && typeof ctx.bareOsPathconf === 'function'
? ctx.bareOsPathconf.bind(ctx)
: null
/** @type {Record<string, unknown>} */
const envImports = {
memory,
bare_os_nop: () => 0
}
if (wantSyscall) {
envImports.bare_os_wall_time_ms32 = () => Date.now() | 0
}
if (wantSyscall && pathconfFn) {
envImports.bare_os_pathconf = (
pathPtr,
pathLen,
namePtr,
nameLen,
outPtr,
outCap
) => {
try {
const path = readUtf8FromMemory(
memory,
Number(pathPtr),
Number(pathLen)
).trim()
const name = readUtf8FromMemory(
memory,
Number(namePtr),
Number(nameLen)
).trim()
if (!path.startsWith('/') || !name) return -1
const v = pathconfFn(path, name)
if (v == null || v === undefined) return -2
return writeCStrToMemory(
memory,
Number(outPtr),
Number(outCap),
String(v)
)
} catch {
return -3
}
}
envImports.bare_os_umask_get = () => {
const m = parseInt(String(shellEnv.UMASK || '022'), 8)
return Number.isFinite(m) ? m & 0o777 : 0o22
}
}
const importObject = {
env: envImports
}
const instP = W.instantiate(mod, importObject)
const done = await Promise.race([
instP,
new Promise((_, rej) =>
setTimeout(
() => rej(new Error('wasm_instantiate_timeout')),
timeoutMs
)
)
])
const instance = /** @type {{ instance: WebAssembly.Instance }} */ (done)
.instance
return {
ok: true,
exportNames,
instance,
memory,
memoryInitialPages: memInitial,
memoryMaxPages: memMax,
wasmSyscallImports: !!wantSyscall,
wasmPathconfImport: !!(wantSyscall && pathconfFn),
wasmWallClockMs32Import: !!wantSyscall
}
} catch (e) {
return { ok: false, reason: String(e && e.message ? e.message : e) }
}
}