Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasip1: fix file open modes used by wasi-libc #1421

Merged
merged 10 commits into from
May 2, 2023
4 changes: 2 additions & 2 deletions cmd/wazero/wazero_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,8 @@ func TestRun(t *testing.T) {
==> wasi_snapshot_preview1.fd_prestat_get(fd=4)
<== (prestat=,errno=EBADF)
==> wasi_snapshot_preview1.fd_fdstat_get(fd=3)
<== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
==> wasi_snapshot_preview1.path_open(fd=3,dirflags=SYMLINK_FOLLOW,path=bear.txt,oflags=,fs_rights_base=,fs_rights_inheriting=,fdflags=)
<== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=FD_DATASYNC|FDSTAT_SET_FLAGS|FD_SYNC|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK,fs_rights_inheriting=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK},errno=ESUCCESS)
==> wasi_snapshot_preview1.path_open(fd=3,dirflags=SYMLINK_FOLLOW,path=bear.txt,oflags=,fs_rights_base=FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_ADVISE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK|PATH_RENAME_SOURCE|PATH_RENAME_TARGET|PATH_FILESTAT_GET|PATH_FILESTAT_SET_SIZE|PATH_FILESTAT_SET_TIMES|FD_FILESTAT_GET|FD_FILESTAT_SET_TIMES|PATH_SYMLINK|PATH_REMOVE_DIRECTORY|PATH_UNLINK_FILE|POLL_FD_READWRITE,fs_rights_inheriting=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK|PATH_RENAME_SOURCE|PATH_RENAME_TARGET|PATH_FILESTAT_GET|PATH_FILESTAT_SET_SIZE|PATH_FILESTAT_SET_TIMES|FD_FILESTAT_GET|FD_FILESTAT_SET_SIZE|FD_FILESTAT_SET_TIMES|PATH_SYMLINK|PATH_REMOVE_DIRECTORY|PATH_UNLINK_FILE|POLL_FD_READWRITE,fdflags=)
<== (opened_fd=4,errno=ESUCCESS)
==> wasi_snapshot_preview1.fd_filestat_get(fd=4)
<== (filestat={filetype=REGULAR_FILE,size=5,mtim=%d},errno=ESUCCESS)
Expand Down
Binary file not shown.
102 changes: 81 additions & 21 deletions imports/wasi_snapshot_preview1/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,24 +222,66 @@ func fdFdstatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E
fdflags = wasip1.FD_APPEND
}

filetype := getWasiFiletype(st.Mode)
writeFdstat(buf, filetype, fdflags)
var fsRightsBase uint32
var fsRightsInheriting uint32
fileType := getWasiFiletype(st.Mode)

switch fileType {
case wasip1.FILETYPE_DIRECTORY:
// To satisfy wasi-testsuite, we must advertise that directories cannot
// be given seek permission (RIGHT_FD_SEEK).
fsRightsBase = dirRightsBase
fsRightsInheriting = fileRightsBase | dirRightsBase
default:
fsRightsBase = fileRightsBase
}

writeFdstat(buf, fileType, fdflags, fsRightsBase, fsRightsInheriting)
return 0
}

var blockFdstat = []byte{
wasip1.FILETYPE_BLOCK_DEVICE, 0, // filetype
0, 0, 0, 0, 0, 0, // fdflags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
}

