diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/lucet-wasi-wasmsbx/tests | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/lucet-wasi-wasmsbx/tests')
21 files changed, 1022 insertions, 0 deletions
diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/cant_dotdot.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/cant_dotdot.c new file mode 100644 index 0000000000..12b6ce78c9 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/cant_dotdot.c @@ -0,0 +1,12 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> + +int main() +{ + FILE *file = fopen("/sandbox/../outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); + + return 0; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/clock_getres.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/clock_getres.c new file mode 100644 index 0000000000..27a9f46472 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/clock_getres.c @@ -0,0 +1,13 @@ +#include <assert.h> +#include <time.h> + +int main() +{ + struct timespec ts; + + // supported clocks + assert(clock_getres(CLOCK_REALTIME, &ts) == 0); + assert(clock_getres(CLOCK_MONOTONIC, &ts) == 0); + assert(clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0); + assert(clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts) == 0); +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/duplicate_import.wat b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/duplicate_import.wat new file mode 100644 index 0000000000..6bd7c1eeca --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/duplicate_import.wat @@ -0,0 +1,38 @@ +(module + (type (func (param i32 i32 i32 i32) (result i32))) + (type (func (result i32))) + + ;; import fd_read, this is fine. + (func $read (import "wasi_unstable" "fd_read") (type 0)) + + ;; import fd_write, this is also fine. + (func $write (import "wasi_unstable" "fd_write") (type 0)) + + ;; import fd_read, again, under a different name! + ;; this is to test that we join together the imports. + ;; the .wat would be invalid if their types disagree, so there + ;; is no observable difference between $read and $read_2 + (func $read_2 (import "wasi_unstable" "fd_read") (type 0)) + + ;; import fd_write again for grins. + (import "wasi_unstable" "fd_write" (func (type 0))) + (memory 1) + (data (i32.const 0) "duplicate import works!\0a") + (data (i32.const 64) "\00\00\00\00\18\00\00\00") + + (func $_setup (type 1) + (call $write (i32.const 1) (i32.const 64) (i32.const 1) (i32.const 0))) + + ;; declare that, actually, one of the imported functions is exported + (export "read_2" (func $read_2)) + ;; and delcare that the *other* read function is also exported, by a + ;; different name. This lets us check that when we merge the functions, + ;; we also merge their export names properly. + (export "read" (func $read)) + + ;; and check that other exported functions still work, and are not affected + (export "write" (func $write)) + + ;; and that we can export local functions without issue + (export "_start" (func $_setup)) +) diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/exitcode.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/exitcode.c new file mode 100644 index 0000000000..500d440ad0 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/exitcode.c @@ -0,0 +1,4 @@ +int main() +{ + return 120; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/follow_symlink.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/follow_symlink.c new file mode 100644 index 0000000000..e8db730df4 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/follow_symlink.c @@ -0,0 +1,14 @@ +#include <assert.h> +#include <stdio.h> + +int main() +{ + FILE *file = fopen("/sandbox/subdir2/input_link.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + assert(fputc(c, stdout) != EOF); + c = fgetc(file); + } +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/fs.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/fs.c new file mode 100644 index 0000000000..6f4bfa78e5 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/fs.c @@ -0,0 +1,158 @@ +#include <sys/stat.h> +#include <sys/time.h> + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> + +int main(void) +{ + struct timespec times[2]; + struct dirent * entry; + char buf[4]; + DIR * dir; + FILE * fp; + struct stat st; + off_t offset; + int fd; + int res; + + fd = open("/sandbox/testfile", O_CREAT | O_RDWR, 0644); + assert(fd != -1); + + res = posix_fallocate(fd, 0, 10000); + assert(res == 0); + + res = fstat(fd, &st); + assert(res == 0); + assert(st.st_size == 10000); + + res = ftruncate(fd, 1000); + res = fstat(fd, &st); + assert(res == 0); + assert(st.st_size == 1000); + assert(st.st_nlink == 1); + res = posix_fadvise(fd, 0, 1000, POSIX_FADV_RANDOM); + assert(res == 0); + + res = (int) write(fd, "test", 4); + assert(res == 4); + + offset = lseek(fd, 0, SEEK_CUR); + assert(offset == 4); + offset = lseek(fd, 0, SEEK_END); + assert(offset == 1000); + offset = lseek(fd, 0, SEEK_SET); + assert(offset == 0); + + res = fdatasync(fd); + assert(res == 0); + + res = fsync(fd); + assert(res == 0); + + times[0] = (struct timespec){ .tv_sec = 1557403800, .tv_nsec = 0 }; + times[1] = (struct timespec){ .tv_sec = 1557403800, .tv_nsec = 0 }; + + res = futimens(fd, times); + assert(res == 0); + + res = pread(fd, buf, sizeof buf, 2); + assert(res == 4); + assert(buf[1] == 't'); + + res = pwrite(fd, "T", 1, 3); + assert(res == 1); + + res = pread(fd, buf, sizeof buf, 2); + assert(res == 4); + assert(buf[1] == 'T'); + + res = close(fd); + assert(res == 0); + + dir = opendir("/nonexistent"); + assert(dir == NULL); + + res = mkdir("/sandbox/test", 0755); + assert(res == 0); + + res = mkdir("/sandbox/test", 0755); + assert(res == -1); + assert(errno == EEXIST); + + res = rmdir("/sandbox/test"); + assert(res == 0); + + res = rmdir("/sandbox/test"); + assert(res == -1); + + res = rename("/sandbox/testfile", "/sandbox/testfile2"); + assert(res == 0); + + res = unlink("/sandbox/testfile"); + assert(res == -1); + + res = access("/sandbox/testfile2", R_OK); + assert(res == 0); + + res = link("/sandbox/testfile2", "/sandbox/testfile-link"); + assert(res == 0); + + res = access("/sandbox/testfile-link", R_OK); + assert(res == 0); + + res = symlink("/sandbox/testfile-link", "/sandbox/testfile-symlink"); + assert(res == 0); + + res = symlink("/sandbox/testfile2", "/sandbox/testfile-symlink"); + assert(res == -1); + + res = sched_yield(); + assert(res == 0); + + fd = open("/sandbox/testfile2", O_RDONLY); + assert(fd != -1); + + fp = fdopen(fd, "r"); + assert(fp != NULL); + + res = fgetc(fp); + assert(res == 't'); + + res = fclose(fp); + assert(res == 0); + + dir = opendir("/sandbox"); + assert(dir != NULL); + + res = 0; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') { + res += 1000; + } else { + res++; + } + } + assert(res == 2003); + + res = closedir(dir); + assert(res == 0); + + res = mkdir("/sandbox/a", 0755); + assert(res == 0); + res = mkdir("/sandbox/a/b", 0755); + assert(res == 0); + res = mkdir("/sandbox/a/b/c", 0755); + assert(res == 0); + res = access("/sandbox/a/b/c", R_OK); + assert(res == 0); + + return 0; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/getentropy.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/getentropy.c new file mode 100644 index 0000000000..439512df10 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/getentropy.c @@ -0,0 +1,17 @@ +#include <assert.h> +#include <unistd.h> + +int main() +{ + char buf[256] = { 0 }; + assert(getentropy(buf, 256) == 0); + + for (int i = 0; i < 256; i++) { + if (buf[i] != 0) { + return 0; + } + } + + // if this ever is reached, we either have a bug or should buy a lottery ticket + return 1; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/getrusage.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/getrusage.c new file mode 100644 index 0000000000..7ff12fe12a --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/getrusage.c @@ -0,0 +1,30 @@ +#include <assert.h> +#include <sys/resource.h> + +// Temporary fix until +// https://github.com/CraneStation/wasi-sysroot/pull/24/ +// is available in wasi-sdk package +extern int getrusage(int who, struct rusage *usage); + +int main() +{ + struct rusage ru1; + getrusage(RUSAGE_SELF, &ru1); + + for (int i = 0; i < 1000; i++) { + } + + struct rusage ru2; + getrusage(RUSAGE_SELF, &ru2); + + // assert that some time has passed + long long s1 = ru1.ru_utime.tv_sec; + long long us1 = ru1.ru_utime.tv_usec; + long long s2 = ru2.ru_utime.tv_sec; + long long us2 = ru2.ru_utime.tv_usec; + assert(s1 <= s2); + if (s1 == s2) { + // strictly less than, so the timestamps can't be equal + assert(us1 < us2); + } +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/gettimeofday.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/gettimeofday.c new file mode 100644 index 0000000000..f9e8d5a811 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/gettimeofday.c @@ -0,0 +1,26 @@ +#include <assert.h> +#include <stdlib.h> +#include <sys/time.h> + +int main() +{ + struct timeval tv1; + gettimeofday(&tv1, NULL); + + for (int i = 0; i < 1000; i++) { + } + + struct timeval tv2; + gettimeofday(&tv2, NULL); + + // assert that some time has passed + long long s1 = tv1.tv_sec; + long long us1 = tv1.tv_usec; + long long s2 = tv2.tv_sec; + long long us2 = tv2.tv_usec; + assert(s1 <= s2); + if (s1 == s2) { + // strictly less than, so the timestamps can't be equal + assert(us1 < us2); + } +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/notdir.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/notdir.c new file mode 100644 index 0000000000..fcd47a69db --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/notdir.c @@ -0,0 +1,12 @@ +#include <assert.h> +#include <errno.h> +#include <dirent.h> + +int main() +{ + DIR *dir = opendir("/sandbox/notadir"); + assert(dir == NULL); + assert(errno == ENOTDIR); + + return 0; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/poll.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/poll.c new file mode 100644 index 0000000000..d8f9a8c1de --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/poll.c @@ -0,0 +1,32 @@ +#include <assert.h> +#include <poll.h> +#include <time.h> +#include <unistd.h> + +int main(void) +{ + struct pollfd fds[2]; + time_t before, now; + int ret; + + fds[0] = (struct pollfd){ .fd = 1, .events = POLLOUT, .revents = 0 }; + fds[1] = (struct pollfd){ .fd = 2, .events = POLLOUT, .revents = 0 }; + + ret = poll(fds, 2, -1); + assert(ret == 2); + assert(fds[0].revents == POLLOUT); + assert(fds[1].revents == POLLOUT); + + fds[0] = (struct pollfd){ .fd = 0, .events = POLLIN, .revents = 0 }; + time(&before); + ret = poll(fds, 1, 2000); + time(&now); + assert(ret == 0); + assert(now - before >= 2); + + sleep(1); + time(&now); + assert(now - before >= 3); + + return 0; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/preopen_populates.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/preopen_populates.c new file mode 100644 index 0000000000..58fe69254d --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/preopen_populates.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/read_file.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/read_file.c new file mode 100644 index 0000000000..cdd758158e --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/read_file.c @@ -0,0 +1,14 @@ +#include <assert.h> +#include <stdio.h> + +int main() +{ + FILE *file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + assert(fputc(c, stdout) != EOF); + c = fgetc(file); + } +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/read_file_twice.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/read_file_twice.c new file mode 100644 index 0000000000..c2504fb40e --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/read_file_twice.c @@ -0,0 +1,16 @@ +#include <assert.h> +#include <stdio.h> + +int main() +{ + for (int i = 0; i < 2; i++) { + FILE *file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + assert(fputc(c, stdout) != EOF); + c = fgetc(file); + } + } +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/stat.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/stat.c new file mode 100644 index 0000000000..7974c31d84 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/stat.c @@ -0,0 +1,54 @@ +#include <sys/stat.h> + +#include <assert.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> + +#define BASE_DIR "/sandbox" +#define OUTPUT_DIR BASE_DIR "/testdir" +#define PATH OUTPUT_DIR "/output.txt" +#define SIZE 500 + +int main(void) +{ + struct stat st; + int fd; + int ret; + off_t pos; + + (void) st; + ret = mkdir(OUTPUT_DIR, 0755); + assert(ret == 0); + + fd = open(PATH, O_CREAT | O_WRONLY, 0666); + assert(fd != -1); + + pos = lseek(fd, SIZE - 1, SEEK_SET); + assert(pos == SIZE - 1); + + ret = (int) write(fd, "", 1); + assert(ret == 1); + + ret = fstat(fd, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = close(fd); + assert(ret == 0); + + ret = access(PATH, R_OK); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = unlink(PATH); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == -1); + + return 0; +}
\ No newline at end of file diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/stdin.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/stdin.c new file mode 100644 index 0000000000..137a447602 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/stdin.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +int main(void) +{ + char x[32]; + + fgets(x, sizeof x, stdin); + fputs(x, stdout); + return 0; +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/symlink_escape.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/symlink_escape.c new file mode 100644 index 0000000000..20929d5193 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/symlink_escape.c @@ -0,0 +1,10 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> + +int main() +{ + FILE *file = fopen("/sandbox/subdir/outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/symlink_loop.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/symlink_loop.c new file mode 100644 index 0000000000..7256121c3b --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/symlink_loop.c @@ -0,0 +1,10 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> + +int main() +{ + FILE *file = fopen("/sandbox/subdir1/loop1", "r"); + assert(file == NULL); + assert(errno == ELOOP); +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/guests/write_file.c b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/write_file.c new file mode 100644 index 0000000000..5922c02763 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/guests/write_file.c @@ -0,0 +1,16 @@ +#include <assert.h> +#include <stdio.h> +#include <string.h> + +static char *message = "hello, file!"; + +int main() +{ + FILE *file = fopen("/sandbox/output.txt", "w"); + assert(file != NULL); + + int nwritten = fprintf(file, "%s", message); + assert(nwritten == strlen(message)); + + assert(fclose(file) == 0); +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/test_helpers/mod.rs b/third_party/rust/lucet-wasi-wasmsbx/tests/test_helpers/mod.rs new file mode 100644 index 0000000000..455928a92b --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/test_helpers/mod.rs @@ -0,0 +1,127 @@ +use failure::{bail, Error}; +use lucet_runtime::{DlModule, Limits, MmapRegion, Module, Region}; +use lucet_wasi::host::__wasi_exitcode_t; +use lucet_wasi::{self, WasiCtx, WasiCtxBuilder}; +use lucet_wasi_sdk::{CompileOpts, Link}; +use lucetc::{Lucetc, LucetcOpts}; +use std::fs::File; +use std::io::Read; +use std::os::unix::io::{FromRawFd, IntoRawFd}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use tempfile::TempDir; + +pub const LUCET_WASI_ROOT: &'static str = env!("CARGO_MANIFEST_DIR"); + +pub fn test_module_wasi<P: AsRef<Path>>(cfile: P) -> Result<Arc<dyn Module>, Error> { + let c_path = guest_file(&cfile); + wasi_test(c_path) +} + +pub fn guest_file<P: AsRef<Path>>(path: P) -> PathBuf { + let p = if path.as_ref().is_absolute() { + path.as_ref().to_owned() + } else { + Path::new(LUCET_WASI_ROOT) + .join("tests") + .join("guests") + .join(path) + }; + assert!(p.exists(), "test case source file {} exists", p.display()); + p +} + +pub fn wasi_test<P: AsRef<Path>>(file: P) -> Result<Arc<dyn Module>, Error> { + let workdir = TempDir::new().expect("create working directory"); + + let wasm_path = match file.as_ref().extension().and_then(|x| x.to_str()) { + Some("c") => { + // some tests are .c, and must be compiled/linked to .wasm we can run + let wasm_build = Link::new(&[file]) + .with_cflag("-Wall") + .with_cflag("-Werror") + .with_print_output(true); + + let wasm_file = workdir.path().join("out.wasm"); + wasm_build.link(wasm_file.clone())?; + + wasm_file + } + Some("wasm") | Some("wat") => { + // others are just wasm we can run directly + file.as_ref().to_owned() + } + Some(ext) => { + panic!("unknown test file extension: .{}", ext); + } + None => { + panic!("unknown test file, has no extension"); + } + }; + + wasi_load(&workdir, wasm_path) +} + +pub fn wasi_load<P: AsRef<Path>>( + workdir: &TempDir, + wasm_file: P, +) -> Result<Arc<dyn Module>, Error> { + let native_build = Lucetc::new(wasm_file).with_bindings(lucet_wasi::bindings()); + + let so_file = workdir.path().join("out.so"); + + native_build.shared_object_file(so_file.clone())?; + + let dlmodule = DlModule::load(so_file)?; + + Ok(dlmodule as Arc<dyn Module>) +} + +pub fn run<P: AsRef<Path>>(path: P, ctx: WasiCtx) -> Result<__wasi_exitcode_t, Error> { + let region = MmapRegion::create(1, &Limits::default())?; + let module = test_module_wasi(path)?; + + let mut inst = region + .new_instance_builder(module) + .with_embed_ctx(ctx) + .build()?; + + match inst.run("_start", &[]) { + // normal termination implies 0 exit code + Ok(_) => Ok(0), + Err(lucet_runtime::Error::RuntimeTerminated( + lucet_runtime::TerminationDetails::Provided(any), + )) => Ok(*any + .downcast_ref::<__wasi_exitcode_t>() + .expect("termination yields an exitcode")), + Err(e) => bail!("runtime error: {}", e), + } +} + +pub fn run_with_stdout<P: AsRef<Path>>( + path: P, + ctx: WasiCtxBuilder, +) -> Result<(__wasi_exitcode_t, String), Error> { + let (pipe_out, pipe_in) = nix::unistd::pipe()?; + + let ctx = unsafe { ctx.raw_fd(1, pipe_in) }.build()?; + + let exitcode = run(path, ctx)?; + + let mut stdout_file = unsafe { File::from_raw_fd(pipe_out) }; + let mut stdout = String::new(); + stdout_file.read_to_string(&mut stdout)?; + nix::unistd::close(stdout_file.into_raw_fd())?; + + Ok((exitcode, stdout)) +} + +/// Call this if you're having trouble with `__wasi_*` symbols not being exported. +/// +/// This is pretty hackish; we will hopefully be able to avoid this altogether once [this +/// issue](https://github.com/rust-lang/rust/issues/58037) is addressed. +#[no_mangle] +#[doc(hidden)] +pub extern "C" fn lucet_wasi_tests_internal_ensure_linked() { + lucet_wasi::hostcalls::ensure_linked(); +} diff --git a/third_party/rust/lucet-wasi-wasmsbx/tests/tests.rs b/third_party/rust/lucet-wasi-wasmsbx/tests/tests.rs new file mode 100644 index 0000000000..18cdf386d2 --- /dev/null +++ b/third_party/rust/lucet-wasi-wasmsbx/tests/tests.rs @@ -0,0 +1,405 @@ +mod test_helpers; + +use crate::test_helpers::{run, run_with_stdout, LUCET_WASI_ROOT}; +use lucet_wasi::{WasiCtx, WasiCtxBuilder}; +use std::fs::File; +use std::path::Path; +use tempfile::TempDir; + +#[test] +fn double_import() { + let ctx = WasiCtxBuilder::new(); + + let (exitcode, stdout) = run_with_stdout("duplicate_import.wat", ctx).unwrap(); + + assert_eq!(stdout, "duplicate import works!\n"); + assert_eq!(exitcode, 0); +} + +#[test] +fn hello() { + let ctx = WasiCtxBuilder::new().args(&["hello"]); + + let (exitcode, stdout) = run_with_stdout( + Path::new(LUCET_WASI_ROOT).join("examples").join("hello.c"), + ctx, + ) + .unwrap(); + + assert_eq!(exitcode, 0); + assert_eq!(&stdout, "hello, wasi!\n"); +} + +#[test] +fn hello_args() { + let ctx = WasiCtxBuilder::new().args(&["hello", "test suite"]); + + let (exitcode, stdout) = run_with_stdout( + Path::new(LUCET_WASI_ROOT).join("examples").join("hello.c"), + ctx, + ) + .unwrap(); + + assert_eq!(exitcode, 0); + assert_eq!(&stdout, "hello, test suite!\n"); +} + +#[test] +fn hello_env() { + let ctx = WasiCtxBuilder::new() + .args(&["hello", "test suite"]) + .env("GREETING", "goodbye"); + + let (exitcode, stdout) = run_with_stdout( + Path::new(LUCET_WASI_ROOT).join("examples").join("hello.c"), + ctx, + ) + .unwrap(); + + assert_eq!(exitcode, 0); + assert_eq!(&stdout, "goodbye, test suite!\n"); +} + +#[test] +fn exitcode() { + let ctx = WasiCtx::new(&["exitcode"]); + + let exitcode = run("exitcode.c", ctx).unwrap(); + + assert_eq!(exitcode, 120); +} + +#[test] +fn clock_getres() { + let ctx = WasiCtx::new(&["clock_getres"]); + + let exitcode = run("clock_getres.c", ctx).unwrap(); + + assert_eq!(exitcode, 0); +} + +#[test] +fn getrusage() { + let ctx = WasiCtx::new(&["getrusage"]); + + let exitcode = run("getrusage.c", ctx).unwrap(); + + assert_eq!(exitcode, 0); +} + +#[test] +fn gettimeofday() { + let ctx = WasiCtx::new(&["gettimeofday"]); + + let exitcode = run("gettimeofday.c", ctx).unwrap(); + + assert_eq!(exitcode, 0); +} + +#[test] +fn getentropy() { + let ctx = WasiCtx::new(&["getentropy"]); + + let exitcode = run("getentropy.c", ctx).unwrap(); + + assert_eq!(exitcode, 0); +} + +#[test] +fn stdin() { + use std::io::Write; + use std::os::unix::io::FromRawFd; + + let (pipe_out, pipe_in) = nix::unistd::pipe().expect("can create pipe"); + + let mut stdin_file = unsafe { File::from_raw_fd(pipe_in) }; + write!(stdin_file, "hello from stdin!").expect("pipe write succeeds"); + drop(stdin_file); + + let ctx = unsafe { WasiCtxBuilder::new().args(&["stdin"]).raw_fd(0, pipe_out) }; + + let (exitcode, stdout) = run_with_stdout("stdin.c", ctx).unwrap(); + + assert_eq!(exitcode, 0); + assert_eq!(&stdout, "hello from stdin!"); +} + +#[test] +fn preopen_populates() { + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + let preopen_dir = File::open(preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["preopen_populates"]) + .preopened_dir(preopen_dir, "/preopen") + .build() + .expect("can build WasiCtx"); + + let exitcode = run("preopen_populates.c", ctx).unwrap(); + + drop(tmpdir); + + assert_eq!(exitcode, 0); +} + +#[test] +fn write_file() { + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["write_file"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .expect("can build WasiCtx"); + + let exitcode = run("write_file.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + let output = std::fs::read(preopen_host_path.join("output.txt")).unwrap(); + + assert_eq!(output.as_slice(), b"hello, file!"); + + drop(tmpdir); +} + +#[test] +fn read_file() { + const MESSAGE: &'static str = "hello from file!"; + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + + std::fs::write(preopen_host_path.join("input.txt"), MESSAGE).unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["read_file"]) + .preopened_dir(preopen_dir, "/sandbox"); + + let (exitcode, stdout) = run_with_stdout("read_file.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + assert_eq!(&stdout, MESSAGE); + + drop(tmpdir); +} + +#[test] +fn read_file_twice() { + const MESSAGE: &'static str = "hello from file!"; + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + + std::fs::write(preopen_host_path.join("input.txt"), MESSAGE).unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["read_file_twice"]) + .preopened_dir(preopen_dir, "/sandbox"); + + let (exitcode, stdout) = run_with_stdout("read_file_twice.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + let double_message = format!("{}{}", MESSAGE, MESSAGE); + assert_eq!(stdout, double_message); + + drop(tmpdir); +} + +#[test] +fn cant_dotdot() { + const MESSAGE: &'static str = "hello from file!"; + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + + std::fs::write( + preopen_host_path.parent().unwrap().join("outside.txt"), + MESSAGE, + ) + .unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["cant_dotdot"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .unwrap(); + + let exitcode = run("cant_dotdot.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + drop(tmpdir); +} + +#[ignore] // needs fd_readdir +#[test] +fn notdir() { + const MESSAGE: &'static str = "hello from file!"; + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + + std::fs::write(preopen_host_path.join("notadir"), MESSAGE).unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["notdir"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .unwrap(); + + let exitcode = run("notdir.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + drop(tmpdir); +} + +#[test] +fn follow_symlink() { + const MESSAGE: &'static str = "hello from file!"; + + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + let subdir1 = preopen_host_path.join("subdir1"); + let subdir2 = preopen_host_path.join("subdir2"); + std::fs::create_dir_all(&subdir1).unwrap(); + std::fs::create_dir_all(&subdir2).unwrap(); + + std::fs::write(subdir1.join("input.txt"), MESSAGE).unwrap(); + + std::os::unix::fs::symlink("../subdir1/input.txt", subdir2.join("input_link.txt")).unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["follow_symlink"]) + .preopened_dir(preopen_dir, "/sandbox"); + + let (exitcode, stdout) = run_with_stdout("follow_symlink.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + assert_eq!(&stdout, MESSAGE); + + drop(tmpdir); +} + +#[test] +fn symlink_loop() { + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + let subdir1 = preopen_host_path.join("subdir1"); + let subdir2 = preopen_host_path.join("subdir2"); + std::fs::create_dir_all(&subdir1).unwrap(); + std::fs::create_dir_all(&subdir2).unwrap(); + + std::os::unix::fs::symlink("../subdir1/loop1", subdir2.join("loop2")).unwrap(); + std::os::unix::fs::symlink("../subdir2/loop2", subdir1.join("loop1")).unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["symlink_loop"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .unwrap(); + + let exitcode = run("symlink_loop.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + drop(tmpdir); +} + +#[test] +fn symlink_escape() { + const MESSAGE: &'static str = "hello from file!"; + + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + let subdir = preopen_host_path.join("subdir"); + std::fs::create_dir_all(&subdir).unwrap(); + + std::fs::write( + preopen_host_path.parent().unwrap().join("outside.txt"), + MESSAGE, + ) + .unwrap(); + std::os::unix::fs::symlink("../../outside.txt", subdir.join("outside.txt")).unwrap(); + + let preopen_dir = File::open(&preopen_host_path).unwrap(); + + let ctx = WasiCtxBuilder::new() + .args(&["symlink_escape"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .unwrap(); + + let exitcode = run("symlink_escape.c", ctx).unwrap(); + assert_eq!(exitcode, 0); + + drop(tmpdir); +} + +#[test] +fn pseudoquine() { + let examples_dir = Path::new(LUCET_WASI_ROOT).join("examples"); + let pseudoquine_c = examples_dir.join("pseudoquine.c"); + + let ctx = WasiCtxBuilder::new() + .args(&["pseudoquine"]) + .preopened_dir(File::open(examples_dir).unwrap(), "/examples"); + + let (exitcode, stdout) = run_with_stdout(&pseudoquine_c, ctx).unwrap(); + + assert_eq!(exitcode, 0); + + let expected = std::fs::read_to_string(&pseudoquine_c).unwrap(); + + assert_eq!(stdout, expected); +} + +#[test] +fn poll() { + let ctx = WasiCtxBuilder::new().args(&["poll"]).build().unwrap(); + let exitcode = run("poll.c", ctx).unwrap(); + assert_eq!(exitcode, 0); +} + +#[test] +fn stat() { + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + let preopen_dir = File::open(&preopen_host_path).unwrap(); + let ctx = WasiCtxBuilder::new() + .args(&["stat"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .expect("can build WasiCtx"); + let exitcode = run("stat.c", ctx).unwrap(); + assert_eq!(exitcode, 0); +} + +#[test] +fn fs() { + let tmpdir = TempDir::new().unwrap(); + let preopen_host_path = tmpdir.path().join("preopen"); + std::fs::create_dir(&preopen_host_path).unwrap(); + let preopen_dir = File::open(&preopen_host_path).unwrap(); + let ctx = WasiCtxBuilder::new() + .args(&["stat"]) + .preopened_dir(preopen_dir, "/sandbox") + .build() + .expect("can build WasiCtx"); + let exitcode = run("fs.c", ctx).unwrap(); + assert_eq!(exitcode, 0); +} |