Repository: tetratelabs/wazero
Pull request #858 (2022-11-24 12:56:30)

wasi: adds path_filestat_get
This adds path_filestat_get which is needed for PDFium and zig's fstatatWasi.

https://github.com/ziglang/zig/blob/aea617c60be848ed372343e08465bb0ac6cb7b85/lib/std/os.zig#L4171

The original source of this was written by @jerbob92

codefromthecrypt: having trouble triggering this with zig so I raised https://github.com/ziglang/zig/issues/13644

I'd like to verify at least two compilers use this coherently..
codefromthecrypt: verified in zig by changing the workdir explicitly to "/". Note, this doesn't work in zig until https://github.com/ziglang/zig/pull/13646


```zig
const std = @import("std");
const allocator = std.heap.page_allocator;
const PreopenList = std.fs.wasi.PreopenList;

pub fn main() !void {
    var preopens = PreopenList.init(allocator);
    defer preopens.deinit();

    try std.os.initPreopensWasi(allocator, "/");
    try std.os.chdir("/");
}
```

```
==> wasi_snapshot_preview1.fd_prestat_get(fd=3,result.prestat=1040264)
<== ESUCCESS
==> wasi_snapshot_preview1.fd_prestat_dir_name(fd=3,path=1044384,path_len=1)
<== ESUCCESS
==> wasi_snapshot_preview1.fd_prestat_get(fd=4,result.prestat=1040264)
<== EBADF
==> wasi_snapshot_preview1.path_filestat_get(fd=3,flags=1,path=1049005,path_len=1,result.buf=1048512)
<== ESUCCESS
==> wasi_snapshot_preview1.proc_exit(rval=0)
```