func writeFdstat(buf []byte, filetype uint8, fdflags uint16) {
// memory is re-used, so ensure the result is defaulted.
copy(buf, blockFdstat)
buf[0] = filetype
buf[2] = byte(fdflags)
const fileRightsBase = wasip1.RIGHT_FD_DATASYNC |
wasip1.RIGHT_FD_READ |
wasip1.RIGHT_FD_SEEK |
wasip1.RIGHT_FDSTAT_SET_FLAGS |
wasip1.RIGHT_FD_SYNC |
wasip1.RIGHT_FD_TELL |
wasip1.RIGHT_FD_WRITE |
wasip1.RIGHT_FD_ADVISE |
wasip1.RIGHT_FD_ALLOCATE |
wasip1.RIGHT_FD_FILESTAT_GET |
wasip1.RIGHT_FD_FILESTAT_SET_SIZE |
wasip1.RIGHT_FD_FILESTAT_SET_TIMES |
wasip1.RIGHT_POLL_FD_READWRITE

const dirRightsBase = wasip1.RIGHT_FD_DATASYNC |
wasip1.RIGHT_FDSTAT_SET_FLAGS |
wasip1.RIGHT_FD_SYNC |
wasip1.RIGHT_PATH_CREATE_DIRECTORY |
wasip1.RIGHT_PATH_CREATE_FILE |
wasip1.RIGHT_PATH_LINK_SOURCE |
wasip1.RIGHT_PATH_LINK_TARGET |
wasip1.RIGHT_PATH_OPEN |
wasip1.RIGHT_FD_READDIR |
wasip1.RIGHT_PATH_READLINK |
wasip1.RIGHT_PATH_RENAME_SOURCE |
wasip1.RIGHT_PATH_RENAME_TARGET |
wasip1.RIGHT_PATH_FILESTAT_GET |
wasip1.RIGHT_PATH_FILESTAT_SET_SIZE |
wasip1.RIGHT_PATH_FILESTAT_SET_TIMES |
wasip1.RIGHT_FD_FILESTAT_GET |
wasip1.RIGHT_FD_FILESTAT_SET_TIMES |
wasip1.RIGHT_PATH_SYMLINK |
wasip1.RIGHT_PATH_REMOVE_DIRECTORY |
wasip1.RIGHT_PATH_UNLINK_FILE

func writeFdstat(buf []byte, fileType uint8, fdflags uint16, fsRightsBase, fsRightsInheriting uint32) {
b := (*[24]byte)(buf)
le.PutUint16(b[0:], uint16(fileType))
le.PutUint16(b[2:], fdflags)
le.PutUint32(b[4:], 0)
le.PutUint64(b[8:], uint64(fsRightsBase))
le.PutUint64(b[16:], uint64(fsRightsInheriting))
}

// fdFdstatSetFlags is the WASI function named FdFdstatSetFlagsName which
Expand Down Expand Up @@ -1713,24 +1755,42 @@ func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags int)
} else if oflags&wasip1.O_EXCL != 0 {
openFlags |= syscall.O_EXCL
}
// Because we don't implement rights, we paritally rely on the open flags
// to determine the mode in which the file will be opened. This will create
// divergeant behavior compared to WASI runtimes which have a more strict
// interpretation of the WASI capabilities model; for example, a program
// which sets O_CREAT but does not give read or write permissions will
// successfully create a file when running with wazero, but might get a
// permission denied error on other runtimes.
defaultMode := syscall.O_RDONLY
if oflags&wasip1.O_TRUNC != 0 {
openFlags |= syscall.O_RDWR | syscall.O_TRUNC
openFlags |= syscall.O_TRUNC
defaultMode = syscall.O_RDWR
}
if oflags&wasip1.O_CREAT != 0 {
openFlags |= syscall.O_RDWR | syscall.O_CREAT
openFlags |= syscall.O_CREAT
defaultMode = syscall.O_RDWR
}
if fdflags&wasip1.FD_APPEND != 0 {
openFlags |= syscall.O_RDWR | syscall.O_APPEND
openFlags |= syscall.O_APPEND
defaultMode = syscall.O_RDWR
}
// Since rights were discontinued in wasi, we only interpret RIGHT_FD_WRITE
// because it is the only way to know that we need to set write permissions
// on a file if the application did not pass any of O_CREATE, O_APPEND, nor
// O_TRUNC.
if rights&wasip1.RIGHT_FD_WRITE != 0 {
const r = wasip1.RIGHT_FD_READ
const w = wasip1.RIGHT_FD_WRITE
const rw = r | w
switch {
case (rights & rw) == rw:
openFlags |= syscall.O_RDWR
}
if openFlags == 0 {
openFlags = syscall.O_RDONLY
case (rights & w) == w:
openFlags |= syscall.O_WRONLY
case (rights & r) == r:
openFlags |= syscall.O_RDONLY
default:
openFlags |= defaultMode
}
return
}
Expand Down
34 changes: 17 additions & 17 deletions imports/wasi_snapshot_preview1/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,12 @@ func Test_fdFdstatGet(t *testing.T) {
expectedMemory: []byte{
1, 0, // fs_filetype
0, 0, 0, 0, 0, 0, // fs_flags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0xff, 0x1, 0xe0, 0x8, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
},
expectedLog: `
==> wasi_snapshot_preview1.fd_fdstat_get(fd=0)
<== (stat={filetype=BLOCK_DEVICE,fdflags=,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
<== (stat={filetype=BLOCK_DEVICE,fdflags=,fs_rights_base=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE,fs_rights_inheriting=},errno=ESUCCESS)
`,
},
{
Expand All @@ -262,12 +262,12 @@ func Test_fdFdstatGet(t *testing.T) {
expectedMemory: []byte{
1, 0, // fs_filetype
1, 0, 0, 0, 0, 0, // fs_flags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0xff, 0x1, 0xe0, 0x8, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
},
expectedLog: `
==> wasi_snapshot_preview1.fd_fdstat_get(fd=1)
<== (stat={filetype=BLOCK_DEVICE,fdflags=APPEND,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
<== (stat={filetype=BLOCK_DEVICE,fdflags=APPEND,fs_rights_base=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE,fs_rights_inheriting=},errno=ESUCCESS)
`,
},
{
Expand All @@ -276,12 +276,12 @@ func Test_fdFdstatGet(t *testing.T) {
expectedMemory: []byte{
1, 0, // fs_filetype
1, 0, 0, 0, 0, 0, // fs_flags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0xff, 0x1, 0xe0, 0x8, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
},
expectedLog: `
==> wasi_snapshot_preview1.fd_fdstat_get(fd=2)
<== (stat={filetype=BLOCK_DEVICE,fdflags=APPEND,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
<== (stat={filetype=BLOCK_DEVICE,fdflags=APPEND,fs_rights_base=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE,fs_rights_inheriting=},errno=ESUCCESS)
`,
},
{
Expand All @@ -290,12 +290,12 @@ func Test_fdFdstatGet(t *testing.T) {
expectedMemory: []byte{
3, 0, // fs_filetype
0, 0, 0, 0, 0, 0, // fs_flags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
0x19, 0xfe, 0xbf, 0x7, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
0xff, 0xff, 0xff, 0xf, 0x0, 0x0, 0x0, 0x0, // fs_rights_inheriting
},
expectedLog: `
==> wasi_snapshot_preview1.fd_fdstat_get(fd=3)
<== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
<== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=FD_DATASYNC|FDSTAT_SET_FLAGS|FD_SYNC|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK,fs_rights_inheriting=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK},errno=ESUCCESS)
`,
},
{
Expand All @@ -304,12 +304,12 @@ func Test_fdFdstatGet(t *testing.T) {
expectedMemory: []byte{
4, 0, // fs_filetype
0, 0, 0, 0, 0, 0, // fs_flags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0xff, 0x1, 0xe0, 0x8, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
},
expectedLog: `
==> wasi_snapshot_preview1.fd_fdstat_get(fd=4)
<== (stat={filetype=REGULAR_FILE,fdflags=,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
<== (stat={filetype=REGULAR_FILE,fdflags=,fs_rights_base=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE,fs_rights_inheriting=},errno=ESUCCESS)
`,
},
{
Expand All @@ -318,12 +318,12 @@ func Test_fdFdstatGet(t *testing.T) {
expectedMemory: []byte{
3, 0, // fs_filetype
0, 0, 0, 0, 0, 0, // fs_flags
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
0x19, 0xfe, 0xbf, 0x7, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
0xff, 0xff, 0xff, 0xf, 0x0, 0x0, 0x0, 0x0, // fs_rights_inheriting
},
expectedLog: `
==> wasi_snapshot_preview1.fd_fdstat_get(fd=5)
<== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=,fs_rights_inheriting=},errno=ESUCCESS)
<== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=FD_DATASYNC|FDSTAT_SET_FLAGS|FD_SYNC|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK,fs_rights_inheriting=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK},errno=ESUCCESS)
`,
},
{
Expand Down Expand Up @@ -3936,16 +3936,16 @@ func Test_pathOpen(t *testing.T) {
`,
},
{
name: "sysfs.DirFS RIGHT_FD_WRITE",
name: "sysfs.DirFS RIGHT_FD_READ|RIGHT_FD_WRITE",
fs: writeFS,
path: func(*testing.T) string { return fileName },
oflags: 0,
rights: wasip1.RIGHT_FD_WRITE,
rights: wasip1.RIGHT_FD_READ | wasip1.RIGHT_FD_WRITE,
expected: func(t *testing.T, fsc *sys.FSContext) {
requireContents(t, fsc, expectedOpenedFd, fileName, fileContents)
},
expectedLog: `
==> wasi_snapshot_preview1.path_open(fd=3,dirflags=,path=file,oflags=,fs_rights_base=FD_WRITE,fs_rights_inheriting=,fdflags=)
==> wasi_snapshot_preview1.path_open(fd=3,dirflags=,path=file,oflags=,fs_rights_base=FD_READ|FD_WRITE,fs_rights_inheriting=,fdflags=)
<== (opened_fd=4,errno=ESUCCESS)
`,
},
Expand Down
10 changes: 10 additions & 0 deletions imports/wasi_snapshot_preview1/fs_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,19 @@ func Test_openFlags(t *testing.T) {
dirflags: wasip1.LOOKUP_SYMLINK_FOLLOW,
expectedOpenFlags: syscall.O_RDONLY,
},
{
name: "rights=FD_READ",
rights: wasip1.RIGHT_FD_READ,
expectedOpenFlags: platform.O_NOFOLLOW | syscall.O_RDONLY,
},
{
name: "rights=FD_WRITE",
rights: wasip1.RIGHT_FD_WRITE,
expectedOpenFlags: platform.O_NOFOLLOW | syscall.O_WRONLY,
},
{
name: "rights=FD_READ|FD_WRITE",
rights: wasip1.RIGHT_FD_READ | wasip1.RIGHT_FD_WRITE,
expectedOpenFlags: platform.O_NOFOLLOW | syscall.O_RDWR,
},
}
Expand Down
54 changes: 54 additions & 0 deletions imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
Expand Down Expand Up @@ -70,6 +71,54 @@ void main_sleepmillis(int millis) {
printf("OK\n");
}

void main_open_rdonly() {
const char *path = "zig-cc.rdonly.test";
int fd;
char buf[32];

fd = open(path, O_CREAT|O_TRUNC|O_RDONLY, 0644);
if (fd < 0) {
perror("ERR: open");
goto cleanup;
}
if (write(fd, "hello world\n", 12) >= 0) {
perror("ERR: write");
goto cleanup;
}
if (read(fd, buf, sizeof(buf)) != 0) {
perror("ERR: read");
goto cleanup;
}
puts("OK");
cleanup:
close(fd);
unlink(path);
}

void main_open_wronly() {
const char *path = "zig-cc.wronly.test";
int fd;
char buf[32];

fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644);
if (fd < 0) {
perror("ERR: open");
goto cleanup;
}
if (write(fd, "hello world\n", 12) != 12) {
perror("ERR: write");
goto cleanup;
}
if (read(fd, buf, sizeof(buf)) >= 0) {
perror("ERR: read");
goto cleanup;
}
puts("OK");
cleanup:
close(fd);
unlink(path);
}

int main(int argc, char** argv) {
if (strcmp(argv[1],"ls")==0) {
bool repeat = false;
Expand All @@ -95,6 +144,11 @@ int main(int argc, char** argv) {
timeout = atoi(argv[2]);
}
main_sleepmillis(timeout);
} else if (strcmp(argv[1],"open-rdonly")==0) {
main_open_rdonly();

} else if (strcmp(argv[1],"open-wronly")==0) {
main_open_wronly();

} else {
fprintf(stderr, "unknown command: %s\n", argv[1]);
Expand Down
Binary file modified imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.wasm
Binary file not shown.
32 changes: 32 additions & 0 deletions imports/wasi_snapshot_preview1/wasi_stdlib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,35 @@ func Test_Sleep(t *testing.T) {
require.True(t, time.Since(start) >= 100*time.Millisecond)
require.Equal(t, "OK\n", console)
}

func Test_open(t *testing.T) {
for toolchain, bin := range map[string][]byte{
"zig-cc": wasmZigCc,
} {
toolchain := toolchain
bin := bin
t.Run(toolchain, func(t *testing.T) {
testOpenReadOnly(t, bin)
testOpenWriteOnly(t, bin)
})
}
}

func testOpenReadOnly(t *testing.T, bin []byte) {
testOpen(t, "rdonly", bin)
}

func testOpenWriteOnly(t *testing.T, bin []byte) {
testOpen(t, "wronly", bin)
}

func testOpen(t *testing.T, cmd string, bin []byte) {
t.Run(cmd, func(t *testing.T) {
moduleConfig := wazero.NewModuleConfig().
WithArgs("wasi", "open-"+cmd).
WithFSConfig(wazero.NewFSConfig().WithDirMount(t.TempDir(), "/"))

console := compileAndRun(t, moduleConfig, bin)
require.Equal(t, "OK", strings.TrimSpace(console))
})
}
Loading