From fe341daf8de270d8fb3269b3ca1d17e619c6a932 Mon Sep 17 00:00:00 2001 From: Techatrix Date: Wed, 13 Aug 2025 19:44:14 +0200 Subject: [PATCH] fix assumptions about how fd_prestat_get may be called The current implementation of `fd_prestat_get` does not appears to handle the following cases properly: - fd_prestat_get is called on the same file descriptor again - fd_prestat_get is called on stdio file descriptors (fd < 3) - fd_prestat_get is called in non sequential order of file descriptors --- wasm-wasi-core/src/common/process.ts | 2 +- wasm-wasi-core/src/common/service.ts | 25 +++++++++++-------- wasm-wasi-core/src/common/test/index.ts | 2 +- .../src/common/test/wasi.worker.test.ts | 6 ++--- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/wasm-wasi-core/src/common/process.ts b/wasm-wasi-core/src/common/process.ts index 4539d041..a91956d1 100644 --- a/wasm-wasi-core/src/common/process.ts +++ b/wasm-wasi-core/src/common/process.ts @@ -242,7 +242,7 @@ export abstract class WasiProcess { this.environmentService = EnvironmentWasiService.create( this.fileDescriptors, this.programName, - this.preOpenDirectories.entries(), options + Array.from(this.preOpenDirectories.entries()), options ); this.processService = { proc_exit: async (_memory, exitCode: exitcode) => { diff --git a/wasm-wasi-core/src/common/service.ts b/wasm-wasi-core/src/common/service.ts index 66b4e34c..e7e484b8 100644 --- a/wasm-wasi-core/src/common/service.ts +++ b/wasm-wasi-core/src/common/service.ts @@ -191,11 +191,13 @@ export interface EnvironmentOptions extends Omit, options: EnvironmentOptions): EnvironmentWasiService { + export function create(fileDescriptors: FileDescriptors, programName: string, preStats: [string, DeviceDriver][], options: EnvironmentOptions): EnvironmentWasiService { const $encoder: RAL.TextEncoder = RAL().TextEncoder.create(options?.encoding); const $preStatDirnames: Map = new Map(); + fileDescriptors.switchToRunning(3 + preStats.length); + const result: EnvironmentWasiService = { args_sizes_get: (memory: ArrayBuffer, argvCount_ptr: ptr, argvBufSize_ptr: ptr): Promise => { let count = 0; @@ -262,16 +264,19 @@ export namespace EnvironmentWasiService { }, fd_prestat_get: async (memory: ArrayBuffer, fd: fd, bufPtr: ptr): Promise => { try { - const next = preStats.next(); - if (next.done === true) { - fileDescriptors.switchToRunning(fd); + if (fd < 3) { + return Errno.badf; + } + if (fd - 3 >= preStats.length) { return Errno.badf; } - const [ mountPoint, driver ] = next.value; - const fileDescriptor = await driver.fd_create_prestat_fd(fd); - fileDescriptors.add(fileDescriptor); - fileDescriptors.setRoot(driver, fileDescriptor); - $preStatDirnames.set(fileDescriptor.fd, mountPoint); + const [ mountPoint, driver ] = preStats[fd - 3]; + if (!fileDescriptors.has(fd)) { + const fileDescriptor = await driver.fd_create_prestat_fd(fd); + fileDescriptors.add(fileDescriptor); + fileDescriptors.setRoot(driver, fileDescriptor); + $preStatDirnames.set(fileDescriptor.fd, mountPoint); + } const view = new DataView(memory); const prestat = Prestat.create(view, bufPtr); prestat.preopentype = Preopentype.dir; @@ -1124,7 +1129,7 @@ export namespace FileSystemService { const clock = Clock.create(); return Object.assign( {}, - EnvironmentWasiService.create(fileDescriptors, 'virtualRootFileSystem', preOpens.entries(), {}), + EnvironmentWasiService.create(fileDescriptors, 'virtualRootFileSystem', Array.from(preOpens.entries()), {}), DeviceWasiService.create(deviceDrivers, fileDescriptors, clock, virtualRootFileSystem, options) ); } diff --git a/wasm-wasi-core/src/common/test/index.ts b/wasm-wasi-core/src/common/test/index.ts index aa4e613b..fd5c9435 100644 --- a/wasm-wasi-core/src/common/test/index.ts +++ b/wasm-wasi-core/src/common/test/index.ts @@ -91,7 +91,7 @@ export function createWasiService(workspaceContent: WorkspaceContent): WasiServi const clock = Clock.create(); const fileSystemService = DeviceWasiService.create(deviceDrivers, fileDescriptors, clock, undefined, options); - const environmentService = EnvironmentWasiService.create(fileDescriptors, 'testApp', preOpenDirectories.entries(), options); + const environmentService = EnvironmentWasiService.create(fileDescriptors, 'testApp', Array.from(preOpenDirectories.entries()), options); const clockService = ClockWasiService.create(clock); const result: WasiService = Object.assign({}, NoSysWasiService, environmentService, clockService, fileSystemService); return result; diff --git a/wasm-wasi-core/src/common/test/wasi.worker.test.ts b/wasm-wasi-core/src/common/test/wasi.worker.test.ts index bba8205c..23cc7a89 100644 --- a/wasm-wasi-core/src/common/test/wasi.worker.test.ts +++ b/wasm-wasi-core/src/common/test/wasi.worker.test.ts @@ -242,7 +242,7 @@ suite(`Simple test - ${memoryQualifier}`, () => { }); suite(`Filesystem - ${memoryQualifier}`, () => { - const rootFd: fd = 4; + const rootFd: fd = 3; test(`fd_prestat`, () => { const memory = createMemory(); const prestat = memory.allocStruct(Prestat); @@ -252,7 +252,7 @@ suite(`Filesystem - ${memoryQualifier}`, () => { errno = wasi.fd_prestat_dir_name(rootFd, buffer.$ptr, prestat.len); assert.strictEqual(errno, Errno.success); // We only have one prestat directory - errno = wasi.fd_prestat_get(5, prestat.$ptr); + errno = wasi.fd_prestat_get(rootFd + 1, prestat.$ptr); assert.strictEqual(errno, Errno.badf); }); @@ -262,7 +262,7 @@ suite(`Filesystem - ${memoryQualifier}`, () => { const path = memory.allocString('fixture/read/helloWorld.txt'); let errno = wasi.path_open(rootFd, Lookupflags.none, path.$ptr, path.byteLength, Oflags.none, FileBaseRights, FileInheritingRights, Fdflags.none, fd.$ptr); assert.strictEqual(errno, Errno.success); - assert.strictEqual(fd.value, 5); + assert.strictEqual(fd.value, rootFd + 1); errno = wasi.fd_close(fd.value); assert.strictEqual(errno, Errno.success); });