Patch:
diff --git a/imports/wasi_snapshot_preview1/fs.go b/imports/wasi_snapshot_preview1/fs.go
index 17ab1bc5..5ab65cfc 100644
--- a/imports/wasi_snapshot_preview1/fs.go
+++ b/imports/wasi_snapshot_preview1/fs.go
@@ -6,6 +6,8 @@ import (
 	"errors"
 	"io"
 	"io/fs"
+	"path"
+	"syscall"
 
 	"github.com/tetratelabs/wazero/api"
 	internalsys "github.com/tetratelabs/wazero/internal/sys"
@@ -267,9 +269,10 @@ const (
 )
 
 func fdFilestatGetFn(ctx context.Context, mod api.Module, params []uint64) []uint64 {
-	fd := uint32(params[0])
-	resultBuf := uint32(params[1])
+	return fdFilestatGetFunc(ctx, mod, uint32(params[0]), uint32(params[1]))
+}
 
+func fdFilestatGetFunc(ctx context.Context, mod api.Module, fd, resultBuf uint32) []uint64 {
 	sysCtx := mod.(*wasm.CallContext).Sys
 	file, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
 	if !ok {
@@ -884,14 +887,85 @@ var pathCreateDirectory = stubFunction(
 )
 
 // pathFilestatGet is the WASI function named functionPathFilestatGet which
-// returns the attributes of a file or directory.
+// returns the stat attributes of a file or directory.
+//
+// # Parameters
+//
+//   - fd: file descriptor of the folder to look in for the path
+//   - flags: flags determining the method of how paths are resolved
+//   - path: path under fd to get the filestat attributes data for
+//   - path_len: length of the path that was given
+//   - resultFilestat: offset to write the result filestat data
+//
+// Result (Errno)
+//
+// The return value is ErrnoSuccess except the following error conditions:
+//   - ErrnoBadf: `fd` is invalid
+//   - ErrnoNotdir: `fd` points to a file not a directory
+//   - ErrnoIo: could not stat `fd` on filesystem
+//   - ErrnoInval: the path contained "../"
+//   - ErrnoNametoolong: `path` + `path_len` is out of memory
+//   - ErrnoFault: `resultFilestat` points to an offset out of memory
+//   - ErrnoNoent: could not find the path
+//
+// The rest of this implementation matches that of fdFilestatGet, so is not
+// repeated here.
 //
+// Note: This is similar to `fstatat` in POSIX.
 // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_filestat_getfd-fd-flags-lookupflags-path-string---errno-filestat
-var pathFilestatGet = stubFunction(
-	functionPathFilestatGet,
-	[]wasm.ValueType{i32, i32, i32, i32, i32},
-	[]string{"fd", "flags", "path", "path_len", "result.buf"},
-)
+// and https://linux.die.net/man/2/fstatat
+var pathFilestatGet = &wasm.HostFunc{
+	ExportNames: []string{functionPathFilestatGet},
+	Name:        functionPathFilestatGet,
+	ParamTypes:  []api.ValueType{i32, i32, i32, i32, i32},
+	ParamNames:  []string{"fd", "flags", "path", "path_len", "result.buf"},
+	ResultTypes: []api.ValueType{i32},
+	Code: &wasm.Code{
+		IsHostFunction: true,
+		GoFunc:         api.GoModuleFunc(pathFilestatGetFn),
+	},
+}
+
+func pathFilestatGetFn(ctx context.Context, mod api.Module, params []uint64) []uint64 {
+	sysCtx := mod.(*wasm.CallContext).Sys
+	fsc := sysCtx.FS(ctx)
+
+	fd := uint32(params[0])
+
+	// TODO: implement flags?
+	// flags := uint32(params[1])
+
+	pathOffset := uint32(params[2])
+	pathLen := uint32(params[3])
+	resultBuf := uint32(params[4])
+
+	// open_at isn't supported in fs.FS, so we check the path can't escape,
+	// then join it with its parent
+	b, ok := mod.Memory().Read(ctx, pathOffset, pathLen)
+	if !ok {
+		return errnoNametoolong
+	}
+	pathName := string(b)
+
+	if dir, ok := fsc.OpenedFile(ctx, fd); !ok {
+		return errnoBadf
+	} else if dir.File == nil { // root
+	} else if _, ok := dir.File.(fs.ReadDirFile); !ok {
+		return errnoNotdir
+	} else {
+		pathName = path.Join(dir.Path, pathName)
+	}
+
+	// Sadly, we need to open the file to stat it.
+	pathFd, errnoResult := openFile(ctx, fsc, pathName)
+	if errnoResult != nil {
+		return errnoResult
+	}
+
+	// Close it when the function returns.
+	defer fsc.CloseFile(ctx, pathFd)
+	return fdFilestatGetFunc(ctx, mod, pathFd, resultBuf)
+}
 
 // pathFilestatSetTimes is the WASI function named functionPathFilestatSetTimes
 // which adjusts the timestamps of a file or directory.
@@ -1001,16 +1075,12 @@ func pathOpenFn(ctx context.Context, mod api.Module, params []uint64) []uint64 {
 		return errnoFault
 	}
 
-	if newFD, err := fsc.OpenFile(ctx, string(b)); err != nil {
-		switch {
-		case errors.Is(err, fs.ErrNotExist):
-			return errnoNoent
-		case errors.Is(err, fs.ErrExist):
-			return errnoExist
-		default:
-			return errnoIo
-		}
-	} else if !mod.Memory().WriteUint32Le(ctx, resultOpenedFd, newFD) {
+	newFD, errnoResult := openFile(ctx, fsc, string(b))
+	if errnoResult != nil {
+		return errnoResult
+	}
+
+	if !mod.Memory().WriteUint32Le(ctx, resultOpenedFd, newFD) {
 		_ = fsc.CloseFile(ctx, newFD)
 		return errnoFault
 	}
@@ -1064,3 +1134,33 @@ var pathUnlinkFile = stubFunction(
 	[]wasm.ValueType{i32, i32, i32},
 	[]string{"fd", "path", "path_len"},
 )
+
+// openFile attempts to open the file at the given path. Errors coerce to WASI
+// Errno, returned as a slice to avoid allocation per-error.
+//
+// Note: Coercion isn't centralized in internalsys.FSContext because ABI use
+// different error codes. For example, wasi-filesystem and GOOS=js don't map to
+// these Errno.
+func openFile(ctx context.Context, fsc *internalsys.FSContext, name string) (fd uint32, errnoResult []uint64) {
+	newFD, err := fsc.OpenFile(ctx, name)
+	if err == nil {
+		fd = newFD
+		return
+	}
+	// handle all the cases of FS.Open or internal to FSContext.OpenFile
+	switch {
+	case errors.Is(err, fs.ErrInvalid):
+		errnoResult = errnoInval
+	case errors.Is(err, fs.ErrNotExist):
+		// fs.FS is allowed to return this instead of ErrInvalid on an invalid path
+		errnoResult = errnoNoent
+	case errors.Is(err, fs.ErrExist):
+		errnoResult = errnoExist
+	case errors.Is(err, syscall.EBADF):
+		// fsc.OpenFile currently returns this on out of file descriptors
+		errnoResult = errnoBadf
+	default:
+		errnoResult = errnoIo
+	}
+	return
+}
diff --git a/imports/wasi_snapshot_preview1/wasi.go b/imports/wasi_snapshot_preview1/wasi.go
index 47f74c67..5c1d1e65 100644
--- a/imports/wasi_snapshot_preview1/wasi.go
+++ b/imports/wasi_snapshot_preview1/wasi.go
@@ -222,6 +222,7 @@ var (
 	errnoInval       = []uint64{uint64(ErrnoInval)}
 	errnoIo          = []uint64{uint64(ErrnoIo)}
 	errnoNoent       = []uint64{uint64(ErrnoNoent)}
+	errnoNotdir      = []uint64{uint64(ErrnoNotdir)}
 	errnoFault       = []uint64{uint64(ErrnoFault)}
 	errnoNametoolong = []uint64{uint64(ErrnoNametoolong)}
 	errnoSuccess     = []uint64{uint64(ErrnoSuccess)}
diff --git a/internal/sys/fs.go b/internal/sys/fs.go
index 0430b7b9..e24f06bc 100644
--- a/internal/sys/fs.go
+++ b/internal/sys/fs.go
@@ -127,7 +127,7 @@ func (c *FSContext) OpenFile(_ context.Context, name string /* TODO: flags int,
 	}
 
 	newFD := c.nextFD()
-	if newFD == 0 {
+	if newFD == 0 { // TODO: out of file descriptors
 		_ = f.Close()
 		return 0, syscall.EBADF
 	}


Test patch:
diff --git a/imports/wasi_snapshot_preview1/fs_test.go b/imports/wasi_snapshot_preview1/fs_test.go
index dbe77abd..932a254f 100644
--- a/imports/wasi_snapshot_preview1/fs_test.go
+++ b/imports/wasi_snapshot_preview1/fs_test.go
@@ -203,7 +203,7 @@ func Test_fdFdstatSetRights(t *testing.T) {
 
 func Test_fdFilestatGet(t *testing.T) {
 	file, dir := "a", "b"
-	testFS := fstest.MapFS{file: {Data: make([]byte, 123456), ModTime: time.Unix(1667482413, 0)}, dir: {Mode: fs.ModeDir, ModTime: time.Unix(1667482413, 0)}}
+	testFS := fstest.MapFS{file: {Data: make([]byte, 10), ModTime: time.Unix(1667482413, 0)}, dir: {Mode: fs.ModeDir, ModTime: time.Unix(1667482413, 0)}}
 
 	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().WithFS(testFS))
 	defer r.Close(testCtx)
@@ -233,7 +233,7 @@ func Test_fdFilestatGet(t *testing.T) {
 				'?', '?', '?', '?', '?', '?', '?', '?', // ino
 				4, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
 				'?', '?', '?', '?', '?', '?', '?', '?', // nlink
-				0x40, 0xe2, 0x1, 0, 0, 0, 0, 0, // size
+				10, 0, 0, 0, 0, 0, 0, 0, // size
 				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
 				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
 				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // ctim
@@ -1426,15 +1426,225 @@ func Test_pathCreateDirectory(t *testing.T) {
 `, log)
 }
 
-// Test_pathFilestatGet only tests it is stubbed for GrainLang per #271
 func Test_pathFilestatGet(t *testing.T) {
-	log := requireErrnoNosys(t, functionPathFilestatGet, 0, 0, 0, 0, 0)
-	require.Equal(t, `
---> proxy.path_filestat_get(fd=0,flags=0,path=0,path_len=0,result.buf=0)
-	--> wasi_snapshot_preview1.path_filestat_get(fd=0,flags=0,path=0,path_len=0,result.buf=0)
-	<-- ENOSYS
-<-- (52)
-`, log)
+	file, dir := "a", "b"
+	testFS := fstest.MapFS{
+		file:             {Data: make([]byte, 10), ModTime: time.Unix(1667482413, 0)},
+		dir:              {Mode: fs.ModeDir, ModTime: time.Unix(1667482413, 0)},
+		dir + "/" + file: {Data: make([]byte, 20), ModTime: time.Unix(1667482413, 0)},
+	}
+
+	initialMemoryFile := append([]byte{'?'}, file...)
+	initialMemoryDir := append([]byte{'?'}, dir...)
+	initialMemoryNotExists := []byte{'?', '?'}
+
+	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().WithFS(testFS))
+	defer r.Close(testCtx)
+	memorySize := mod.Memory().Size(testCtx)
+
+	// open both paths without using WASI
+	fsc := mod.(*wasm.CallContext).Sys.FS(testCtx)
+
+	rootFd := uint32(3) // after stderr
+
+	fileFd, err := fsc.OpenFile(testCtx, file)
+	require.NoError(t, err)
+
+	dirFd, err := fsc.OpenFile(testCtx, dir)
+	require.NoError(t, err)
+
+	tests := []struct {
+		name                        string
+		fd, pathLen, resultFilestat uint32
+		memory, expectedMemory      []byte
+		expectedErrno               Errno
+		expectedLog                 string
+	}{
+		{
+			name:           "file under root",
+			fd:             rootFd,
+			memory:         initialMemoryFile,
+			pathLen:        1,
+			resultFilestat: 2,
+			expectedMemory: append(
+				initialMemoryFile,
+				'?', '?', '?', '?', '?', '?', '?', '?', // dev
+				'?', '?', '?', '?', '?', '?', '?', '?', // ino
+				4, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
+				'?', '?', '?', '?', '?', '?', '?', '?', // nlink
+				10, 0, 0, 0, 0, 0, 0, 0, // size
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // ctim
+			),
+			expectedLog: `
+--> proxy.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=2)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=2)
+	<== ESUCCESS
+<-- (0)
+`,
+		},
+		{
+			name:           "file under dir",
+			fd:             dirFd, // root
+			memory:         initialMemoryFile,
+			pathLen:        1,
+			resultFilestat: 2,
+			expectedMemory: append(
+				initialMemoryFile,
+				'?', '?', '?', '?', '?', '?', '?', '?', // dev
+				'?', '?', '?', '?', '?', '?', '?', '?', // ino
+				4, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
+				'?', '?', '?', '?', '?', '?', '?', '?', // nlink
+				20, 0, 0, 0, 0, 0, 0, 0, // size
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // ctim
+			),
+			expectedLog: `
+--> proxy.path_filestat_get(fd=5,flags=0,path=1,path_len=1,result.buf=2)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=5,flags=0,path=1,path_len=1,result.buf=2)
+	<== ESUCCESS
+<-- (0)
+`,
+		},
+		{
+			name:           "dir under root",
+			fd:             rootFd,
+			memory:         initialMemoryDir,
+			pathLen:        1,
+			resultFilestat: 2,
+			expectedMemory: append(
+				initialMemoryDir,
+				'?', '?', '?', '?', '?', '?', '?', '?', // dev
+				'?', '?', '?', '?', '?', '?', '?', '?', // ino
+				3, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
+				'?', '?', '?', '?', '?', '?', '?', '?', // nlink
+				0, 0, 0, 0, 0, 0, 0, 0, // size
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
+				0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // ctim
+			),
+			expectedLog: `
+--> proxy.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=2)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=2)
+	<== ESUCCESS
+<-- (0)
+`,
+		},
+		{
+			name:          "bad FD - not opened",
+			fd:            math.MaxUint32,
+			expectedErrno: ErrnoBadf,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=4294967295,flags=0,path=1,path_len=0,result.buf=0)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=4294967295,flags=0,path=1,path_len=0,result.buf=0)
+	<== EBADF
+<-- (8)
+`,
+		},
+		{
+			name:           "bad FD - not dir",
+			fd:             fileFd,
+			memory:         initialMemoryFile,
+			pathLen:        1,
+			resultFilestat: 2,
+			expectedErrno:  ErrnoNotdir,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=4,flags=0,path=1,path_len=1,result.buf=2)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=4,flags=0,path=1,path_len=1,result.buf=2)
+	<== ENOTDIR
+<-- (54)
+`,
+		},
+		{
+			name:           "path under root doesn't exist",
+			fd:             rootFd,
+			memory:         initialMemoryNotExists,
+			pathLen:        1,
+			resultFilestat: 2,
+			expectedErrno:  ErrnoNoent,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=2)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=2)
+	<== ENOENT
+<-- (44)
+`,
+		},
+		{
+			name:           "path under dir doesn't exist",
+			fd:             dirFd,
+			memory:         initialMemoryNotExists,
+			pathLen:        1,
+			resultFilestat: 2,
+			expectedErrno:  ErrnoNoent,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=5,flags=0,path=1,path_len=1,result.buf=2)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=5,flags=0,path=1,path_len=1,result.buf=2)
+	<== ENOENT
+<-- (44)
+`,
+		},
+		{
+			name:           "path invalid",
+			fd:             dirFd,
+			memory:         []byte("?../foo"),
+			pathLen:        6,
+			resultFilestat: 7,
+			expectedErrno:  ErrnoNoent,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=5,flags=0,path=1,path_len=6,result.buf=7)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=5,flags=0,path=1,path_len=6,result.buf=7)
+	<== ENOENT
+<-- (44)
+`,
+		},
+		{
+			name:          "path is out of memory",
+			fd:            rootFd,
+			memory:        initialMemoryFile,
+			pathLen:       memorySize,
+			expectedErrno: ErrnoNametoolong,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=3,flags=0,path=1,path_len=65536,result.buf=0)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=3,flags=0,path=1,path_len=65536,result.buf=0)
+	<== ENAMETOOLONG
+<-- (37)
+`,
+		},
+		{
+			name:           "resultFilestat exceeds the maximum valid address by 1",
+			fd:             rootFd,
+			memory:         initialMemoryFile,
+			pathLen:        1,
+			resultFilestat: memorySize - 64 + 1,
+			expectedErrno:  ErrnoFault,
+			expectedLog: `
+--> proxy.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=65473)
+	==> wasi_snapshot_preview1.path_filestat_get(fd=3,flags=0,path=1,path_len=1,result.buf=65473)
+	<== EFAULT
+<-- (21)
+`,
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+
+		t.Run(tc.name, func(t *testing.T) {
+			defer log.Reset()
+
+			maskMemory(t, testCtx, mod, len(tc.expectedMemory))
+			mod.Memory().Write(testCtx, 0, tc.memory)
+
+			requireErrno(t, tc.expectedErrno, mod, functionPathFilestatGet, uint64(tc.fd), uint64(0), uint64(1), uint64(tc.pathLen), uint64(tc.resultFilestat))
+			require.Equal(t, tc.expectedLog, "\n"+log.String())
+
+			actual, ok := mod.Memory().Read(testCtx, 0, uint32(len(tc.expectedMemory)))
+			require.True(t, ok)
+			require.Equal(t, tc.expectedMemory, actual)
+		})
+	}
 }
 
 // Test_pathFilestatSetTimes only tests it is stubbed for GrainLang per #271
@@ -1462,7 +1672,7 @@ func Test_pathLink(t *testing.T) {
 func Test_pathOpen(t *testing.T) {
 	rootFD := uint32(3) // after 0, 1, and 2, that are stdin/out/err
 	expectedFD := rootFD + 1
-	// Setup the initial memory to include the path name starting at an offset.
+	// set up the initial memory to include the path name starting at an offset.
 	pathName := "wazero"
 	initialMemory := append([]byte{'?'}, pathName...)
 
@@ -1519,10 +1729,9 @@ func Test_pathOpen_Errors(t *testing.T) {
 
 	validPath := uint32(0)    // arbitrary offset
 	validPathLen := uint32(6) // the length of "wazero"
-	mod.Memory().Write(testCtx, validPath, []byte(pathName))
 
 	tests := []struct {
-		name                                      string
+		name, pathName                            string
 		fd, path, pathLen, oflags, resultOpenedFd uint32
 		expectedErrno                             Errno
 		expectedLog                               string
@@ -1549,6 +1758,20 @@ func Test_pathOpen_Errors(t *testing.T) {
 	==> wasi_snapshot_preview1.path_open(fd=3,dirflags=0,path=65536,path_len=6,oflags=0,fs_rights_base=0,fs_rights_inheriting=0,fdflags=0,result.opened_fd=0)
 	<== EFAULT
 <-- (21)
+`,
+		},
+		{
+			name:     "path invalid",
+			fd:       validFD,
+			pathName: "../foo",
+			pathLen:  6,
+			// fstest.MapFS returns file not found instead of invalid on invalid path
+			expectedErrno: ErrnoNoent,
+			expectedLog: `
+--> proxy.path_open(fd=3,dirflags=0,path=0,path_len=6,oflags=0,fs_rights_base=0,fs_rights_inheriting=0,fdflags=0,result.opened_fd=0)
+	==> wasi_snapshot_preview1.path_open(fd=3,dirflags=0,path=0,path_len=6,oflags=0,fs_rights_base=0,fs_rights_inheriting=0,fdflags=0,result.opened_fd=0)
+	<== ENOENT
+<-- (44)
 `,
 		},
 		{
@@ -1567,6 +1790,7 @@ func Test_pathOpen_Errors(t *testing.T) {
 		{
 			name:          "no such file exists",
 			fd:            validFD,
+			pathName:      pathName,
 			path:          validPath,
 			pathLen:       validPathLen - 1, // this make the path "wazer", which doesn't exit
 			expectedErrno: ErrnoNoent,
@@ -1580,6 +1804,7 @@ func Test_pathOpen_Errors(t *testing.T) {
 		{
 			name:           "out-of-memory writing resultOpenedFd",
 			fd:             validFD,
+			pathName:       pathName,
 			path:           validPath,
 			pathLen:        validPathLen,
 			resultOpenedFd: mod.Memory().Size(testCtx), // path and pathLen correctly point to the right path, but where to write the opened FD is outside memory.
@@ -1598,6 +1823,8 @@ func Test_pathOpen_Errors(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			defer log.Reset()
 
+			mod.Memory().Write(testCtx, validPath, []byte(tc.pathName))
+
 			requireErrno(t, tc.expectedErrno, mod, functionPathOpen, uint64(tc.fd), uint64(0), uint64(tc.path),
 				uint64(tc.pathLen), uint64(tc.oflags), 0, 0, 0, uint64(tc.resultOpenedFd))
 			require.Equal(t, tc.expectedLog, "\n"+log.String())