├── .rustfmt.toml ├── docs ├── safety.md ├── architecture.md └── optimizations.md ├── examples ├── fallocate │ ├── output │ │ └── tmp.txt │ ├── Makefile │ ├── README.txt │ └── fallocate.c ├── rename │ ├── B.txt │ ├── Makefile │ └── rename.c ├── symlink │ ├── data │ │ ├── link │ │ └── tmp.txt │ ├── Makefile │ └── symlink.c ├── permissions_regression │ ├── log.txt │ ├── not_exist.txt │ ├── Makefile │ └── permissions.c ├── cat │ ├── data │ │ └── tmp.txt │ ├── Makefile │ └── cat.c ├── cp │ ├── data │ │ └── tmp.txt │ ├── output │ │ └── tmp.txt │ ├── Makefile │ └── cp.c ├── link │ ├── data │ │ ├── link.txt │ │ └── tmp.txt │ ├── Makefile │ └── link.c ├── renumber │ ├── data │ │ └── tmp.txt │ ├── Makefile │ └── renumber.c ├── setfl │ ├── data │ │ └── tmp.txt │ ├── Makefile │ └── setfl.c ├── stat │ ├── data │ │ └── tmp.txt │ ├── Makefile │ └── stat.c ├── sync │ ├── data │ │ └── tmp.txt │ ├── Makefile │ └── sync.c ├── cp_and_insert │ ├── data │ │ └── tmp.txt │ ├── output │ │ └── tmp.txt │ ├── Makefile │ └── cp_and_insert.c ├── ls │ ├── Makefile │ └── ls.c ├── hello │ ├── Makefile │ └── hello.c ├── mkdir │ ├── Makefile │ └── mkdir.c ├── raise │ ├── Makefile │ └── raise.c ├── rmdir │ ├── Makefile │ └── rmdir.c ├── sleep │ ├── Makefile │ └── sleep.c ├── random │ ├── Makefile │ └── random.c ├── client │ ├── client │ ├── Makefile │ ├── native_client │ │ └── client.c │ ├── client.c │ └── server │ │ └── server.c ├── Makefile ├── clock │ ├── Makefile │ └── clock.c ├── client_send_recv │ ├── Makefile │ ├── native_client │ │ └── client.c │ ├── client_send_recv.c │ └── server │ │ └── server.c ├── print_args_and_environ │ ├── Makefile │ └── print_args_and_environ.c └── Makefile.inc ├── rust-toolchain ├── src ├── tcb │ ├── verifier │ │ ├── external_specs │ │ │ ├── mod.rs │ │ │ ├── option.rs │ │ │ ├── result.rs │ │ │ ├── vec.rs │ │ │ └── paths.rs │ │ ├── mod.rs │ │ ├── spec.rs │ │ └── trace.rs │ ├── mod.rs │ ├── ffi.rs │ ├── os_specs │ │ └── platform │ │ │ ├── linux.rs │ │ │ └── macos.rs │ ├── path.rs │ ├── misc.rs │ └── sbox_mem.rs ├── stats │ ├── mod.rs │ ├── noop_instrumentation.rs │ ├── stats.rs │ └── timing.rs ├── types │ └── platform │ │ ├── mod.rs │ │ ├── macos.rs │ │ └── linux.rs ├── tests │ ├── mod.rs │ ├── unit_tests.rs │ ├── fuzz_trusted.rs │ ├── memwrite_experiment.rs │ └── integration_tests.rs ├── lib.rs ├── verifier_interface │ └── mod.rs ├── os │ └── platform │ │ ├── linux.rs │ │ └── macos.rs ├── path_resolution.rs ├── iov.rs ├── writeback.rs ├── fdmap.rs └── poll.rs ├── owned-components ├── Cargo.toml └── src │ └── lib.rs ├── Prusti.toml ├── tools └── fuzz-gen │ ├── src │ ├── span_overrider.rs │ ├── parse_quote_spanned.rs │ ├── spec_attribute_kind.rs │ ├── specifications │ │ ├── mod.rs │ │ └── json.rs │ ├── parse_closure_macro.rs │ └── rewriter.rs │ └── Cargo.toml ├── waverunner ├── Cargo.toml └── src │ ├── types.rs │ ├── waverunner.rs │ └── main.rs ├── .gitmodules ├── wave-macros └── Cargo.toml ├── Dockerfile ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── scan_for_trusted.sh ├── cbindgen.toml ├── README.md └── Makefile /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/safety.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/optimizations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/fallocate/output/tmp.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/rename/B.txt: -------------------------------------------------------------------------------- 1 | I'm a file! 2 | -------------------------------------------------------------------------------- /examples/symlink/data/link: -------------------------------------------------------------------------------- 1 | ./data/tmp.txt -------------------------------------------------------------------------------- /examples/permissions_regression/log.txt: -------------------------------------------------------------------------------- 1 | hello hello -------------------------------------------------------------------------------- /examples/permissions_regression/not_exist.txt: -------------------------------------------------------------------------------- 1 | hello hello -------------------------------------------------------------------------------- /examples/cat/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/cp/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/cp/output/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/link/data/link.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/link/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/renumber/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/setfl/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/stat/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/symlink/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/sync/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-03-15" 3 | -------------------------------------------------------------------------------- /examples/cp/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=cp 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/cp_and_insert/data/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents of the file! 2 | -------------------------------------------------------------------------------- /examples/ls/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=ls 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/cat/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=cat 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/cp_and_insert/output/tmp.txt: -------------------------------------------------------------------------------- 1 | This is the contents in the file! 2 | -------------------------------------------------------------------------------- /examples/hello/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=hello 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/link/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=link 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/mkdir/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=mkdir 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/raise/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=raise 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/rmdir/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=rmdir 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/setfl/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=setfl 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/sleep/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=sleep 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/stat/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=stat 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/sync/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=sync 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/random/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=random 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/rename/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=rename 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/symlink/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=symlink 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/fallocate/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=fallocate 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/renumber/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=renumber 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/client/client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/wave/master/examples/client/client -------------------------------------------------------------------------------- /examples/cp_and_insert/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=cp_and_insert 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /examples/permissions_regression/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=permissions 3 | 4 | include ../Makefile.inc 5 | -------------------------------------------------------------------------------- /src/tcb/verifier/external_specs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod option; 2 | pub mod paths; 3 | pub mod result; 4 | pub mod vec; 5 | -------------------------------------------------------------------------------- /examples/hello/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main() { 3 | printf("Hello, World!\n"); 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: clean_all 3 | clean_all: ./* 4 | @for file in $^ ; do \ 5 | make -C $${file} clean ; \ 6 | done 7 | -------------------------------------------------------------------------------- /examples/fallocate/README.txt: -------------------------------------------------------------------------------- 1 | uses posix_fallocate to generate an empty file of size = 100 bytes 2 | Observable effect: shoult create output/tmp.txt 3 | -------------------------------------------------------------------------------- /examples/rmdir/rmdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | rmdir("./remove_me"); 6 | printf("Done!!\n"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/tcb/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ffi; 2 | pub mod misc; 3 | pub mod os_specs; 4 | pub mod path; 5 | pub mod sbox_mem; 6 | #[cfg(any(feature = "verify", test))] 7 | pub mod verifier; 8 | -------------------------------------------------------------------------------- /src/stats/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod noop_instrumentation; 2 | #[cfg(all(not(feature = "verify")))] 3 | pub mod stats; 4 | // TODO: fix on mac 5 | #[cfg(all(not(feature = "verify")))] 6 | pub mod timing; 7 | -------------------------------------------------------------------------------- /examples/clock/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=clock 3 | 4 | include ../Makefile.inc 5 | 6 | %.wasm: %.c 7 | $(CC) $(CFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -lwasi-emulated-process-clocks $< -o $@ $(LDFLAGS) 8 | -------------------------------------------------------------------------------- /examples/client/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=client 3 | 4 | include ../Makefile.inc 5 | 6 | .PHONY: run 7 | run: $(TARGET) 8 | $(WASM2C_BIN_ROOT)/wasm2c-runner ./$< --homedir=. --netlist=TCP:127.0.0.1:36895 9 | -------------------------------------------------------------------------------- /examples/client_send_recv/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=client_send_recv 3 | 4 | include ../Makefile.inc 5 | 6 | .PHONY: run 7 | run: $(TARGET) 8 | $(WASM2C_BIN_ROOT)/wasm2c-runner ./$< --homedir=. --netlist=UDP:127.0.0.1:36895 9 | -------------------------------------------------------------------------------- /examples/rename/rename.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // int rename(const char *oldpath, const char *newpath); 4 | 5 | int main() { 6 | rename("A.txt", "B.txt"); 7 | printf("Done!\n"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /examples/sleep/sleep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | printf("Please wait for 2 seconds...\n"); 6 | sleep(2); 7 | printf("Thank you for waiting!\n"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /owned-components/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "owned-components" 3 | version = "0.1.0" 4 | 5 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6 | 7 | [dependencies] 8 | libc = "0.2" 9 | 10 | -------------------------------------------------------------------------------- /examples/print_args_and_environ/Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=print_args_and_environ 3 | 4 | include ../Makefile.inc 5 | 6 | run: print_args_and_environ 7 | $(WASM2C_BIN_ROOT)/wasm2c-runner ./print_args_and_environ --args="a b c" --env="CC=clang CXX=clang++" --homedir=. 8 | -------------------------------------------------------------------------------- /examples/raise/raise.c: -------------------------------------------------------------------------------- 1 | #include 2 | //#include 3 | //int raise(int sig); 4 | 5 | // This test doesn't really do much, all I'm expecting is that it doesn't crash 6 | int main() { 7 | __wasi_proc_raise(11); 8 | __wasi_sched_yield(); 9 | printf("Done!\n"); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /src/tcb/verifier/external_specs/option.rs: -------------------------------------------------------------------------------- 1 | use prusti_contracts::*; 2 | 3 | #[extern_spec] 4 | impl std::option::Option { 5 | #[pure] 6 | #[ensures(matches!(*self, Some(_)) == result)] 7 | fn is_some(&self) -> bool; 8 | 9 | #[pure] 10 | #[ensures(self.is_some() == !result)] 11 | fn is_none(&self) -> bool; 12 | } 13 | -------------------------------------------------------------------------------- /src/types/platform/mod.rs: -------------------------------------------------------------------------------- 1 | // TODO(emlaufer): Are there differences between x86_64 and aarch64 to handle? 2 | #[cfg_attr(target_os = "macos", path = "macos-aarch64.rs")] 3 | #[cfg_attr( 4 | all(target_os = "linux", target_arch = "x86_64"), 5 | path = "linux-x86_64.rs" 6 | )] 7 | mod platform_impl; 8 | 9 | pub use platform_impl::*; 10 | -------------------------------------------------------------------------------- /src/tcb/verifier/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "fuzz"))] 2 | pub(crate) mod external_specs; 3 | mod spec; 4 | #[cfg(not(feature = "fuzz"))] 5 | mod trace; 6 | // Re-export verifier to the crate 7 | #[cfg(not(feature = "fuzz"))] 8 | pub(crate) use self::external_specs::*; 9 | pub(crate) use self::spec::*; 10 | #[cfg(not(feature = "fuzz"))] 11 | pub(crate) use self::trace::*; 12 | -------------------------------------------------------------------------------- /Prusti.toml: -------------------------------------------------------------------------------- 1 | counterexample = false 2 | check_overflows = false 3 | assert_timeout = 0 4 | extra_verifier_args = [ "--checkTimeout=50", "--z3SaturationTimeout=500", "--numberOfParallelVerifiers=1" ] 5 | #extra_verifier_args = [ "--checkTimeout=0", "--qpSplitTimeout=0", "--z3SaturationTimeout=0", "--numberOfParallelVerifiers=1" ] 6 | enable_cache=true 7 | no_verify_deps = true # Don't verify macro generation code 8 | -------------------------------------------------------------------------------- /src/tcb/verifier/external_specs/result.rs: -------------------------------------------------------------------------------- 1 | use prusti_contracts::*; 2 | 3 | #[extern_spec] 4 | impl std::result::Result { 5 | #[pure] 6 | #[ensures(matches!(*self, Ok(_)) == result)] 7 | fn is_ok(&self) -> bool; 8 | 9 | #[pure] 10 | #[ensures(self.is_ok() == !result)] 11 | fn is_err(&self) -> bool; 12 | 13 | // #[requires(self.is_some())] 14 | // fn unwrap(self) -> T; 15 | 16 | // ... 17 | } 18 | -------------------------------------------------------------------------------- /examples/mkdir/mkdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | int main(int argc, char *argv[]) { 10 | struct stat st = {0}; 11 | if (stat("test_dir", &st) == -1) { 12 | int res = mkdir("test_dir", 0700); 13 | printf("mkdir result = %d %d %s\n", res, errno, strerror(errno)); 14 | } 15 | printf("Done!\n"); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /examples/sync/sync.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | // These three calls are all unobservable (or close to unobservable) 7 | // Really I just expect all these calls to succeed 8 | int main() { 9 | int fd = open("./data/tmp.txt", O_RDONLY); 10 | fsync(fd); 11 | fdatasync(fd); 12 | posix_fadvise(fd, 0, 10, POSIX_FADV_SEQUENTIAL); 13 | close(fd); 14 | printf("Done!\n"); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /examples/fallocate/fallocate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | //printf("Hello, World!\n"); 6 | //file = fopen("./data/tmp.txt", "r"); 7 | int fd = open("./output/tmp.txt", O_WRONLY | O_CREAT | S_IRUSR | S_IWUSR); 8 | 9 | //int fallocate(int fd, int mode, off_t offset, off_t len); 10 | // create an empty file of size 100 bytes 11 | posix_fallocate(fd, 0, 100); 12 | printf("Done!\n"); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /examples/permissions_regression/permissions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | printf("Hello, World!\n"); 7 | int fd = open("log.txt", O_RDWR | O_CREAT); 8 | char buf[] = "hello hello"; 9 | int ret = write(fd, buf, 11); 10 | printf("write returns %d\n", ret); 11 | close(fd); 12 | 13 | FILE *fp = fopen("not_exist.txt", "w"); 14 | fwrite(buf, 1, 11, fp); 15 | 16 | return 0; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/span_overrider.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | 3 | /// Override all span information 4 | pub struct SpanOverrider { 5 | span: Span 6 | } 7 | 8 | impl SpanOverrider { 9 | pub fn new(span: Span) -> Self { 10 | SpanOverrider { 11 | span 12 | } 13 | } 14 | } 15 | 16 | impl syn::visit_mut::VisitMut for SpanOverrider { 17 | fn visit_span_mut(&mut self, span: &mut Span) { 18 | *span = self.span; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /waverunner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "waverunner" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | dlopen = "0.1.8" 10 | dlopen_derive = "0.1.4" 11 | 12 | # For mmap, munmap, and mprotect wrappers 13 | libc = "0.2" 14 | 15 | # Runtime 16 | wave = { path = ".." } 17 | 18 | # Arg parsing 19 | clap = "3.0.14" 20 | 21 | # Error handling 22 | anyhow = "1.0.53" 23 | -------------------------------------------------------------------------------- /examples/renumber/renumber.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | int main() { 7 | // 1. open file 8 | int fd = open("./data/tmp.txt", O_RDONLY); 9 | int new_fd = 7; 10 | ssize_t count; 11 | 12 | char buffer[1024]; 13 | 14 | // 2. renumber file 15 | __wasi_fd_renumber(fd, new_fd); 16 | 17 | // 3. cat file 18 | while ((count = read(new_fd, buffer, sizeof(buffer))) != 0) 19 | write(1, buffer, count); //stdout 20 | 21 | 22 | printf("Done!\n"); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /examples/symlink/symlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // int symlink(const char *target, const char *linkpath); 5 | // ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsiz); 6 | 7 | 8 | int main() { 9 | symlink("./data/tmp.txt","./data/link"); 10 | char buf[1024]; 11 | readlink("./data/link", &buf, 1024); 12 | printf("Contents of symlink = %s\n", buf); 13 | symlink("./data/tmp.txt","./data/link2"); 14 | unlink("./data/link2"); 15 | printf("Done!\n"); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /src/stats/noop_instrumentation.rs: -------------------------------------------------------------------------------- 1 | // noop functions useful in conditional compilation 2 | // see tcb/os_specs/linux or wasm2c_frontend for details 3 | 4 | #[inline] 5 | pub fn start_timer() -> u64 { 6 | 0 7 | } 8 | 9 | #[inline] 10 | pub fn stop_timer() -> u64 { 11 | 0 12 | } 13 | 14 | #[inline] 15 | pub fn push_hostcall_result(_name: &str, _start: u64, _end: u64) {} 16 | 17 | #[inline] 18 | pub fn push_syscall_result(_name: &str, _start: u64, _end: u64) {} 19 | 20 | pub fn output_hostcall_perf_results() {} 21 | pub fn output_syscall_perf_results() {} 22 | -------------------------------------------------------------------------------- /examples/print_args_and_environ/print_args_and_environ.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern char **environ; 4 | 5 | 6 | int main(int argc, char* argv[]){ 7 | //1. print commandline args 8 | printf("Number of arguments passed: %d\n", argc); 9 | 10 | for(int counter=0;counter 2 | #include 3 | 4 | 5 | 6 | // This test just prints 8 random hex butes 7 | int main(void){ 8 | int length = 8; 9 | char str[] = "0123456789ABCDEF"; 10 | 11 | unsigned char buf[8]; 12 | unsigned char outbuf[9]; 13 | getentropy(buf, 8); 14 | 15 | // translate to hex 16 | for (int i = 0; i < 8; i++){ 17 | int index = buf[i] % 16; 18 | outbuf[i] = str[buf[i] % 16]; 19 | } 20 | outbuf[8] = '\0'; 21 | printf("outbuf = %s\n", outbuf); 22 | printf("Done!\n"); 23 | 24 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "prusti-dev"] 2 | path = prusti-dev 3 | url = https://github.com/PLSysSec/prusti-dev.git 4 | [submodule "wasm-runtime-benchmarks"] 5 | path = wasm-runtime-benchmarks 6 | url = git@github.com:PLSysSec/wasm-runtime-benchmarks.git 7 | [submodule "wasix"] 8 | path = wasix 9 | url = git@github.com:zzjas/wasix.git 10 | [submodule "tools/wasm2c_sandbox_compiler"] 11 | path = tools/wasm2c_sandbox_compiler 12 | url = git@github.com:PLSysSec/wasm2c_sandbox_compiler.git 13 | [submodule "tools/wasi-sdk"] 14 | path = tools/wasi-sdk 15 | url = git@github.com:PLSysSec/wasi-sdk.git 16 | -------------------------------------------------------------------------------- /examples/link/link.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // create a link to data/tmp.txt and then cat the link 6 | int main() { 7 | // 1. create link 8 | link("./data/tmp.txt", "./data/link.txt"); 9 | 10 | // 2. open file 11 | int fd = open("./data/link.txt", O_RDONLY); 12 | int new_fd = 7; 13 | ssize_t count; 14 | 15 | char buffer[1024]; 16 | 17 | 18 | // 3. cat file 19 | while ((count = read(fd, buffer, sizeof(buffer))) != 0) 20 | write(1, buffer, count); //stdout 21 | 22 | 23 | printf("Done!\n"); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod fuzz_trusted; 2 | mod unit_tests; 3 | mod integration_tests; 4 | 5 | //mod test_gen_trace; 6 | #[cfg(test)] 7 | mod test_generator; 8 | 9 | use crate::runtime::fresh_ctx; 10 | //use crate::types::Stat; 11 | //use crate::tcb::verifier::*; 12 | use crate::types::VmCtx; 13 | use quickcheck::{Arbitrary, Gen}; 14 | 15 | /// Any common initialization for the tests (e.g. changing the working directory) 16 | pub fn init() { 17 | // this will only actually happen once, but just call it everytime 18 | // it will fail but that is fine... 19 | std::env::set_current_dir("./fuzz-dir"); 20 | } 21 | -------------------------------------------------------------------------------- /tools/fuzz-gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuzz_gen" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # impl syn::Parse for Signature was added in 1.0.43 10 | syn = { version = "^1.0.43", features = ["full", "extra-traits", "visit-mut", "parsing", "printing"] } 11 | quote = "1.0" 12 | proc-macro2 = "1.0" 13 | uuid = { version = "0.8", features = ["v4", "serde"] } 14 | serde_json = "1.0" 15 | serde = { version = "1.0", features = ["derive"] } 16 | #prusti-utils = { path = "../prusti-utils" } 17 | -------------------------------------------------------------------------------- /examples/ls/ls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "string.h" 3 | #include 4 | #include 5 | 6 | void do_ls(char* path) 7 | { 8 | printf("I'm doing an ls(%s)!\n", path); 9 | 10 | DIR *d; 11 | struct dirent *dir; 12 | d = opendir(path); 13 | if (d) 14 | { 15 | while ((dir = readdir(d)) != NULL) 16 | { 17 | printf("%s ", dir->d_name); 18 | } 19 | closedir(d); 20 | } else { 21 | printf("d not valid! errno: %d", errno); 22 | } 23 | printf("\n"); 24 | } 25 | 26 | int main() { 27 | do_ls("."); 28 | printf("Done!\n"); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /waverunner/src/types.rs: -------------------------------------------------------------------------------- 1 | use dlopen::wrapper::{Container, WrapperApi}; 2 | use wave::types::{Netlist, VmCtx}; 3 | 4 | 5 | #[derive(Debug)] 6 | pub struct WaveConfig { 7 | pub module_path: String, 8 | pub homedir: String, 9 | pub netlist: Netlist, 10 | pub args: Vec, 11 | pub argc: usize, 12 | pub env: Vec, 13 | pub envc: usize, 14 | } 15 | 16 | // #[derive(Debug)] 17 | pub struct WaveSandbox { 18 | pub module: Container, 19 | pub vmctx: VmCtx, 20 | pub linmem: *mut u8, 21 | } 22 | 23 | #[derive(WrapperApi, Debug)] 24 | pub struct Wasm2cBinary { 25 | w2c__start: unsafe extern "C" fn() -> i32, 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/setfl/setfl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int main() { 6 | int fd = open("./data/tmp.txt", O_RDONLY); 7 | 8 | int oldflags = fcntl (fd, F_GETFL, 0); 9 | /* If reading the flags failed, return error indication now. */ 10 | if (oldflags == -1) 11 | return -1; 12 | printf("oldflags = %x\n", oldflags); 13 | /* Set just the flag we want to set. */ 14 | oldflags |= O_APPEND; 15 | /* Store modified flag word in the descriptor. */ 16 | fcntl (fd, F_SETFL, oldflags); 17 | 18 | int newflags = fcntl (fd, F_GETFL, 0); 19 | printf("newflags = %x\n", newflags); 20 | printf("Done!\n"); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /wave-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wave-macros" 3 | version = "0.1.72" 4 | edition = "2018" 5 | license = "MIT" 6 | description = "Rust macros for the Wave verified Wasm runtime" 7 | homepage = "https://github.com/enjhnsn2/extra_args" 8 | repository = "https://github.com/enjhnsn2/extra_args" 9 | readme = "README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [dependencies] 18 | proc-macro2 = { version = "1.0", features = ["nightly"] } 19 | quote = "1.0" 20 | syn = { version = "1.0.76", features = ["full", "fold", "extra-traits"] } 21 | 22 | 23 | [features] 24 | enable = [] # enables extra args (used to enable/disable macro at compiletime) 25 | -------------------------------------------------------------------------------- /examples/clock/clock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) { 10 | clock_t start = clock(); 11 | clock_t end = clock(); 12 | printf("time = %lld\n", end - start); 13 | // __imported_wasi_snapshot_preview1_clock_res_get(); 14 | struct timespec res_get; 15 | int result = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &res_get); 16 | printf("result = %d res_get.tv_sec = %lld res_get.tv_nsec = %ld\n", result, res_get.tv_sec, res_get.tv_nsec); 17 | 18 | // struct stat st = {0}; 19 | // if (stat("test_dir", &st) == -1) { 20 | // printf("mkdir result = %d %d %s\n",mkdir("test_dir", 0700), errno, strerror(errno)); 21 | // } 22 | printf("Done!\n"); 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | dead_code, 3 | unused_imports, 4 | unused_variables, 5 | unused_must_use, 6 | unused_comparisons, 7 | unused_parens 8 | )] 9 | 10 | use wave_macros::{external_call, external_method, with_ghost_var}; 11 | 12 | extern crate prusti_contracts; 13 | 14 | mod fdmap; 15 | #[cfg(not(feature = "verify"))] // TODO: verify this final ffi layer 16 | pub mod lucet_frontend; 17 | mod os; 18 | pub mod runtime; 19 | pub mod stats; 20 | pub mod tcb; 21 | #[cfg(not(feature = "verify"))] 22 | mod tests; 23 | pub mod types; 24 | pub mod verifier_interface; 25 | #[cfg(not(feature = "verify"))] // TODO: verify this final ffi layer 26 | pub mod wasm2c_frontend; 27 | mod writeback; 28 | // #[with_ghost_var(trace: &mut Trace)] 29 | mod iov; 30 | mod path_resolution; 31 | mod poll; 32 | mod wrappers; 33 | //pub mod setup_teardown; 34 | -------------------------------------------------------------------------------- /examples/cp/cp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char buffer[1024]; 10 | int files[2]; 11 | ssize_t count; 12 | 13 | /* Check for insufficient parameters */ 14 | //if (argc < 3) 15 | // return -1; 16 | files[0] = open("./data/tmp.txt", O_RDONLY); 17 | if (files[0] == -1) /* Check if file opened */ 18 | return -1; 19 | files[1] = open("./output/tmp.txt", O_RDONLY | O_WRONLY | O_CREAT | S_IRUSR | S_IWUSR); 20 | if (files[1] == -1) /* Check if file opened (permissions problems ...) */ 21 | { 22 | close(files[0]); 23 | return -1; 24 | } 25 | 26 | while ((count = read(files[0], buffer, sizeof(buffer))) != 0) 27 | write(files[1], buffer, count); 28 | 29 | return 0; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /examples/cat/cat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | FILE *file; 8 | int chr; 9 | /* 10 | int count; 11 | 12 | for(count = 1; count < argc; count++) { 13 | if((file = fopen(argv[count], "r")) == NULL) { 14 | fprintf(stderr, "%s: %s : %s\n", argv[0], argv[count], 15 | strerror(errno)); 16 | continue; 17 | } 18 | while((chr = getc(file)) != EOF) 19 | fprintf(stdout, "%c", chr); 20 | fclose(file); 21 | }*/ 22 | 23 | // For now, just hardcode the path 24 | file = fopen("./data/tmp.txt", "r"); 25 | if (file == NULL) { 26 | printf("fopen result: %s\n", strerror(errno)); 27 | } 28 | //printf("file = %d", file); 29 | while((chr = getc(file)) != EOF) 30 | fprintf(stdout, "%c", chr); 31 | fclose(file); 32 | 33 | exit(0); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/parse_quote_spanned.rs: -------------------------------------------------------------------------------- 1 | /// Same as `parse_quote!`, but applies a given span to all tokens originating within 2 | /// the macro invocation. 3 | /// 4 | /// ``` 5 | /// # use proc_macro2::Span; 6 | /// # use quote::quote_spanned; 7 | /// # 8 | /// # const IGNORE_TOKENS: &'static str = stringify! { 9 | /// let span = /* ... */; 10 | /// # }; 11 | /// # let span = Span::call_site(); 12 | /// # let init = 0; 13 | /// 14 | /// // On one line, use parentheses. 15 | /// let expr: syn::Expr = parse_quote_spanned!(span=> my_identifier); 16 | /// 17 | /// // On multiple lines, place the span at the top and use braces. 18 | /// let mut struct: syn::ItemStruct = parse_quote_spanned! {span=> 19 | /// struct MyStruct {} 20 | /// }; 21 | /// ``` 22 | #[macro_export] 23 | macro_rules! parse_quote_spanned { 24 | ($($tt:tt)*) => {{ 25 | let spanned_tokens = quote::quote_spanned!($($tt)*); 26 | syn::parse_quote!(#spanned_tokens) 27 | }}; 28 | } 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | # Full Docker build will take 15-20 minutes 3 | 4 | # Misc dependencies 5 | RUN apt-get update 6 | RUN apt-get install -y curl git unzip build-essential pkg-config libssl-dev cmake ninja-build clang 7 | 8 | # Java (for Prusti) 9 | RUN apt-get install default-jre -y 10 | 11 | # Rust 12 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 13 | ENV PATH="/root/.cargo/bin:${PATH}" 14 | RUN cargo install --force cbindgen 15 | 16 | # add ssh private key so we can download private repos 17 | # TODO: remove once repos are public 18 | # TODO: once ssh key is stripped out, add prebuilt container to repo 19 | ARG SSH_PRIVATE_KEY 20 | RUN mkdir /root/.ssh/ 21 | RUN echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa 22 | RUN chmod 0600 /root/.ssh/id_rsa 23 | RUN ssh-keyscan github.com > /root/.ssh/known_hosts 24 | 25 | # WaVe 26 | RUN git clone git@github.com:PLSysSec/wave.git 27 | WORKDIR /wave 28 | RUN make bootstrap 29 | RUN make build 30 | 31 | -------------------------------------------------------------------------------- /src/tests/unit_tests.rs: -------------------------------------------------------------------------------- 1 | // use super::*; 2 | // use crate::runtime::*; 3 | // use crate::types::*; 4 | // use crate::wrappers::*; 5 | // use std::time::Instant; 6 | 7 | // // some basic sanity tests 8 | // #[cfg(test)] 9 | // #[test] 10 | // fn test_time_get() -> RuntimeResult<()> { 11 | // let mut ctx = fresh_ctx(String::from(".")); 12 | // let ret = wasi_clock_time_get(&mut ctx, ClockId::Realtime, Timestamp::new(0))?; 13 | // let reference = Instant::now(); 14 | 15 | // assert_ne!(ret, Timestamp::new(0)); 16 | // assert_eq!(ctx.errno, RuntimeError::Success); 17 | // Ok(()) 18 | // } 19 | 20 | // #[cfg(test)] 21 | // #[test] 22 | // fn test_res_get() -> RuntimeResult<()> { 23 | // let mut ctx = fresh_ctx(String::from(".")); 24 | // let ret = wasi_clock_res_get(&mut ctx, ClockId::Realtime)?; 25 | // let reference = Instant::now(); 26 | 27 | // assert_ne!(ret, Timestamp::new(0)); 28 | // assert_eq!(ctx.errno, RuntimeError::Success); 29 | // Ok(()) 30 | // } 31 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/spec_attribute_kind.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | /// This type identifies one of the procedural macro attributes of Prusti 4 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 5 | pub enum SpecAttributeKind { 6 | Requires, 7 | Ensures, 8 | AfterExpiry, 9 | AfterExpiryIf, 10 | Pure, 11 | Trusted, 12 | Predicate, 13 | } 14 | 15 | impl TryFrom for SpecAttributeKind { 16 | type Error = String; 17 | 18 | fn try_from(name: String) -> Result { 19 | match name.as_str() { 20 | "requires" => Ok(SpecAttributeKind::Requires), 21 | "ensures" => Ok(SpecAttributeKind::Ensures), 22 | "after_expiry" => Ok(SpecAttributeKind::AfterExpiry), 23 | "after_expiry_if" => Ok(SpecAttributeKind::AfterExpiryIf), 24 | "pure" => Ok(SpecAttributeKind::Pure), 25 | "trusted" => Ok(SpecAttributeKind::Trusted), 26 | "predicate" => Ok(SpecAttributeKind::Predicate), 27 | _ => Err(name), 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wave" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | name = "wave" 9 | crate_type = ["dylib", "rlib"] 10 | 11 | [dependencies] 12 | system-call = { git = "https://github.com/emlaufer/system-call.rs.git", branch = "master" } 13 | prusti-contracts = { path = "prusti-dev/prusti-contracts" } 14 | libc = "0.2" 15 | trace = "*" 16 | #extra_args = "=0.1.69" 17 | wave-macros = { path = "wave-macros"} 18 | owned-components = { path = "owned-components"} 19 | paste = "1.0" # used in syscall function generation 20 | 21 | # Used for debugging 22 | log = "0.4.14" 23 | env_logger = "0.8.0" 24 | 25 | quickcheck="1.0.3" 26 | quickcheck_macros = "1.0.0" 27 | # Used for computing stats on benchmarks 28 | statistical = "1.0.0" 29 | 30 | #[dev-dependencies] 31 | regex = "1" 32 | 33 | 34 | [target.'cfg(target_os = "macos")'.dependencies] 35 | mach2 = "0.4.0" 36 | security-framework-sys = "2.4.2" 37 | 38 | [features] 39 | verify = ["wave-macros/enable"] # feature for when we're verifying 40 | time_syscalls = [] 41 | time_hostcalls = [] 42 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/specifications/mod.rs: -------------------------------------------------------------------------------- 1 | /// The following grammar defines Prusti expressions (this is an LL(finite) grammar): 2 | /// assertion ::= prusti_expr ; 3 | /// pledge ::= pledge_lhs, ",", prusti_expr ; 4 | /// pledge_lhs ::= [ ? actual rust expression ?, "=>" ], prusti_expr ; 5 | /// 6 | /// prusti_expr ::= conjunction, [ "==>", prusti_expr ] ; 7 | /// conjunction ::= entailment, { "&&", entailment } ; 8 | /// entailment ::= primary | ? actual rust expression ?, [ "|=", [ "|", ? args as parsed by syn2 ?, "|" ], "[", [ ( requires | ensures ), { ",", ( requires | ensures ) } ], "]" ] ; 9 | /// primary ::= "(", prusti_expr, ")" 10 | /// | "forall", "(", "|", ? one or more args as parsed by syn2 ?, "|", prusti_expr, [ ",", "triggers", "=", ? array as parsed by syn2 ? ] ")" 11 | /// ; 12 | /// requires ::= "requires", "(", prusti_expr, ")" ; 13 | /// ensures ::= "ensures", "(", prusti_expr, ")" ; 14 | /// 15 | /// This grammar doesn't yet handle the unintuitive precedence difference between `&&` and `||` operators. 16 | 17 | pub mod common; 18 | pub mod json; 19 | pub mod untyped; 20 | pub mod preparser; 21 | 22 | pub use common::SpecType; 23 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | # Lint code with rustfmt, report an error if it needs to be run. 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install rustfmt 16 | run: rustup component add rustfmt 17 | - name: Run rustfmt and check there's no difference 18 | run: cargo fmt -- --check 19 | 20 | # build: 21 | # runs-on: ubuntu-latest 22 | # steps: 23 | # - uses: actions/checkout@v2 24 | # - uses: actions-rs/toolchain@v1 25 | # with: 26 | # profile: minimal 27 | # toolchain: stable 28 | # override: true 29 | # - uses: actions-rs/cargo@v1 30 | # with: 31 | # command: check 32 | 33 | # test: 34 | # runs-on: ubuntu-latest 35 | # steps: 36 | # - uses: actions/checkout@v2 37 | # - uses: actions-rs/toolchain@v1 38 | # with: 39 | # profile: minimal 40 | # toolchain: stable 41 | # override: true 42 | # - uses: actions-rs/cargo@v1 43 | # with: 44 | # command: test 45 | -------------------------------------------------------------------------------- /src/tests/fuzz_trusted.rs: -------------------------------------------------------------------------------- 1 | use crate::os; 2 | use crate::runtime::fresh_ctx; 3 | use crate::types::{SboxPtr, VmCtx}; 4 | use quickcheck::{QuickCheck, TestResult}; 5 | use quickcheck_macros; 6 | 7 | // impl Arbitrary for VmCtx { 8 | // fn arbitrary(g: &mut Gen) -> Point { 9 | // VmCtx { 10 | // mem: Vec::arbitrary(g), 11 | // memlen, 12 | // fdmap, 13 | // homedir, 14 | // errno: Success, 15 | // arg_buffer, 16 | // argc, 17 | // env_buffer, 18 | // envc, 19 | // } 20 | // // Point { 21 | // // x: i32::arbitrary(g), 22 | // // y: i32::arbitrary(g), 23 | // // } 24 | // } 25 | // } 26 | 27 | /*#[quickcheck_macros::quickcheck] 28 | fn check_memcpy_to_sandbox(dst: SboxPtr, src: Vec, n: u32) -> TestResult { 29 | let mut ctx = fresh_ctx(".".to_string()); 30 | let old_memlen = ctx.memlen; 31 | if !(src.len() == (n as usize)) { 32 | return TestResult::discard(); 33 | } 34 | if !ctx.fits_in_lin_mem(dst, n) { 35 | return TestResult::discard(); 36 | } 37 | 38 | let r = ctx.memcpy_to_sandbox(dst, &src, n); 39 | TestResult::from_bool(old_memlen == ctx.memlen) 40 | }*/ 41 | -------------------------------------------------------------------------------- /src/tcb/verifier/external_specs/vec.rs: -------------------------------------------------------------------------------- 1 | use prusti_contracts::*; 2 | use std::vec::Vec; 3 | 4 | #[extern_spec] 5 | impl Vec { 6 | #[ensures(result.len() == 0)] 7 | #[ensures(result.capacity() == 0)] 8 | fn new() -> Vec; 9 | 10 | #[pure] 11 | fn len(&self) -> usize; 12 | 13 | #[ensures(self.len() == old(self.len()) + 1)] 14 | #[ensures(self.capacity() >= old(self.capacity()))] 15 | fn push(&mut self, value: T); 16 | 17 | #[ensures(self.len() == 0)] 18 | fn clear(&mut self); 19 | 20 | #[pure] 21 | fn capacity(&self) -> usize; 22 | 23 | #[ensures(self.capacity() >= old(self.len() + additional))] 24 | #[ensures(self.len() == old(self.len()))] 25 | fn reserve_exact(&mut self, additional: usize); 26 | 27 | #[pure] 28 | fn as_slice(&self) -> &[T]; 29 | 30 | // #[pure] 31 | // fn as_slice(&self) -> &[T]; 32 | 33 | // #[pure] 34 | // fn as_mut_slice(&mut self) -> &mut [T]; 35 | 36 | // #[pure] 37 | // #[requires (index < MAX_SBOX_FDS )] 38 | // // #[requires(0 <= index && index < self.len())] 39 | // // #[ensures(*result == old(self.lookup(index)))] 40 | // pub fn get(&self, index: usize) -> &T { 41 | // self.get(index).unwrap() 42 | // } 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | log/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | # MSVC Windows builds of rustc generate these, which store debugging information 15 | *.pdb 16 | 17 | # Various files generated by wave examples (in ./examples) 18 | *.wasm.c 19 | *.wasm.h 20 | *.wasm 21 | 22 | 23 | examples/cat/cat 24 | examples/clock/clock 25 | examples/cp/cp 26 | examples/cp_and_insert/cp_and_insert 27 | examples/fallocate/fallocate 28 | examples/hello/hello 29 | examples/link/link 30 | examples/ls/ls 31 | examples/mkdir/mkdir 32 | examples/permissions_regression/permissions 33 | examples/print_args_and_environ/print_args_and_environ 34 | examples/raise/raise 35 | examples/random/random 36 | examples/rename/rename 37 | examples/renumber/renumber 38 | examples/rmdir/rmdir 39 | examples/setfl/setfl 40 | examples/sleep/sleep 41 | examples/stat/stat 42 | examples/symlink/symlink 43 | examples/sync/sync 44 | examples/client/client 45 | examples/client/server/server 46 | examples/client_send_recv/client_send_recv 47 | examples/client_send_recv/server/server 48 | 49 | -------------------------------------------------------------------------------- /src/stats/stats.rs: -------------------------------------------------------------------------------- 1 | use crate::stats::timing::{ResultsType, HOSTCALL_RESULTS, SYSCALL_RESULTS}; 2 | use statistical::mean; 3 | use statistical::univariate::geometric_mean; 4 | use std::fs::File; 5 | use std::io::Write; 6 | 7 | // better implementation of geomean 8 | // every rust library screws this up by not controlling for overflow using the log trick 9 | pub fn geomean(arr: &[f64]) -> f64 { 10 | f64::exp(arr.iter().map(|x| f64::ln(*x)).sum()) 11 | } 12 | 13 | //Elk is 2.1 GHz 14 | pub fn output_hostcall_perf_results() { 15 | let mut f = File::create("./hostcall_results.txt").expect("Unable to open file"); 16 | 17 | HOSTCALL_RESULTS.with(|r| { 18 | for (k, v) in r.borrow().iter() { 19 | if !v.is_empty() { 20 | let mean = mean(v); 21 | let geomean = geometric_mean(v); 22 | writeln!(f, "{:?},{:?},{:?},{:?}", k, v.len(), mean, geomean); 23 | } 24 | } 25 | }); 26 | } 27 | 28 | pub fn output_syscall_perf_results() { 29 | let mut f = File::create("./syscall_results.txt").expect("Unable to open file"); 30 | 31 | SYSCALL_RESULTS.with(|r| { 32 | for (k, v) in r.borrow().iter() { 33 | if !v.is_empty() { 34 | let mean = mean(v); 35 | let geomean = geometric_mean(v); 36 | writeln!(f, "{:?},{:?},{:?},{:?}", k, v.len(), mean, geomean); 37 | } 38 | } 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /src/tcb/ffi.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use libc::{c_char, strlen}; 3 | use prusti_contracts::*; 4 | use std::ffi::CStr; 5 | 6 | /// To get wasm2c ffi working, we need to pass a VmCtx pointer back and forth 7 | /// from C to Rust and back again. 8 | /// The actual pointer that wasm2c gives us has a second layer of indrection 9 | /// so we deref it twice to get the vmctx, then return a reference to that VmCtx 10 | #[trusted] 11 | pub fn ptr_to_ref(ctx: *const *mut VmCtx) -> &'static mut VmCtx { 12 | if ctx.is_null() { 13 | panic!("null ctx") 14 | } 15 | unsafe { &mut **ctx } 16 | } 17 | 18 | #[trusted] 19 | pub fn transmut_netlist(nl: *const Netlist) -> Netlist { 20 | if nl.is_null() { 21 | panic!("null netlist") 22 | } 23 | unsafe { *nl } 24 | } 25 | 26 | #[trusted] 27 | pub fn ffi_load_vec(ptr: *mut u8, len: usize) -> Vec { 28 | unsafe { Vec::from_raw_parts(ptr, len, len) } 29 | } 30 | 31 | #[trusted] 32 | pub fn ffi_load_cstr(ptr: *const c_char) -> &'static str { 33 | unsafe { CStr::from_ptr(ptr).to_str().unwrap() } 34 | } 35 | 36 | #[trusted] 37 | pub fn ffi_load_cstr_as_vec(ptr: *mut u8) -> Vec { 38 | // The plus one adds the null byte at the end since strlen normally 39 | // does not count it 40 | let mut len = unsafe { strlen(ptr as *const i8) }; 41 | // All non-zero length strings in C are null-terminated 42 | if len != 0 { 43 | len += 1 44 | } 45 | unsafe { Vec::from_raw_parts(ptr, len, len) } 46 | } 47 | -------------------------------------------------------------------------------- /src/tcb/verifier/external_specs/paths.rs: -------------------------------------------------------------------------------- 1 | use owned_components::*; 2 | use prusti_contracts::*; 3 | 4 | // #[extern_spec] 5 | // impl std::path::PathBuf { 6 | // #[pure] 7 | // // #[ensures(matches!(*self, Some(_)) == result)] 8 | // fn is_relative(&self) -> bool; 9 | 10 | // // #[pure] 11 | // // #[ensures(self.is_some() == !result)] 12 | // // fn is_none(&self) -> bool; 13 | // } 14 | 15 | // #[extern_spec] 16 | // impl OwnedComponents { 17 | // #[pure] 18 | // fn len(&self) -> usize; 19 | 20 | // // #[ensures(result.len() == 0)] 21 | // pub fn new() -> OwnedComponents; 22 | 23 | // //pub fn as_path(&self) -> &Path; 24 | 25 | // //pub fn parse(p: PathBuf) -> Self; 26 | 27 | // // #[pure] 28 | // // #[requires(idx < self.len())] 29 | // pub fn lookup(&self, idx: usize); 30 | 31 | // // #[ensures(self.len() == old(self.len()) + 1)] 32 | // // #[ensures(self.lookup(old(self.len())) == old(value))] 33 | // // #[ensures(forall(|i: usize| (i < old(self.len())) ==> 34 | // // self.lookup(i) == old(self.lookup(i))))] 35 | // pub fn push(&mut self, value: OwnedComponent); 36 | 37 | // // #[requires(self.len() > 0)] 38 | // // #[ensures(self.len() == old(self.len()) - 1)] 39 | // // #[ensures(forall(|i: usize| (i < self.len()) ==> 40 | // // self.lookup(i) == old(self.lookup(i))))] 41 | // pub fn pop(&mut self); 42 | 43 | // pub fn unparse(self) -> Vec; 44 | 45 | // } 46 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/parse_closure_macro.rs: -------------------------------------------------------------------------------- 1 | use syn::parse::{Parse, ParseStream}; 2 | 3 | pub(crate) struct ClosureWithSpec { 4 | pub pres: Vec, 5 | pub posts: Vec, 6 | pub cl: syn::ExprClosure 7 | } 8 | 9 | impl Parse for ClosureWithSpec { 10 | fn parse(input: ParseStream) -> syn::Result { 11 | let mut requires: Vec = vec! []; 12 | let mut ensures: Vec = vec! []; 13 | let mut cl: Option = None; 14 | 15 | while !input.is_empty() { 16 | if input.peek(syn::Ident) { 17 | let id: syn::Ident = input.parse()?; 18 | let expr: syn::Expr = input.parse()?; 19 | input.parse::()?; 20 | 21 | if id == "requires" { 22 | requires.push(expr); 23 | } else if id == "ensures" { 24 | ensures.push(expr); 25 | } else { 26 | return Err(syn::Error::new(id.span(), "invalid closure specification")); 27 | } 28 | } else { 29 | cl = Some(input.parse()?); 30 | } 31 | } 32 | 33 | if cl.is_none() { 34 | return Err(syn::Error::new(input.span(), "closure specification without closure")); 35 | } 36 | 37 | Ok(ClosureWithSpec { 38 | pres: requires, 39 | posts: ensures, 40 | cl: cl.unwrap() 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/verifier_interface/mod.rs: -------------------------------------------------------------------------------- 1 | /// This file contains dummy implementations that do nothing when we are not verifying 2 | 3 | #[cfg(not(any(feature = "verify", test)))] 4 | #[macro_export] 5 | macro_rules! effect { 6 | ($trace:expr, $input:expr) => {}; 7 | } 8 | 9 | #[cfg(not(any(feature = "verify", test)))] 10 | #[macro_export] 11 | macro_rules! path_effect { 12 | ($trace:expr, $input:expr) => {}; 13 | } 14 | 15 | // #[cfg(not(feature = "verify"))] 16 | // #[macro_export] 17 | // macro_rules! do_effect { 18 | // ($trace:expr, $input:expr) => {}; 19 | // } 20 | 21 | #[cfg(not(any(feature = "verify", test)))] 22 | #[macro_export] 23 | macro_rules! effects { 24 | ($trace:expr, $input:expr) => {}; 25 | } 26 | 27 | #[cfg(not(any(feature = "verify", test)))] 28 | #[macro_export] 29 | macro_rules! map_effects { 30 | ($trace:expr, $input:expr) => {}; 31 | } 32 | 33 | // Dummy timing functions that should not exist during verification 34 | // #[cfg(feature = "verify")] 35 | #[inline] 36 | pub fn start_timer() -> u64 { 37 | 0 38 | } 39 | 40 | // #[cfg(feature = "verify")] 41 | #[inline] 42 | pub fn stop_timer() -> u64 { 43 | 0 44 | } 45 | 46 | // #[cfg(feature = "verify")] 47 | #[inline] 48 | pub fn push_hostcall_result(_name: &str, _start: u64, _end: u64) {} 49 | 50 | // #[cfg(feature = "verify")] 51 | #[inline] 52 | pub fn push_syscall_result(_name: &str, _start: u64, _end: u64) {} 53 | 54 | // #[cfg(feature = "verify")] 55 | pub fn output_hostcall_perf_results() {} 56 | 57 | // #[cfg(feature = "verify")] 58 | pub fn output_syscall_perf_results() {} 59 | -------------------------------------------------------------------------------- /examples/Makefile.inc: -------------------------------------------------------------------------------- 1 | WASI_SDK_ROOT=../../tools/wasi-sdk 2 | WASI_SDK_INSTALL=$(WASI_SDK_ROOT)/build/install/opt/wasi-sdk/ 3 | WASM2C_ROOT=../../tools/wasm2c_sandbox_compiler 4 | 5 | WASM2C_SRC_ROOT = $(WASM2C_ROOT)/wasm2c 6 | WASM2C_BIN_ROOT = $(WASM2C_ROOT)/bin 7 | 8 | # Added this cause build directories were different on my mac 9 | # This way both mac and linux should "Just work" 10 | UNAME_S := $(shell uname -s) 11 | ifeq ($(UNAME_S), Darwin) 12 | WASILIBC_ROOT = $(WASI_SDK_INSTALL)/share 13 | DYLIB_EXT = dylib 14 | endif 15 | ifeq ($(UNAME_S), Linux) 16 | DYLIB_EXT = so 17 | endif 18 | 19 | CFLAGS = --sysroot $(WASI_SDK_INSTALL)/share/wasi-sysroot/ 20 | 21 | CC = $(WASI_SDK_INSTALL)/bin/clang 22 | CXX = $(WASI_SDK_INSTALL)/bin/clang++ 23 | LD = $(WASI_SDK_INSTALL)/bin/wasm-ld 24 | LDFLAGS = -Wl,--export-all -Wl,--growable-table 25 | 26 | $(call check_defined, TARGET) 27 | 28 | .PHONY: build 29 | build: $(TARGET) 30 | 31 | .PHONY: clean 32 | clean: 33 | $(RM) $(TARGET).wasm 34 | $(RM) $(TARGET).wasm.c 35 | $(RM) $(TARGET).wasm.h 36 | $(RM) $(TARGET) 37 | 38 | %.wasm: %.c 39 | $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 40 | 41 | %.wasm.c: %.wasm 42 | $(WASM2C_BIN_ROOT)/wasm2c -o $@ $< 43 | 44 | $(TARGET): $(TARGET).wasm.c $(TARGET).wasm 45 | gcc -shared -fPIC -O3 -o $@ $< -I$(WASM2C_SRC_ROOT) $(WASM2C_SRC_ROOT)/wasm-rt-impl.c $(WASM2C_SRC_ROOT)/wasm-rt-os-unix.c $(WASM2C_SRC_ROOT)/wasm-rt-os-win.c $(WASM2C_SRC_ROOT)/wasm-rt-wasi.c ../../target/release/libwave.$(DYLIB_EXT) -I../../bindings 46 | 47 | .PHONY: run 48 | run: $(TARGET) 49 | $(WASM2C_BIN_ROOT)/wasm2c-runner ./$< --homedir=. 50 | -------------------------------------------------------------------------------- /examples/cp_and_insert/cp_and_insert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char buffer[1024]; 10 | int files[2]; 11 | ssize_t count; 12 | 13 | /* Check for insufficient parameters */ 14 | //if (argc < 3) 15 | // return -1; 16 | files[0] = open("./data/tmp.txt", O_RDONLY); 17 | if (files[0] == -1) /* Check if file opened */ 18 | return -1; 19 | files[1] = open("./output/tmp.txt", O_RDONLY | O_WRONLY | O_CREAT | S_IRUSR | S_IWUSR); 20 | if (files[1] == -1) /* Check if file opened (permissions problems ...) */ 21 | { 22 | close(files[0]); 23 | return -1; 24 | } 25 | 26 | while ((count = read(files[0], buffer, sizeof(buffer))) != 0) 27 | write(files[1], buffer, count); 28 | 29 | // will invoke fd_tell (all lseeks of the form lseek(x,0,SEEK_CUR) do) 30 | int position = lseek(files[1], 0, SEEK_CUR); 31 | printf("position for lseek1 = %d\n", position); 32 | 33 | lseek(files[1], 0, SEEK_SET); //get back to the beginning 34 | 35 | position = lseek(files[1], 0, SEEK_CUR); 36 | printf("position for lseek2 = %d\n", position); 37 | 38 | // Change 39 | // This is the contents of the file! 40 | // to 41 | // This is the contents in the file! 42 | pwrite(files[1], "in", 2, 21); 43 | 44 | // read the word file from ./output/tmp.txt 45 | char buf[4]; 46 | pread(files[1], buf, 4, 28); 47 | printf("results of pread = %s\n", buf); 48 | 49 | 50 | printf("Done!\n"); 51 | return 0; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /examples/client/native_client/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define MAX 80 8 | #define PORT 8080 9 | #define SA struct sockaddr 10 | void func(int sockfd) 11 | { 12 | char buff[MAX]; 13 | int n; 14 | for (;;) { 15 | bzero(buff, sizeof(buff)); 16 | printf("Enter the string : "); 17 | n = 0; 18 | while ((buff[n++] = getchar()) != '\n') 19 | ; 20 | write(sockfd, buff, sizeof(buff)); 21 | bzero(buff, sizeof(buff)); 22 | read(sockfd, buff, sizeof(buff)); 23 | printf("From Server : %s", buff); 24 | if ((strncmp(buff, "exit", 4)) == 0) { 25 | printf("Client Exit...\n"); 26 | break; 27 | } 28 | } 29 | } 30 | 31 | int main() 32 | { 33 | int sockfd, connfd; 34 | struct sockaddr_in servaddr, cli; 35 | 36 | // socket create and varification 37 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 38 | if (sockfd == -1) { 39 | printf("socket creation failed...\n"); 40 | exit(0); 41 | } 42 | else 43 | printf("Socket successfully created..\n"); 44 | bzero(&servaddr, sizeof(servaddr)); 45 | 46 | // assign IP, PORT 47 | servaddr.sin_family = AF_INET; 48 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 49 | servaddr.sin_port = htons(PORT); 50 | 51 | // connect the client socket to server socket 52 | if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 53 | printf("connection with the server failed...\n"); 54 | exit(0); 55 | } 56 | else 57 | printf("connected to the server..\n"); 58 | 59 | // function for chat 60 | func(sockfd); 61 | 62 | // close the socket 63 | close(sockfd); 64 | } -------------------------------------------------------------------------------- /examples/client_send_recv/native_client/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define MAX 80 8 | #define PORT 8080 9 | #define SA struct sockaddr 10 | void func(int sockfd) 11 | { 12 | char buff[MAX]; 13 | int n; 14 | for (;;) { 15 | bzero(buff, sizeof(buff)); 16 | printf("Enter the string : "); 17 | n = 0; 18 | while ((buff[n++] = getchar()) != '\n') 19 | ; 20 | write(sockfd, buff, sizeof(buff)); 21 | bzero(buff, sizeof(buff)); 22 | read(sockfd, buff, sizeof(buff)); 23 | printf("From Server : %s", buff); 24 | if ((strncmp(buff, "exit", 4)) == 0) { 25 | printf("Client Exit...\n"); 26 | break; 27 | } 28 | } 29 | } 30 | 31 | int main() 32 | { 33 | int sockfd, connfd; 34 | struct sockaddr_in servaddr, cli; 35 | 36 | // socket create and varification 37 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 38 | if (sockfd == -1) { 39 | printf("socket creation failed...\n"); 40 | exit(0); 41 | } 42 | else 43 | printf("Socket successfully created..\n"); 44 | bzero(&servaddr, sizeof(servaddr)); 45 | 46 | // assign IP, PORT 47 | servaddr.sin_family = AF_INET; 48 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 49 | servaddr.sin_port = htons(PORT); 50 | 51 | // connect the client socket to server socket 52 | if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 53 | printf("connection with the server failed...\n"); 54 | exit(0); 55 | } 56 | else 57 | printf("connected to the server..\n"); 58 | 59 | // function for chat 60 | func(sockfd); 61 | 62 | // close the socket 63 | close(sockfd); 64 | } -------------------------------------------------------------------------------- /examples/client_send_recv/client_send_recv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define MAX 80 8 | #define PORT 8080 9 | #define SA struct sockaddr 10 | void func(int sockfd) 11 | { 12 | char buff[MAX]; 13 | int n; 14 | for (;;) { 15 | bzero(buff, sizeof(buff)); 16 | printf("Enter the string : \n"); 17 | n = 0; 18 | while ((buff[n++] = getchar()) != '\n') 19 | ; 20 | send(sockfd, buff, sizeof(buff), 0); 21 | //ssize_t send(int socket, const void *buffer, size_t length, int flags); 22 | 23 | bzero(buff, sizeof(buff)); 24 | // ssize_t recv(int socket, void *buffer, size_t length, int flags); 25 | recv(sockfd, buff, sizeof(buff), 0); 26 | printf("From Server : %s\n", buff); 27 | if ((strncmp(buff, "exit", 4)) == 0) { 28 | printf("Client Exit...\n"); 29 | break; 30 | } 31 | } 32 | } 33 | 34 | 35 | 36 | int main() 37 | { 38 | int sockfd, connfd; 39 | struct sockaddr_in servaddr, cli; 40 | struct in_addr addr; 41 | 42 | 43 | // socket create and varification 44 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 45 | if (sockfd == -1) { 46 | printf("socket creation failed... "); 47 | perror("socket creation error"); 48 | printf("\n"); 49 | exit(0); 50 | } 51 | else 52 | printf("Socket successfully created..\n"); 53 | bzero(&servaddr, sizeof(servaddr)); 54 | 55 | inet_aton("127.0.0.1", &addr); 56 | 57 | //assign IP, PORT 58 | servaddr.sin_family = AF_INET; 59 | servaddr.sin_addr.s_addr = addr.s_addr;//inet_addr("127.0.0.1"); 60 | servaddr.sin_port = htons(PORT); 61 | 62 | // connect the client socket to server socket 63 | if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 64 | printf("connection with the server failed...\n"); 65 | exit(0); 66 | } 67 | else 68 | printf("connected to the server..\n"); 69 | 70 | // function for chat 71 | func(sockfd); 72 | 73 | // close the socket 74 | close(sockfd); 75 | } 76 | -------------------------------------------------------------------------------- /examples/client/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define MAX 80 8 | #define PORT 8080 9 | #define SA struct sockaddr 10 | void sock_func(int sockfd) 11 | { 12 | char buff[MAX]; 13 | int n; 14 | for (;;) { 15 | bzero(buff, sizeof(buff)); 16 | // write(1,"Enter the string : \n", 20); 17 | printf("Enter the string: \n"); 18 | n = 0; 19 | while ((buff[n++] = getchar()) != '\n') 20 | ; 21 | write(sockfd, buff, sizeof(buff)); 22 | bzero(buff, sizeof(buff)); 23 | read(sockfd, buff, sizeof(buff)); 24 | printf("From Server : %s\n", buff); 25 | fflush(stdout); // unsure, but stdout buffering is broken after socket creation 26 | if ((strncmp(buff, "exit", 4)) == 0) { 27 | printf("Client Exit...\n"); 28 | break; 29 | } 30 | } 31 | } 32 | 33 | 34 | 35 | int main() 36 | { 37 | int sockfd, connfd; 38 | struct sockaddr_in servaddr, cli; 39 | struct in_addr addr; 40 | 41 | // socket create and varification 42 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 43 | if (sockfd == -1) { 44 | printf("socket creation failed... "); 45 | perror("socket creation error"); 46 | printf("\n"); 47 | exit(0); 48 | } 49 | else{ 50 | printf("Socket successfully created..\n"); 51 | } 52 | bzero(&servaddr, sizeof(servaddr)); 53 | inet_aton("127.0.0.1", &addr); 54 | 55 | //assign IP, PORT 56 | servaddr.sin_family = AF_INET; 57 | servaddr.sin_addr.s_addr = addr.s_addr;//inet_addr("127.0.0.1"); 58 | servaddr.sin_port = htons(PORT); 59 | 60 | // connect the client socket to server socket 61 | if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 62 | printf("connection with the server failed...errno %s\n", strerror(errno)); 63 | exit(0); 64 | } 65 | else { 66 | printf("connected to the server...\n"); 67 | } 68 | 69 | // function for chat 70 | sock_func(sockfd); 71 | 72 | // close the socket 73 | close(sockfd); 74 | } 75 | -------------------------------------------------------------------------------- /scan_for_trusted.sh: -------------------------------------------------------------------------------- 1 | # This script checks whether trusted code is in the src/tcb folder 2 | # This isn't meant to really handle adverserial input, its more like a 3 | # best-practices linter 4 | 5 | # There are three possible sources of trusted code 6 | # 1. the [trusted] annotation 7 | # 2. the effect! macro 8 | # 3. the assume! macro 9 | 10 | # This script just greps for these three sources of trusted code 11 | 12 | # Grep flags used: 13 | # -l flag = list file names instead of lines found 14 | # -R flag = recursive 15 | 16 | # TODO: this should probably exclude trusted/effect/assume/unsafe 17 | # anotations that are in comments 18 | 19 | # Check for [trusted] annotation 20 | check_for_trusted=`grep -lR "\[trusted\]" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats"` 21 | if [ -z "$check_for_trusted" ] 22 | then 23 | result1=1 24 | else 25 | result1=0 26 | echo "Found [trusted] annotations outside the src/tcb folder" 27 | grep -lR "\[trusted\]" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats" 28 | fi 29 | 30 | # 2. Check for effect! macro 31 | check_for_effect=`grep -lR "\sdo_effect!" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats"` 32 | if [ -z "$check_for_effect" ] 33 | then 34 | result2=1 35 | else 36 | result2=0 37 | echo "Found do_effect! annotations outside the src/tcb folder" 38 | grep -lR "\sdo_effect!" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats" 39 | fi 40 | 41 | # 3. Check for assume! macro 42 | check_for_assume=`grep -lR "\sassume!" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats"` 43 | if [ -z "$check_for_assume" ] 44 | then 45 | result3=1 46 | else 47 | result3=0 48 | echo "Found assume! annotations outside the src/tcb folder" 49 | grep -lR "\sassume!" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats" 50 | fi 51 | 52 | # 3. Check for unsafe annotations 53 | check_for_unsafe=`grep -lR "\sunsafe\s" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats"` 54 | if [ -z "$check_for_unsafe" ] 55 | then 56 | result4=1 57 | else 58 | result4=0 59 | echo "Found unsafe annotations outside the src/tcb folder" 60 | grep -lR "\sunsafe\s" src | grep -v "src\/tcb" | grep -v "src\/tests" | grep -v "src\/stats" 61 | fi 62 | 63 | # If all tests passed, let the user know 64 | if [ "$result1" == "1" -a "$result2" == "1" -a "$result3" == "1" -a "$result4" == "1" ] 65 | then 66 | echo "All trusted code is in src/tcb. Good job!" 67 | fi 68 | 69 | -------------------------------------------------------------------------------- /cbindgen.toml: -------------------------------------------------------------------------------- 1 | # The language to output bindings in 2 | # 3 | # possible values: "C", "C++", "Cython" 4 | # 5 | # default: "C++" 6 | language = "C" 7 | 8 | # An optional name to use as an include guard 9 | # default: doesn't emit an include guard 10 | include_guard = "wave_bindings_h" 11 | 12 | # An optional string of text to output between major sections of the generated 13 | # file as a warning against manual editing 14 | # 15 | # default: doesn't emit anything 16 | autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" 17 | 18 | # Whether to include a comment with the version of cbindgen used to generate the file 19 | # default: false 20 | include_version = true 21 | 22 | 23 | # Whether to make a C header C++ compatible. 24 | # These will wrap generated functions into a `extern "C"` block, e.g. 25 | # 26 | # #ifdef __cplusplus 27 | # extern "C" { 28 | # #endif // __cplusplus 29 | # 30 | # // Generated functions. 31 | # 32 | # #ifdef __cplusplus 33 | # } // extern "C" 34 | # #endif // __cplusplus 35 | # 36 | # If the language is not C this option won't have any effect. 37 | # 38 | # default: false 39 | cpp_compat = true 40 | 41 | # The desired length of a line to use when formatting lines 42 | # default: 100 43 | line_length = 80 44 | 45 | 46 | # Include doc comments from Rust as documentation 47 | documentation = true 48 | 49 | # A list of substitutions for converting cfg's to ifdefs. cfgs which aren't 50 | # defined here will just be discarded. 51 | # 52 | # e.g. 53 | # `#[cfg(target = "freebsd")] ...` 54 | # becomes 55 | # `#if defined(DEFINE_FREEBSD) ... #endif` 56 | [defines] 57 | "target_os = linux" = "DEFINE_LINUX" 58 | "target_os = windows" = "DEFINE_WINDOWS" 59 | "target_os = macos" = "DEFINE_MACOS" 60 | 61 | 62 | # [fn] 63 | # # An optional prefix to put before every function declaration 64 | # # default: no prefix added 65 | # prefix = "Z_wasi_snapshot_preview1Z_" 66 | 67 | # # An optional postfix to put after any function declaration 68 | # # default: no postix added 69 | # postfix = "_iiiiii" 70 | 71 | 72 | # Options for how your Rust library should be parsed 73 | 74 | [parse] 75 | # Whether to parse dependent crates and include their types in the output 76 | # default: false 77 | parse_deps = false 78 | 79 | # A white list of crate names that are allowed to be parsed. If this is defined, 80 | # only crates found in this list will ever be parsed. 81 | # 82 | # default: there is no whitelist (NOTE: this is the opposite of []) 83 | include = ["veriwasi"] 84 | -------------------------------------------------------------------------------- /src/types/platform/macos.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::tcb::misc::*; 3 | use libc; 4 | 5 | /// On Mac, posix_advise doesn't exist. Just have the call do nothing. 6 | impl From for i32 { 7 | fn from(advice: Advice) -> Self { 8 | 0 9 | } 10 | } 11 | 12 | impl TryFrom for Advice { 13 | type Error = RuntimeError; 14 | fn try_from(advice: i32) -> RuntimeResult { 15 | Ok(Advice::Normal) 16 | } 17 | } 18 | 19 | impl FdFlags { 20 | pub fn to_posix(&self) -> i32 { 21 | let mut flags = 0; 22 | // TODO: DSYNC, RSYNC, and SYNC do not exist on mac. Ignoring for now... 23 | if nth_bit_set(self.0, 0) { 24 | flags = bitwise_or(flags, libc::O_APPEND) 25 | } 26 | if nth_bit_set(self.0, 2) { 27 | flags = bitwise_or(flags, libc::O_NONBLOCK) 28 | } 29 | flags 30 | } 31 | 32 | pub fn from_posix(flags: i32) -> Self { 33 | // FdFlags(flags as u16) 34 | //let mut result = FdFlags(0); 35 | // TODO: DSYNC, RSYNC, and SYNC do not exist on mac. Ignoring for now... 36 | let mut result = FdFlags(0); 37 | if bitwise_and(flags, libc::O_APPEND) != 0 { 38 | result.0 = with_nth_bit_set(result.0, 0); 39 | } 40 | if bitwise_and(flags, libc::O_NONBLOCK) != 0 { 41 | result.0 = with_nth_bit_set(result.0, 2); 42 | } 43 | result 44 | } 45 | } 46 | 47 | impl Dirent { 48 | #[requires(in_idx < host_buf.len())] 49 | pub fn parse(host_buf: &Vec, in_idx: usize) -> RuntimeResult { 50 | assert!(false); 51 | // Inode number 52 | let d_ino = u32::from_le_bytes([ 53 | host_buf[in_idx + 0], 54 | host_buf[in_idx + 1], 55 | host_buf[in_idx + 2], 56 | host_buf[in_idx + 3], 57 | ]); 58 | 59 | // Offset to next linux_dirent 60 | let d_reclen = u16::from_le_bytes([host_buf[in_idx + 4], host_buf[in_idx + 5]]); 61 | 62 | // File type 63 | let d_type = host_buf[in_idx + 6]; 64 | 65 | // Length of this linux_dirent 66 | let d_namlen = host_buf[in_idx + 7]; 67 | 68 | // If we would overflow - don't :) 69 | if d_reclen < 8 || (in_idx + d_reclen as usize) > host_buf.len() { 70 | return Err(RuntimeError::Eoverflow); 71 | } 72 | 73 | let out_namlen = first_null(&host_buf, in_idx, 8, d_reclen as usize); 74 | let dirent = Dirent { 75 | ino: d_ino as u64, 76 | reclen: d_reclen, 77 | name_start: 8, 78 | out_namlen, 79 | typ: d_type, 80 | }; 81 | 82 | Ok(dirent) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /waverunner/src/waverunner.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{Wasm2cBinary, WaveConfig, WaveSandbox}; 2 | use dlopen::wrapper::Container; 3 | use libc::{mprotect, munmap}; 4 | use std::ptr; 5 | use wave::wasm2c_frontend::{create_ctx, wave_cleanup}; // TODO: fix path in wave 6 | use wave::types::VmCtx; 7 | use wave::setup_teardown::wave_alloc_linmem; 8 | // handles wasm2c files 9 | 10 | // // TODO: how does the stack guard work? 11 | // // TODO: direct syscalls for mmap, munmap, and mprotect? 12 | // // TODO: zero initialize linear memory 13 | 14 | 15 | 16 | // 1 << 32 = 4GB 17 | const FOUR_GB: usize = 1 << 32; 18 | // 1 << 33 = 8GB 19 | const EIGHT_GB: usize = 1 << 33; 20 | 21 | 22 | 23 | fn wave_setup_signals() { 24 | // For now, do nothings, since we just fail on any signal 25 | } 26 | 27 | // Setup has 3 steps 28 | // 1. load the AOT-compiled Wasm module via DlOpen 29 | // 2. MMap and mprotect the linear memory 30 | // 3. Setup signals (maybe?) 31 | // It then returns a wave_runtime object with all these things set up 32 | fn setup(config: &WaveConfig) -> WaveSandbox { 33 | // 1. Load AOT-compiled Wasm module 34 | let module: Container = unsafe { 35 | Container::load(config.module_path.as_str()) // wrapper around dlopen 36 | } 37 | .unwrap(); 38 | 39 | // 2. MMap and mprotect the linear memory 40 | let linmem = wave_alloc_linmem() as *mut u8; 41 | 42 | // 3. Setup signals 43 | wave_setup_signals(); 44 | 45 | let vmctx = create_ctx( 46 | linmem, 47 | &config.homedir, 48 | config.args.clone(), 49 | config.argc, 50 | config.env.clone(), 51 | config.envc, 52 | config.netlist, 53 | ); 54 | 55 | WaveSandbox { 56 | module, 57 | vmctx, 58 | linmem, 59 | } 60 | } 61 | 62 | // The execute stage simply consists of calling the __start function 63 | // returns the result code of executing the sandbox 64 | fn execute(sandbox: &WaveSandbox) -> i32 { 65 | unsafe { sandbox.module.w2c__start() } 66 | } 67 | 68 | // // Here, we just: 69 | // // 1. Unmap linear memory 70 | // // 2. Drop the sandbox 71 | fn teardown(mut sandbox: WaveSandbox) { 72 | unsafe { 73 | // release file descriptors and such 74 | wave_cleanup(&(&mut sandbox.vmctx as *mut VmCtx) as *const *mut VmCtx); 75 | // unmap linear memory 76 | 77 | munmap(sandbox.linmem as *mut libc::c_void, EIGHT_GB); 78 | } 79 | // then the sandbox gets dropped automatically by rustc since we moved the sandbox here 80 | } 81 | 82 | // Run consists of 3 steps: 83 | // 1. Set up runtime 84 | // 2. Execute AOT-compiled Wasm binary 85 | // 3. Teardown runtime 86 | pub fn run(config: &WaveConfig) { 87 | let sandbox = setup(config); 88 | let _result = execute(&sandbox); 89 | teardown(sandbox); 90 | } 91 | 92 | -------------------------------------------------------------------------------- /src/os/platform/linux.rs: -------------------------------------------------------------------------------- 1 | //! Contains call implementations that are specific to Linux/Posix 2 | //! See src/tcb/os_specs for the raw system calls. 3 | 4 | use crate::tcb::os_specs::*; 5 | #[cfg(feature = "verify")] 6 | use crate::tcb::verifier::*; 7 | use crate::types::*; 8 | use crate::{effect, effects}; 9 | use prusti_contracts::*; 10 | use syscall::syscall; 11 | use wave_macros::with_ghost_var; 12 | 13 | #[with_ghost_var(trace: &mut Trace)] 14 | #[requires(ctx_safe(ctx))] 15 | #[requires(trace_safe(trace, ctx))] 16 | #[ensures(ctx_safe(ctx))] 17 | #[ensures(trace_safe(trace, ctx))] 18 | // #[ensures(effects!(old(trace), trace, effect!(FdAccess)))] 19 | pub fn trace_advise( 20 | ctx: &VmCtx, 21 | fd: HostFd, 22 | offset: i64, 23 | len: i64, 24 | advice: i32, 25 | ) -> RuntimeResult { 26 | let os_fd: usize = fd.to_raw(); 27 | let r = os_fadvise64(os_fd, offset, len, advice); 28 | RuntimeError::from_syscall_ret(r) 29 | } 30 | 31 | #[with_ghost_var(trace: &mut Trace)] 32 | #[requires(ctx_safe(ctx))] 33 | #[requires(trace_safe(trace, ctx))] 34 | #[ensures(ctx_safe(ctx))] 35 | #[ensures(trace_safe(trace, ctx))] 36 | #[ensures(effects!(old(trace), trace))] 37 | pub fn trace_clock_get_time( 38 | ctx: &VmCtx, 39 | clock_id: libc::clockid_t, 40 | spec: &mut libc::timespec, 41 | ) -> RuntimeResult { 42 | let r = os_clock_gettime(clock_id, spec); 43 | RuntimeError::from_syscall_ret(r) 44 | } 45 | 46 | #[with_ghost_var(trace: &mut Trace)] 47 | #[requires(ctx_safe(ctx))] 48 | #[requires(trace_safe(trace, ctx))] 49 | #[ensures(ctx_safe(ctx))] 50 | #[ensures(trace_safe(trace, ctx))] 51 | #[ensures(effects!(old(trace), trace))] 52 | pub fn trace_clock_get_res( 53 | ctx: &VmCtx, 54 | clock_id: libc::clockid_t, 55 | spec: &mut libc::timespec, 56 | ) -> RuntimeResult { 57 | let r = os_clock_getres(clock_id, spec); 58 | RuntimeError::from_syscall_ret(r) 59 | } 60 | 61 | #[with_ghost_var(trace: &mut Trace)] 62 | #[requires(ctx_safe(ctx))] 63 | #[requires(trace_safe(trace, ctx))] 64 | #[ensures(ctx_safe(ctx))] 65 | #[ensures(trace_safe(trace, ctx))] 66 | #[ensures(effects!(old(trace), trace))] 67 | pub fn trace_nanosleep( 68 | ctx: &VmCtx, 69 | req: &libc::timespec, 70 | rem: &mut libc::timespec, 71 | ) -> RuntimeResult { 72 | let r = os_nanosleep(req, rem); 73 | RuntimeError::from_syscall_ret(r) 74 | } 75 | 76 | #[with_ghost_var(trace: &mut Trace)] 77 | #[requires(ctx_safe(ctx))] 78 | #[requires(trace_safe(trace, ctx))] 79 | #[ensures(ctx_safe(ctx))] 80 | #[ensures(trace_safe(trace, ctx))] 81 | #[ensures(effects!(old(trace), trace, effect!(FdAccess)))] 82 | pub fn trace_allocate(ctx: &VmCtx, fd: HostFd, offset: i64, len: i64) -> RuntimeResult { 83 | let os_fd: usize = fd.to_raw(); 84 | let r = os_fallocate(os_fd, 0, offset, len); 85 | RuntimeError::from_syscall_ret(r) 86 | } 87 | -------------------------------------------------------------------------------- /examples/client/server/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define MAX 80 10 | #define PORT 8080 11 | #define SA struct sockaddr 12 | 13 | // Function designed for chat between client and server. 14 | void func(int sockfd) 15 | { 16 | char buff[MAX]; 17 | int n; 18 | // infinite loop for chat 19 | for (;;) { 20 | bzero(buff, MAX); 21 | 22 | // read the message from client and copy it in buffer 23 | read(sockfd, buff, sizeof(buff)); 24 | // print buffer which contains the client contents 25 | printf("From client: %s\n To client : \n", buff); 26 | bzero(buff, MAX); 27 | n = 0; 28 | // copy server message in the buffer 29 | while ((buff[n++] = getchar()) != '\n') 30 | ; 31 | 32 | // and send that buffer to client 33 | write(sockfd, buff, sizeof(buff)); 34 | 35 | // if msg contains "Exit" then server exit and chat ended. 36 | if (strncmp("exit", buff, 4) == 0) { 37 | printf("Server Exit...\n"); 38 | break; 39 | } 40 | } 41 | } 42 | 43 | // Driver function 44 | int main() 45 | { 46 | int sockfd, connfd, len; 47 | struct sockaddr_in servaddr, cli; 48 | 49 | // socket create and verification 50 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 51 | if (sockfd == -1) { 52 | printf("socket creation failed...\n"); 53 | exit(0); 54 | } 55 | else 56 | printf("Socket successfully created..\n"); 57 | bzero(&servaddr, sizeof(servaddr)); 58 | 59 | // assign IP, PORT 60 | servaddr.sin_family = AF_INET; 61 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 62 | servaddr.sin_port = htons(PORT); 63 | 64 | // Binding newly created socket to given IP and verification 65 | if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { 66 | printf("socket bind failed...\n"); 67 | exit(0); 68 | } 69 | else 70 | printf("Socket successfully binded..\n"); 71 | 72 | // Now server is ready to listen and verification 73 | if ((listen(sockfd, 5)) != 0) { 74 | printf("Listen failed...\n"); 75 | exit(0); 76 | } 77 | else 78 | printf("Server listening..\n"); 79 | len = sizeof(cli); 80 | 81 | // Accept the data packet from client and verification 82 | connfd = accept(sockfd, (SA*)&cli, &len); 83 | if (connfd < 0) { 84 | printf("server accept failed...\n"); 85 | exit(0); 86 | } 87 | else 88 | printf("server accept the client...\n"); 89 | 90 | // Function for chatting between client and server 91 | func(connfd); 92 | 93 | // After chatting close the socket 94 | close(sockfd); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /examples/client_send_recv/server/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define MAX 80 10 | #define PORT 8080 11 | #define SA struct sockaddr 12 | 13 | // Function designed for chat between client and server. 14 | void func(int sockfd) 15 | { 16 | char buff[MAX]; 17 | int n; 18 | // infinite loop for chat 19 | for (;;) { 20 | bzero(buff, MAX); 21 | 22 | // read the message from client and copy it in buffer 23 | recv(sockfd, buff, sizeof(buff), 0); 24 | // print buffer which contains the client contents 25 | printf("From client: %s\n To client : \n", buff); 26 | bzero(buff, MAX); 27 | n = 0; 28 | // copy server message in the buffer 29 | while ((buff[n++] = getchar()) != '\n') 30 | ; 31 | 32 | // and send that buffer to client 33 | send(sockfd, buff, sizeof(buff), 0); 34 | 35 | // if msg contains "Exit" then server exit and chat ended. 36 | if (strncmp("exit", buff, 4) == 0) { 37 | printf("Server Exit...\n"); 38 | break; 39 | } 40 | } 41 | } 42 | 43 | // Driver function 44 | int main() 45 | { 46 | int sockfd, connfd, len; 47 | struct sockaddr_in servaddr, cli; 48 | 49 | // socket create and verification 50 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 51 | if (sockfd == -1) { 52 | printf("socket creation failed...\n"); 53 | exit(0); 54 | } 55 | else 56 | printf("Socket successfully created..\n"); 57 | bzero(&servaddr, sizeof(servaddr)); 58 | 59 | // assign IP, PORT 60 | servaddr.sin_family = AF_INET; 61 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 62 | servaddr.sin_port = htons(PORT); 63 | 64 | // Binding newly created socket to given IP and verification 65 | if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { 66 | printf("socket bind failed...\n"); 67 | exit(0); 68 | } 69 | else 70 | printf("Socket successfully binded..\n"); 71 | 72 | // Now server is ready to listen and verification 73 | if ((listen(sockfd, 5)) != 0) { 74 | printf("Listen failed...\n"); 75 | exit(0); 76 | } 77 | else 78 | printf("Server listening..\n"); 79 | len = sizeof(cli); 80 | 81 | // Accept the data packet from client and verification 82 | connfd = accept(sockfd, (SA*)&cli, &len); 83 | if (connfd < 0) { 84 | printf("server accept failed...\n"); 85 | exit(0); 86 | } 87 | else 88 | printf("server accept the client...\n"); 89 | 90 | // Function for chatting between client and server 91 | func(connfd); 92 | 93 | // After chatting close the socket 94 | close(sockfd); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WaVe: a verifiably secure WebAssembly sandboxing runtime 2 | 3 | This repository contains all the code and data necessary for building WaVe and reproducing the results presented in our paper [WaVe: a verifiably secure WebAssembly sandboxing runtime](https://cseweb.ucsd.edu/~dstefan/pubs/johnson:2023:wave.pdf)). 4 | 5 | ## Abstract 6 | The promise of software sandboxing is flexible, fast and portable isolation; capturing the benefits of hardware-based memory protection without requiring operating system involvement. This promise is reified in WebAssembly (Wasm), a popular portable bytecode whose compilers automatically insert 7 | runtime checks to ensure that data and control flow are constrained to a single memory segment. Indeed, modern compiled Wasm implementations have advanced to the point where these checks can themselves be verified, removing the compiler from the trusted computing base. However, the resulting integrity 8 | properties are only valid for code executing strictly inside the Wasm sandbox. Any interactions with the runtime system, which manages sandboxes and exposes the WebAssembly System Interface (WASI) used to access operating system resources, operate outside this contract. The resulting conundrum is how to 9 | maintain Wasm’s strong isolation properties while still allowing such programs to interact with the outside world (i.e., with the file system, the network, etc.). Our paper presents a solution to this problem, via WaVe, a verified secure runtime system that implements WASI. We mechanically verify that interactions with WaVe (including OS side effects) not only maintain Wasm’s memory safety guarantees, but also maintain access isolation for the host OS’s storage and network resources. Finally, in spite of completely removing the runtime from the trusted computing base, we show that WaVe offers performance competitive with existing industrial (yet unsafe) Wasm runtimes. 10 | 11 | ### Install dependencies 12 | `apt-get install -y curl git unzip build-essential pkg-config libssl-dev cmake ninja-build clang` 13 | 14 | Additionally, ensure you have [https://rustup.rs/](Rustup) installed, and install additional Rust dependencies: 15 | `cargo install --force cbindgen` 16 | 17 | ### To build and verify: 18 | 19 | ``` 20 | make bootstrap # Setup build the first time. This will take 15-20 minutes. 21 | make build # Build WaVe. This should take < 1 minute. 22 | cargo test # Run all compliance tests. This should take < 1 minute 23 | make verify # Verify correctness of WaVe. This will take 30-60 minutes. 24 | ``` 25 | 26 | ## To run an example application using WaVe 27 | ``` 28 | cd examples/cat # enter example directory 29 | make # build 30 | make run # execute cat example 31 | ``` 32 | You can examine the makefile to see the exact commands 33 | 34 | 35 | ## Related documentation 36 | Interface for WASI calls: [WASI API](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md) 37 | 38 | -------------------------------------------------------------------------------- /src/tcb/verifier/spec.rs: -------------------------------------------------------------------------------- 1 | use crate::tcb::misc::netlist_unmodified; 2 | #[cfg(feature = "verify")] 3 | use crate::tcb::path::path_safe; 4 | use crate::tcb::sbox_mem::{raw_ptr, valid_linmem}; 5 | use crate::tcb::verifier::trace::{Effect, EffectType, Trace}; 6 | use crate::types::{addr_in_netlist, VmCtx, HOMEDIR_FD, LINEAR_MEM_SIZE}; 7 | use prusti_contracts::*; 8 | 9 | #[cfg(feature = "verify")] 10 | predicate! { 11 | pub fn ctx_safe(ctx: &VmCtx) -> bool { 12 | //let mem_ptr = raw_ptr(ctx.mem.as_slice()); 13 | ctx.memlen == LINEAR_MEM_SIZE && 14 | ctx.argc < 1024 && 15 | ctx.envc < 1024 && 16 | ctx.arg_buffer.len() < 1024 * 1024 && 17 | ctx.env_buffer.len() < 1024 * 1024 && 18 | netlist_unmodified(&ctx.netlist) && 19 | valid_linmem(raw_ptr(ctx.mem.as_slice())) //&& 20 | //mem_ptr <= mem_ptr + count 21 | } 22 | } 23 | 24 | #[cfg(feature = "verify")] 25 | predicate! { 26 | pub fn trace_safe(trace: &Trace, ctx: &VmCtx) -> bool { 27 | forall(|i: usize| 28 | (i < trace.len() ==> ( 29 | match trace.lookup(i) { 30 | Effect { typ: EffectType::ReadMem | EffectType::WriteMem, f1: addr, f2: count, .. } => { 31 | let mem_ptr = raw_ptr(ctx.mem.as_slice()); 32 | valid_linmem(mem_ptr) && // sbox mem has been initialized 33 | addr >= mem_ptr && // start of buffer in sbox mem 34 | addr + count < mem_ptr + ctx.memlen && // end of buffer in sbox mem 35 | mem_ptr <= mem_ptr + ctx.memlen && // memory region does not overflow address space 36 | addr <= addr + count // buffer does not overflow 37 | }, 38 | //Effect { typ: EffectType::WriteMem, f1: addr, f2: count, .. } => valid_linmem(raw_ptr(ctx.mem.as_slice())),//(addr < ctx.memlen) && (count < ctx.memlen) && (addr <= (addr + count)), 39 | Effect { typ: EffectType::Shutdown, .. } => true, // currently, all shutdowns are safe 40 | Effect { typ: EffectType::FdAccess, .. } => true, 41 | Effect { typ: EffectType::PathAccessAt, f1: dir_fd, f2:_, f3:_, p: Some(path), should_follow: Some(b) } => dir_fd == ctx.homedir_host_fd.to_raw() && path.len() == 4096 && path_safe(&path, b), 42 | Effect { typ: EffectType::NetAccess, f1: _proto, f2:addr, f3:port, .. } => addr_in_netlist(&ctx.netlist, addr as u32, port as u32), 43 | Effect { typ: EffectType::SockCreation, f1: domain, f2:ty, .. } => domain == (libc::AF_INET as usize) && (ty == (libc::SOCK_STREAM as usize) || ty == (libc::SOCK_DGRAM as usize)), 44 | _ => false, 45 | } 46 | )) 47 | ) 48 | } 49 | } 50 | /* 51 | pub fn path_safe(v: &HostPath, should_follow: bool) -> bool { 52 | arr_is_relative(&v) && 53 | (arr_depth(&v) >= 0) && 54 | (should_follow ==> !arr_is_symlink(&v)) && 55 | arr_has_no_symlink_prefixes(&v) 56 | } 57 | */ 58 | 59 | /* 60 | #[ensures(effects!(old(trace), trace, effect!(PathAccessAt, dirfd, path, !flag_set(flags, libc::O_NOFOLLOW)) 61 | pub fn os_openat(dirfd: usize, path: [u8; PATH_MAX], flags: i32) -> isize { 62 | */ 63 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: bootstrap build verify prusti bindings wasm2c 2 | 3 | # Prusti setup and build instructions: 4 | # (this makefile should handle anything, but you can look at these if you run into problems) 5 | # https://viperproject.github.io/prusti-dev/dev-guide/development/setup.html 6 | # https://viperproject.github.io/prusti-dev/dev-guide/development/build.html 7 | 8 | # Used by Prusti 9 | JAVA_HOME ?= /usr/lib/jvm/ 10 | 11 | SPEC_PATH := ./wave-specbenchmark 12 | 13 | bootstrap: 14 | git submodule update --init --recursive 15 | cd prusti-dev && ./x.py setup 16 | cd prusti-dev && JAVA_HOME=$(JAVA_HOME) ./x.py build --release 17 | cargo build --release 18 | make bindings # this is dirty, fix this 19 | cd tools/wasm2c_sandbox_compiler && make 20 | cd tools/wasi-sdk && make 21 | 22 | 23 | prusti: 24 | cd prusti-dev && JAVA_HOME=$(JAVA_HOME) ./x.py build --release 25 | 26 | build: 27 | cargo build --release 28 | 29 | build_hello_example: 30 | cd examples/hello && make clean 31 | cd examples/hello && make build 32 | 33 | run_hello_example: 34 | cd examples/hello && make run 35 | 36 | # If this command is giving you trouble, try deleting the ./target directory and retrying 37 | # TODO: reenable overflow checks 38 | verify: 39 | prusti-dev/target/release/cargo-prusti --features verify 40 | 41 | verify-debug: 42 | PRUSTI_LOG=trace prusti-dev/target/debug/cargo-prusti --features verify 43 | 44 | # Generate C/Cpp bindings for wave 45 | # wasm2c expects to pass a void pointer instead of a VmCtx pointer 46 | # (which cbindgen generates), so I just use a sed command to replace it :) 47 | bindings: 48 | mkdir -p bindings 49 | # Generates a temporary file with sed because Mac doesn't support -i flag 50 | cbindgen --config cbindgen.toml --crate wave --lang c --output bindings/wave_tmp.h 51 | sed 's/struct[[:space:]]VmCtx[[:space:]][*]const/void/g' bindings/wave_tmp.h > bindings/wave.h 52 | rm bindings/wave_tmp.h 53 | 54 | wasm2c: 55 | cd rlbox_wasm2c_sandbox && cmake -S . -B ./build 56 | cd rlbox_wasm2c_sandbox && cmake --build ./build --target all 57 | 58 | fuzz_trusted: 59 | mkdir -p fuzz-dir 60 | bash scan_for_trusted.sh 61 | RUST_LOG=quickcheck cargo test -- --nocapture 62 | rm -r fuzz-dir 63 | 64 | 65 | NATIVE_BUILD=linux32-i386-clang linux32-i386-clangzerocost 66 | NACL_BUILDS=linux32-i386-nacl 67 | SPEC_BUILDS=$(NACL_BUILDS) $(NATIVE_BUILDS) 68 | 69 | 70 | $(SPEC_PATH): # libnsl/build/lib/libnsl.so.1 71 | git clone git@github.com:PLSysSec/wave-specbenchmark.git 72 | cd $(SPEC_PATH) && SPEC_INSTALL_NOCHECK=1 SPEC_FORCE_INSTALL=1 sh install.sh -f 73 | 74 | # TODO: use parallel compilation? remove unnecessary options? 75 | build_spec: $(SPEC_PATH) 76 | cd $(SPEC_PATH) && source ./shrc && \ 77 | cd config && \ 78 | runspec --config=linux64-amd64-clang.cfg --action=build --define cores=1 --iterations=1 --noreportable --size=ref wasm_compatible 79 | runspec --config=wasmtime.cfg --action=build --define cores=1 --iterations=1 --noreportable --size=ref wasm_compatible 80 | runspec --config=wasm2c_wave.cfg --action=build --define cores=1 --iterations=1 --noreportable --size=ref wasm_compatible 81 | 82 | # echo "Cleaning dirs" && \ 83 | # for spec_build in $(SPEC_BUILDS); do \ 84 | # runspec --config=$$spec_build.cfg --action=clobber all_c_cpp 2&>1 > /dev/null; \ 85 | # done && \ 86 | # 2>&1 | grep -i "building" 87 | 88 | # TODO: change size of spec runs back to size=ref 89 | # TODO: finalize 90 | run_spec: 91 | cd $(SPEC_PATH) && source ./shrc && cd config && \ 92 | runspec --config=wasm2c_wave.cfg --wasm2c_wave --action=run --define cores=1 --iterations=1 --noreportable --size=test wasm_compatible 93 | #for spec_build in $(NATIVE_BUILDS); do \ 94 | # runspec --config=$$spec_build.cfg --action=run --define cores=1 --iterations=1 --noreportable --size=ref all_c_cpp; \ 95 | #done && \ 96 | #for spec_build in $(NACL_BUILDS); do \ 97 | # runspec --config=$$spec_build.cfg --action=run --define cores=1 --iterations=1 --noreportable --size=ref --nacl all_c_cpp; \ 98 | #done 99 | #python3 spec_stats.py -i $(SPEC_PATH)/result --filter \ 100 | # "$(SPEC_PATH)/result/spec_results=Stock:Stock,NaCl:NaCl,SegmentZero:SegmentZero" -n 3 --usePercent 101 | #mv $(SPEC_PATH)/result/ benchmarks/spec_$(shell date --iso=seconds) 102 | 103 | -------------------------------------------------------------------------------- /src/path_resolution.rs: -------------------------------------------------------------------------------- 1 | use crate::tcb::path::*; 2 | #[cfg(feature = "verify")] 3 | use crate::tcb::verifier::*; 4 | use crate::types::*; 5 | use owned_components::{readlinkat, OwnedComponent, OwnedComponents}; 6 | use prusti_contracts::*; 7 | use std::ffi::OsString; 8 | use std::os::unix::ffi::OsStringExt; 9 | use std::path::{Component, Path, PathBuf}; 10 | 11 | const MAXSYMLINKS: isize = 10; 12 | 13 | // #[pure] 14 | // #[ensures()] 15 | fn to_pathbuf(v: Vec) -> PathBuf { 16 | PathBuf::from(OsString::from_vec(v.clone())) 17 | } 18 | 19 | // #[ensures(!is_symlink(out_path))] 20 | #[ensures( 21 | match &result { 22 | Ok(v) => forall(|i: usize| (i < v.len() - 1) ==> !is_symlink(v.prefix(i)) ) && 23 | (!should_follow || (should_follow && !is_symlink(&v))), 24 | _ => true, 25 | } 26 | )] 27 | fn expand_path(vec: Vec, should_follow: bool, dirfd: HostFd) -> RuntimeResult { 28 | let p = to_pathbuf(vec); 29 | let components = get_components(&p); 30 | 31 | let mut out_path = fresh_components(); 32 | let mut num_symlinks = 0; 33 | let mut idx = 0; 34 | 35 | while idx < components.len() { 36 | body_invariant!(!is_symlink(&out_path)); 37 | // out_path should never contain symlinks 38 | body_invariant!(forall(|i: usize| i < out_path.len() ==> !is_symlink(out_path.prefix(i)) ) ); 39 | let comp = components[idx]; 40 | let c = OwnedComponent::from_borrowed(&comp); 41 | // if this is the last element, and we are NO_FOLLOW, then don't expand 42 | if !should_follow && idx + 1 == components.len() { 43 | out_path.push(c); 44 | break; 45 | } 46 | // if comp is a symlink, return path + update num_symlinks 47 | // if not, just extend out_path 48 | let maybe_linkpath = maybe_expand_component(dirfd, &mut out_path, c, &mut num_symlinks); 49 | 50 | if let Some(linkpath) = maybe_linkpath { 51 | expand_symlink(&mut out_path, linkpath, &mut num_symlinks, dirfd); 52 | } 53 | if num_symlinks >= MAXSYMLINKS { 54 | return Err(RuntimeError::Eloop); 55 | } 56 | idx += 1; 57 | } 58 | //assert!(!should_follow || (should_follow && !is_symlink(&out_path))); 59 | Ok(out_path) 60 | } 61 | 62 | #[ensures( 63 | match &result { 64 | Ok(v) => path_safe(&v, should_follow), 65 | _ => true, 66 | } 67 | )] 68 | pub fn resolve_path(path: Vec, should_follow: bool, dirfd: HostFd) -> RuntimeResult { 69 | // TODO: use ? when that works properly in Prusti 70 | let c = expand_path(path, should_follow, dirfd); 71 | 72 | let c = match c { 73 | Ok(oc) => oc, 74 | Err(e) => { 75 | return Err(e); 76 | } 77 | }; 78 | 79 | if c.len() <= 0 || !is_relative(&c) || min_depth(&c) < 0 { 80 | return Err(RuntimeError::Enotcapable); 81 | } 82 | 83 | match OwnedComponents::unparse(c) { 84 | Some(result_arr) => Ok(result_arr), 85 | _ => Err(RuntimeError::Enametoolong), 86 | } 87 | } 88 | 89 | // Recursively expands a symlink (without explicit recursion) 90 | // maintains a queue of path components to process 91 | #[requires(forall(|i: usize| (i < out_path.len()) ==> !is_symlink(out_path.prefix(i)) ))] 92 | #[requires(!is_symlink(out_path) )] 93 | #[ensures(!is_symlink(out_path))] 94 | #[ensures(forall(|i: usize| (i < out_path.len()) ==> !is_symlink(out_path.prefix(i)) ))] 95 | fn expand_symlink( 96 | out_path: &mut OwnedComponents, 97 | linkpath_components: OwnedComponents, 98 | num_symlinks: &mut isize, 99 | dirfd: HostFd, 100 | ) { 101 | let mut idx = 0; 102 | while idx < linkpath_components.len() { 103 | body_invariant!(!is_symlink(out_path)); 104 | // out_path should never contain symlinks 105 | body_invariant!(forall(|i: usize| i < out_path.len() ==> !is_symlink(out_path.prefix(i)))); 106 | if *num_symlinks >= MAXSYMLINKS { 107 | return; 108 | } 109 | let c = linkpath_components.lookup(idx); 110 | let maybe_linkpath = maybe_expand_component(dirfd, out_path, c, num_symlinks); 111 | if let Some(linkpath) = maybe_linkpath { 112 | expand_symlink(out_path, linkpath, num_symlinks, dirfd); 113 | } 114 | idx += 1; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /examples/stat/stat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | // struct stat { 12 | // dev_t st_dev; /* ID of device containing file */ 13 | // ino_t st_ino; /* inode number */ 14 | // mode_t st_mode; /* protection */ 15 | // nlink_t st_nlink; /* number of hard links */ 16 | // uid_t st_uid; /* user ID of owner */ 17 | // gid_t st_gid; /* group ID of owner */ 18 | // dev_t st_rdev; /* device ID (if special file) */ 19 | 20 | // off_t st_size; /* total size, in bytes */ 21 | // blksize_t st_blksize; /* blocksize for file system I/O */ 22 | // blkcnt_t st_blocks; /* number of 512B blocks allocated */ 23 | 24 | // time_t st_atime; /* time of last access */ 25 | // time_t st_mtime; /* time of last modification */ 26 | // time_t st_ctime; /* time of last status change */ 27 | // }; 28 | 29 | 30 | void pprint_stat(struct stat st){ 31 | printf("Device containing file : %llu\n", st.st_dev); 32 | printf("Inode # : %llu\n", st.st_ino); 33 | printf("Permissions # : %d\n", st.st_mode); 34 | printf("# of hard links : %llu\n", st.st_nlink); 35 | printf("UID : %d\n", st.st_uid); 36 | printf("GID : %d\n", st.st_gid); 37 | printf("Device ID (if special file) : %llu\n", st.st_rdev); 38 | printf("Size : %lld\n", st.st_size); 39 | printf("Block size : %ld\n", st.st_blksize); 40 | printf("Block count : %lld\n", st.st_blocks); 41 | printf("Last Access was : %s\n",ctime(&st.st_atime)); 42 | printf("Last Modification was : %s\n",ctime(&st.st_mtime)); 43 | printf("Last Status change was : %s\n",ctime(&st.st_ctime)); 44 | } 45 | 46 | int main() { 47 | int fd = open("./data/tmp.txt", O_RDONLY); 48 | struct stat st; 49 | 50 | // 1. Do initial stat 51 | if(fstat(fd, &st)){ 52 | printf("\nfstat error: [%s]\n",strerror(errno)); 53 | close(fd); 54 | return -1; 55 | } 56 | 57 | // printf("Device containing file : %llu\n", st.st_dev); 58 | // printf("Inode # : %llu\n", st.st_ino); 59 | // printf("Permissions # : %d\n", st.st_mode); 60 | // printf("# of hard links : %llu\n", st.st_nlink); 61 | // printf("UID : %d\n", st.st_uid); 62 | // printf("GID : %d\n", st.st_gid); 63 | // printf("Device ID (if special file) : %llu\n", st.st_rdev); 64 | // printf("Size : %lld\n", st.st_size); 65 | // printf("Block size : %ld\n", st.st_blksize); 66 | // printf("Block count : %lld\n", st.st_blocks); 67 | // printf("Last Access was : %s\n",ctime(&st.st_atime)); 68 | // printf("Last Modification was : %s\n",ctime(&st.st_mtime)); 69 | // printf("Last Status change was : %s\n",ctime(&st.st_ctime)); 70 | pprint_stat(st); 71 | 72 | // 2. Extend file by 1 byte 73 | off_t previous_size = st.st_size; 74 | ftruncate(fd, previous_size + 1); 75 | 76 | if(fstat(fd, &st)){ 77 | printf("\nfstat error: [%s]\n",strerror(errno)); 78 | close(fd); 79 | return -1; 80 | } 81 | 82 | pprint_stat(st); 83 | 84 | printf("Done!\n"); 85 | 86 | 87 | // 3. Update timestamp 88 | // - first my resetting to start of epoch with futimens 89 | // - then by resetting to current time with utimensat 90 | /* 91 | int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags); 92 | int futimens(int fd, const struct timespec times[2]); 93 | */ 94 | 95 | struct timespec atim = {0, 0}; 96 | struct timespec mtim = {0, 0}; 97 | struct timespec times[2] = {atim, mtim}; 98 | 99 | futimens(fd, times); 100 | 101 | if(fstat(fd, &st)){ 102 | printf("\nfstat error: [%s]\n",strerror(errno)); 103 | close(fd); 104 | return -1; 105 | } 106 | 107 | pprint_stat(st); 108 | 109 | 110 | utimensat(3, "./data/tmp.txt", NULL, 0); 111 | 112 | if(fstat(fd, &st)){ 113 | printf("\nfstat error: [%s]\n",strerror(errno)); 114 | close(fd); 115 | return -1; 116 | } 117 | 118 | pprint_stat(st); 119 | 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /src/iov.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "verify")] 2 | use crate::tcb::verifier::*; 3 | use crate::types::*; 4 | use crate::{effect, effects, unwrap_result}; 5 | use prusti_contracts::*; 6 | use wave_macros::{external_methods, with_ghost_var}; 7 | 8 | use RuntimeError::*; 9 | 10 | // #[macro_export] 11 | // macro_rules! effects_from_iovs { 12 | // ($trace:expr, $buf:expr) => { 13 | // $trace.len() == old($trace.len() + $buf.len()) && 14 | // forall(|i: usize| (i >= $trace.len()) || 15 | // { 16 | // if i < old($trace.len()) 17 | // { $trace.lookup(i) == old($trace.lookup(i)) } 18 | // else 19 | // { 20 | // let this = $buf.lookup(i - old($trace.len())); 21 | // let ev = $trace.lookup(i); 22 | // iov_eq(ev, &this) 23 | // } 24 | // } 25 | // ) 26 | // } 27 | // } 28 | 29 | // #[cfg(feature = "verify")] 30 | // predicate! { 31 | // pub fn effects_from_iovs(old_trace: &Trace, trace: &Trace, buf: &NativeIoVecs) -> bool { 32 | // trace.len() == old_trace.len() + buf.len() && 33 | // forall(|i: usize| (i < trace.len()) ==> 34 | // { 35 | // if i < old(trace.len()) 36 | // { trace.lookup(i) == old_trace.lookup(i) } 37 | // else 38 | // { 39 | // let this = buf.lookup(i - old_trace.len()); 40 | // let ev = trace.lookup(i); 41 | // iov_eq(ev, &this) 42 | // } 43 | // } 44 | // ) 45 | // } 46 | // } 47 | 48 | #[cfg(feature = "verify")] 49 | predicate! { 50 | pub fn iov_eq_read(ev: Effect, iov: &NativeIoVec) -> bool { 51 | match ev { 52 | effect!(ReadMem,addr,count) => 53 | addr == iov.iov_base && 54 | count == iov.iov_len, 55 | _ => false, 56 | } 57 | } 58 | } 59 | 60 | #[cfg(feature = "verify")] 61 | predicate! { 62 | pub fn iov_eq_write(ev: Effect, iov: &NativeIoVec) -> bool { 63 | match ev { 64 | effect!(WriteMem,addr,count) => 65 | addr == iov.iov_base && 66 | count == iov.iov_len, 67 | _ => false, 68 | } 69 | } 70 | } 71 | 72 | #[with_ghost_var(trace: &mut Trace)] 73 | #[requires(ctx_safe(ctx))] 74 | #[requires(trace_safe(trace, ctx))] 75 | #[ensures(ctx_safe(ctx))] 76 | #[ensures(trace_safe(trace, ctx))] 77 | #[ensures( 78 | match &result { 79 | Ok(wasm_iovs) => wasm_iovs.len() >= 0 && (forall(|idx: usize| (idx < wasm_iovs.len() && idx >= 0) ==> { 80 | let iov = wasm_iovs.lookup(idx); 81 | let buf = iov.iov_base; 82 | let cnt = iov.iov_len; 83 | // ctx.fits_in_lin_mem(buf, cnt, trace) 84 | (buf >= 0) && (cnt >= 0) && 85 | (buf as usize) + (cnt as usize) < LINEAR_MEM_SIZE && 86 | (buf <= buf + cnt) 87 | })), 88 | _ => true, 89 | } 90 | )] 91 | #[external_methods(push)] 92 | pub fn parse_iovs(ctx: &VmCtx, iovs: u32, iovcnt: u32) -> RuntimeResult { 93 | let mut i = 0; 94 | let mut wasm_iovs = WasmIoVecs::new(); 95 | while i < iovcnt { 96 | body_invariant!(ctx_safe(ctx)); 97 | body_invariant!(trace_safe(trace, ctx)); 98 | body_invariant!(wasm_iovs.len() >= 0); 99 | body_invariant!( 100 | forall(|idx: usize| (idx < wasm_iovs.len() && idx >= 0) ==> { 101 | let iov = wasm_iovs.lookup(idx); 102 | let buf = iov.iov_base; 103 | let cnt = iov.iov_len; 104 | // ctx.fits_in_lin_mem(buf, cnt, trace) 105 | (buf >= 0) && (cnt >= 0) && 106 | (buf as usize) + (cnt as usize) < LINEAR_MEM_SIZE && 107 | (buf <= buf + cnt) 108 | }) 109 | 110 | ); 111 | 112 | let start = (iovs + i * 8) as usize; 113 | //TODO: Once we fix ? operatior - fix this 114 | //let (ptr, len) = ctx.read_u32_pair(start)?; 115 | let v = ctx.read_u32_pair(start); 116 | unwrap_result!(v); 117 | let (ptr, len) = v; 118 | 119 | if !ctx.fits_in_lin_mem(ptr, len) { 120 | return Err(Efault); 121 | } 122 | 123 | wasm_iovs.push(WasmIoVec { 124 | iov_base: ptr, 125 | iov_len: len, 126 | }); 127 | i += 1; 128 | } 129 | assert!(wasm_iovs.len() >= 0); 130 | Ok(wasm_iovs) 131 | } 132 | -------------------------------------------------------------------------------- /waverunner/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate dlopen_derive; 3 | mod types; 4 | mod waverunner; 5 | use crate::types::WaveConfig; 6 | use anyhow::{anyhow, Result}; 7 | use clap::{App, Arg}; 8 | use std::str::FromStr; 9 | use wave::tcb::misc::empty_netlist; 10 | use wave::types::{NetEndpoint, Netlist, WasiProto}; 11 | 12 | // Converts a space seperated string into a null-seperated Vec 13 | // also counts the number of words 14 | fn parse_argenv(s: String) -> (Vec, usize) { 15 | let mut buf = s.into_bytes(); 16 | let mut inword = false; 17 | let mut count = 0; 18 | for c in &mut buf { 19 | match c { 20 | 0 | 32 => { 21 | if inword { 22 | inword = false; 23 | count += 1; 24 | *c = 0; 25 | } 26 | } 27 | _ => inword = true, 28 | } 29 | } 30 | (buf, count) 31 | } 32 | 33 | // Parses a triple of the form protocol:ip:port 34 | fn parse_net_triple(s: &str) -> Result { 35 | // let (protocol_s, ip_s, port_s) = s.split(":").collect(); 36 | let triple: Vec<&str> = s.split(':').collect(); 37 | if triple.len() != 3 { 38 | return Err(anyhow!("Not 3 entries in net_triple: {}", s)); 39 | } 40 | let protocol_s = triple[0]; 41 | let ip_s = triple[1]; 42 | let port_s = triple[2]; 43 | 44 | let protocol = match protocol_s.to_lowercase().as_str() { 45 | "tcp" => WasiProto::Tcp, 46 | "udp" => WasiProto::Udp, 47 | _ => return Err(anyhow!("Unknown protocol: {}", protocol_s)), 48 | }; 49 | let addr: u32 = std::net::Ipv4Addr::from_str(ip_s)?.into(); 50 | let port = u32::from_str(port_s)?; 51 | Ok(NetEndpoint { 52 | protocol, 53 | addr, 54 | port, 55 | }) 56 | } 57 | 58 | // Parses a comma-seperated string of triples of the form protocol:ip:port 59 | fn parse_netlist(s: String) -> Result { 60 | let mut netlist = empty_netlist(); 61 | if s.is_empty() { 62 | return Ok(netlist); 63 | } 64 | for (idx, triple_str) in s.split(',').enumerate() { 65 | if idx >= netlist.len() { 66 | return Err(anyhow!("Too many net endpoints for the allow list")); 67 | }; 68 | netlist[idx] = parse_net_triple(triple_str)?; 69 | } 70 | Ok(netlist) 71 | } 72 | 73 | fn main() { 74 | let matches = App::new("Wave Runner") 75 | .version("0.1.0") 76 | .about("Runs Wasm code -- safely!") 77 | .arg( 78 | Arg::new("module path") 79 | .takes_value(true) 80 | .help("path to native Wasm module to run") 81 | .required(true), 82 | ) 83 | .arg( 84 | Arg::new("homedir") 85 | .long("homedir") 86 | .takes_value(true) 87 | .help("Home directory") 88 | .required(true), 89 | ) 90 | .arg( 91 | Arg::new("netlist") 92 | .long("netlist") 93 | .takes_value(true) 94 | .help("Allow-list for net endpoints that the Wasm application"), 95 | ) 96 | .arg( 97 | Arg::new("args") 98 | .long("args") 99 | .takes_value(true) 100 | .help("Arguments to pass to sandbox (space seperated)"), 101 | ) 102 | .arg( 103 | Arg::new("env") 104 | .long("env") 105 | .takes_value(true) 106 | .help("Environment to pass to sandbox (space seperated key-value pairs)"), 107 | ) 108 | .get_matches(); 109 | 110 | let module_path = matches.value_of("module path").unwrap().to_string(); 111 | let homedir = matches.value_of("homedir").unwrap().to_string(); 112 | let netlist_str = matches.value_of("netlist").unwrap_or("").to_string(); 113 | let args_str = matches.value_of("args").unwrap_or("").to_string(); 114 | let env_str = matches.value_of("env").unwrap_or("").to_string(); 115 | 116 | let (arg_buffer, argc) = parse_argenv(args_str); 117 | let (env_buffer, envc) = parse_argenv(env_str); 118 | let netlist = parse_netlist(netlist_str).unwrap(); 119 | 120 | let config = WaveConfig { 121 | module_path, 122 | homedir, 123 | netlist, 124 | args: arg_buffer, 125 | argc, 126 | env: env_buffer, 127 | envc, 128 | }; 129 | 130 | println!("{:?}", config); 131 | 132 | waverunner::run(&config); 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/types/platform/linux.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::tcb::misc::*; 3 | use libc; 4 | use std::convert::TryFrom; 5 | 6 | impl From for i32 { 7 | fn from(advice: Advice) -> Self { 8 | match advice { 9 | Advice::Normal => libc::POSIX_FADV_NORMAL, 10 | Advice::Sequential => libc::POSIX_FADV_SEQUENTIAL, 11 | Advice::Random => libc::POSIX_FADV_RANDOM, 12 | Advice::WillNeed => libc::POSIX_FADV_WILLNEED, 13 | Advice::DontNeed => libc::POSIX_FADV_DONTNEED, 14 | Advice::NoReuse => libc::POSIX_FADV_NOREUSE, 15 | } 16 | } 17 | } 18 | 19 | impl TryFrom for Advice { 20 | type Error = RuntimeError; 21 | fn try_from(advice: i32) -> RuntimeResult { 22 | match advice { 23 | libc::POSIX_FADV_NORMAL => Ok(Advice::Normal), 24 | libc::POSIX_FADV_SEQUENTIAL => Ok(Advice::Sequential), 25 | libc::POSIX_FADV_RANDOM => Ok(Advice::Random), 26 | libc::POSIX_FADV_WILLNEED => Ok(Advice::WillNeed), 27 | libc::POSIX_FADV_DONTNEED => Ok(Advice::DontNeed), 28 | libc::POSIX_FADV_NOREUSE => Ok(Advice::NoReuse), 29 | _ => Err(RuntimeError::Einval), 30 | } 31 | } 32 | } 33 | 34 | impl FdFlags { 35 | pub fn to_posix(&self) -> i32 { 36 | let mut flags = 0; 37 | if nth_bit_set(self.0, 0) { 38 | flags = bitwise_or(flags, libc::O_APPEND) 39 | } 40 | if nth_bit_set(self.0, 1) { 41 | flags = bitwise_or(flags, libc::O_DSYNC) 42 | } 43 | if nth_bit_set(self.0, 2) { 44 | flags = bitwise_or(flags, libc::O_NONBLOCK) 45 | } 46 | if nth_bit_set(self.0, 3) { 47 | flags = bitwise_or(flags, libc::O_RSYNC) 48 | } 49 | if nth_bit_set(self.0, 4) { 50 | flags = bitwise_or(flags, libc::O_SYNC) 51 | } 52 | flags 53 | } 54 | 55 | pub fn from_posix(flags: i32) -> Self { 56 | // FdFlags(flags as u16) 57 | //let mut result = FdFlags(0); 58 | let mut result = FdFlags(0); 59 | if bitwise_and(flags, libc::O_APPEND) != 0 { 60 | result.0 = with_nth_bit_set(result.0, 0); 61 | } 62 | if bitwise_and(flags, libc::O_DSYNC) != 0 { 63 | result.0 = with_nth_bit_set(result.0, 1); 64 | } 65 | if bitwise_and(flags, libc::O_NONBLOCK) != 0 { 66 | result.0 = with_nth_bit_set(result.0, 2); 67 | } 68 | if bitwise_and(flags, libc::O_RSYNC) != 0 { 69 | result.0 = with_nth_bit_set(result.0, 3); 70 | } 71 | if bitwise_and(flags, libc::O_SYNC) != 0 { 72 | result.0 = with_nth_bit_set(result.0, 4); 73 | } 74 | result 75 | } 76 | } 77 | 78 | impl Dirent { 79 | #[requires(in_idx < host_buf.len())] 80 | pub fn parse(host_buf: &Vec, in_idx: usize) -> RuntimeResult { 81 | // Inode number 82 | let d_ino = u64::from_le_bytes([ 83 | host_buf[in_idx + 0], 84 | host_buf[in_idx + 1], 85 | host_buf[in_idx + 2], 86 | host_buf[in_idx + 3], 87 | host_buf[in_idx + 4], 88 | host_buf[in_idx + 5], 89 | host_buf[in_idx + 6], 90 | host_buf[in_idx + 7], 91 | ]); 92 | 93 | // Offset to next linux_dirent 94 | let d_offset = u64::from_le_bytes([ 95 | host_buf[in_idx + 8], 96 | host_buf[in_idx + 9], 97 | host_buf[in_idx + 10], 98 | host_buf[in_idx + 11], 99 | host_buf[in_idx + 12], 100 | host_buf[in_idx + 13], 101 | host_buf[in_idx + 14], 102 | host_buf[in_idx + 15], 103 | ]); 104 | 105 | // File type 106 | let d_type = u8::from_le_bytes([host_buf[in_idx + 18]]); 107 | 108 | // Length of this linux_dirent 109 | let d_reclen = u16::from_le_bytes([host_buf[in_idx + 16], host_buf[in_idx + 17]]); 110 | 111 | // If we would overflow - don't :) 112 | if d_reclen < 19 || (in_idx + d_reclen as usize) > host_buf.len() { 113 | return Err(RuntimeError::Eoverflow); 114 | } 115 | 116 | let out_namlen = first_null(&host_buf, in_idx, 19, d_reclen as usize); 117 | // let out_namlen = 3; 118 | 119 | let dirent = Dirent { 120 | ino: d_ino, 121 | reclen: d_reclen, 122 | name_start: 19, 123 | out_namlen, 124 | typ: d_type, 125 | }; 126 | 127 | Ok(dirent) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/tests/memwrite_experiment.rs: -------------------------------------------------------------------------------- 1 | use crate::os; 2 | use crate::runtime::fresh_ctx; 3 | use crate::types::{SboxPtr, VmCtx}; 4 | use std::time::{Duration, Instant}; 5 | use std::ptr::{copy, copy_nonoverlapping}; 6 | use quickcheck::{QuickCheck, TestResult}; 7 | use quickcheck_macros; 8 | 9 | /* 10 | The goal of these experiments are to compare the correctness and performance of 7 different means 11 | by which we could write data to mem. 12 | I will use this information to select an implementation for how wave will read/write to memory. 13 | The methods are: 14 | 1. repeated 1-byte writes 15 | 2. vector writer 16 | 3. vector writer-vectored 17 | 4. direct pointer manipulation 18 | 5. vector::splice 19 | 6. copy_nonoverlapping (memcpy) 20 | 7. copy (memmove) 21 | 22 | 23 | All of these will be tested both with small and large writes, specifically: 24 | 1. 4 byte write 25 | 2. 8 byte write 26 | 3. 8-8 byte write (random) 27 | 4. 64 byte write (any way) 28 | 5. 4096 byte write (any way) 29 | */ 30 | 31 | /* 32 | 1 small write 33 | repeated small writes 34 | big writes 35 | */ 36 | 37 | /* 38 | let now = Instant::now(); 39 | now.elapsed().as_nanos() 40 | */ 41 | 42 | // Method 1: repeated writes 43 | fn write_method_1(ctx: &mut VmCtx, src: &[u8], offset:usize){ 44 | for idx in 0..src.len() { 45 | ctx[offset + idx] = src[idx]; 46 | } 47 | } 48 | 49 | fn write_method_1_u32(ctx: &mut VmCtx, v: u32, offset:usize){ 50 | let bytes: [u8; 4] = v.to_le_bytes(); 51 | self.mem[offset] = bytes[0]; 52 | self.mem[offset + 1] = bytes[1]; 53 | self.mem[offset + 2] = bytes[2]; 54 | self.mem[offset + 3] = bytes[3]; 55 | } 56 | 57 | fn write_method_1_u64(ctx: &mut VmCtx, v: u64, offset:usize){ 58 | let bytes: [u8; 8] = v.to_le_bytes(); 59 | self.mem[offset] = bytes[0]; 60 | self.mem[offset + 1] = bytes[1]; 61 | self.mem[offset + 2] = bytes[2]; 62 | self.mem[offset + 3] = bytes[3]; 63 | self.mem[offset + 4] = bytes[4]; 64 | self.mem[offset + 5] = bytes[5]; 65 | self.mem[offset + 6] = bytes[6]; 66 | self.mem[offset + 7] = bytes[7]; 67 | } 68 | 69 | // Method 2: vector write 70 | fn write_method_2(ctx: &mut VmCtx, src: &[u8], offset:usize){ 71 | (ctx.mem.get(offset)).write(src); 72 | ctx.mem.flush(); 73 | } 74 | 75 | // Method 4: direct pointer manipulation 76 | fn write_method_4_u32(ctx: &mut VmCtx, v: u32, offset:usize){ 77 | unsafe{ ptr::write(ctx.mem.as_mut_ptr().offset(offset), v); } 78 | } 79 | 80 | fn write_method_4_u64(ctx: &mut VmCtx, v: u64, offset:usize){ 81 | unsafe{ ptr::write(ctx.mem.as_mut_ptr().offset(offset), v); } 82 | } 83 | 84 | // Method 5: vector::splice 85 | fn write_method_5(ctx: &mut VmCtx, src: &[u8], offset:usize){ 86 | ctx.mem.splice(offset..offset+src.len(), src); 87 | 88 | } 89 | 90 | // Method 6: copy_non_overlapping (memcpy) 91 | fn write_method_6(ctx: &mut VmCtx, src: &[u8], offset:usize){ 92 | unsafe { 93 | copy_nonoverlapping( 94 | src.as_ptr(), 95 | self.mem.as_mut_ptr().offset(offset as isize), 96 | src.len(), 97 | ) 98 | }; 99 | } 100 | 101 | // Method 7: copy (memmove) 102 | fn write_method_7(ctx: &mut VmCtx, src: &[u8], offset:usize){ 103 | unsafe { 104 | copy( 105 | src.as_ptr(), 106 | self.mem.as_mut_ptr().offset(offset as isize), 107 | src.len(), 108 | ) 109 | }; 110 | } 111 | 112 | // Compute lag on using Instant::now 113 | fn trial_0(){ 114 | //let ctx = fresh_ctx(); 115 | let total_time = 0; 116 | // Run 1000 trials 117 | for idx in 0..1000{ 118 | let now = Instant::now(); 119 | let time = now.elapsed().as_nanos(); 120 | total_time += time; 121 | } 122 | println!("Average delay: {:?}", total_time / 1000); 123 | } 124 | 125 | // Testing 32 bit writes using safe method vs unsafe method 126 | fn trial_1_safe(){ 127 | let ctx = fresh_ctx(); 128 | let total_time = 0; 129 | // Run 1000 trials 130 | for idx in 0..1000{ 131 | let addr = idx * 8; 132 | let now = Instant::now(); 133 | write_method_1_u32(ctx, 1337, addr); 134 | let time = now.elapsed().as_nanos(); 135 | total_time += time; 136 | } 137 | println!("Average safe 32-bit write delay: {:?}", total_time / 1000); 138 | } 139 | 140 | fn trial_1_unsafe(){ 141 | let ctx = fresh_ctx(); 142 | let total_time = 0; 143 | // Run 1000 trials 144 | for idx in 0..1000{ 145 | let addr = idx * 8; 146 | let now = Instant::now(); 147 | write_method_4_u32(ctx, 1337, addr); 148 | let time = now.elapsed().as_nanos(); 149 | total_time += time; 150 | } 151 | println!("Average unsafe 32-bit write delay: {:?}", total_time / 1000); 152 | } 153 | 154 | -------------------------------------------------------------------------------- /owned-components/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Component, Path, PathBuf}; 2 | // use std::ffi::{OsStr, OsString}; 3 | use std::os::unix::ffi::{OsStringExt, OsStrExt}; 4 | use std::io::{Error, ErrorKind}; 5 | use std::io; 6 | use std::ffi::{CStr, CString, OsStr, OsString}; 7 | //use libc; 8 | 9 | // Represents an Owned version of a Component<'a> 10 | // Currently only works for *nix (neglects the prefix component present on windows) 11 | #[cfg_attr(not(feature = "verify"), derive(Debug))] 12 | #[derive(Clone, PartialEq, Eq)] 13 | pub enum OwnedComponent { 14 | /// The root directory component, appears before anything else. 15 | /// 16 | /// It represents a separator that designates that a path starts from root. 17 | RootDir, 18 | 19 | /// A reference to the current directory, i.e., `.`. 20 | CurDir, 21 | 22 | /// A reference to the parent directory, i.e., `..`. 23 | ParentDir, 24 | 25 | /// A normal component, e.g., `a` and `b` in `a/b`. 26 | /// 27 | /// This variant is the most common one, it represents references to files 28 | /// or directories. 29 | Normal(OsString), 30 | } 31 | 32 | impl OwnedComponent { 33 | pub fn from_borrowed(c: &Component) -> Self { 34 | match c { 35 | Component::Prefix(_) => panic!("OwnedComponents do not support prefix"), 36 | Component::RootDir => OwnedComponent::RootDir, 37 | Component::CurDir => OwnedComponent::CurDir, 38 | Component::ParentDir => OwnedComponent::ParentDir, 39 | Component::Normal(s) => OwnedComponent::Normal(s.to_os_string()), 40 | } 41 | } 42 | 43 | // https://doc.rust-lang.org/stable/src/std/path.rs.html#548-556 line 548 44 | pub fn as_os_string(&self) -> &OsStr { 45 | match self { 46 | OwnedComponent::RootDir => OsStr::new("/"), 47 | OwnedComponent::CurDir => OsStr::new("."), 48 | OwnedComponent::ParentDir => OsStr::new(".."), 49 | OwnedComponent::Normal(path) => path, 50 | } 51 | } 52 | } 53 | 54 | #[cfg_attr(not(feature = "verify"), derive(Debug))] 55 | #[derive(Clone, PartialEq, Eq)] 56 | pub struct OwnedComponents { 57 | pub inner: Vec 58 | } 59 | 60 | impl OwnedComponents { 61 | pub fn new() -> Self { 62 | Self { 63 | inner: Vec::new() 64 | } 65 | } 66 | // 1 copy: 67 | pub fn as_pathbuf(&self) -> PathBuf { 68 | // let s: OsString = self.inner.iter().map(|oc| oc.as_os_string()).collect(); 69 | self.inner.iter().map(|oc| oc.as_os_string()).collect() 70 | } 71 | 72 | // 1 copy 73 | pub fn parse(p: PathBuf) -> Self { 74 | let inner: Vec = p.components().map(|c| 75 | OwnedComponent::from_borrowed(&c)).collect(); 76 | Self{ inner } 77 | } 78 | 79 | // Currently requires two copies 80 | pub fn unparse(self) -> Option<[u8; 4096]> { 81 | // let s: PathBuf = self.inner.iter().map(|oc| oc.as_os_string()).collect(); 82 | let s = self.as_pathbuf().into_os_string(); 83 | // let s = s.into_os_string(); 84 | if s.len() >= 4096 { 85 | return None; 86 | } 87 | let mut out = [0; 4096]; 88 | for (dst, src) in out.iter_mut().zip(s.as_bytes()) { *dst = *src } 89 | // out.copy_from_slice(s.as_bytes()); 90 | Some(out) 91 | } 92 | 93 | pub fn lookup(&self, idx: usize) -> OwnedComponent { 94 | self.inner[idx].clone() 95 | } 96 | 97 | pub fn push(&mut self, c: OwnedComponent) { 98 | self.inner.push(c) 99 | } 100 | 101 | pub fn pop(&mut self) -> Option { 102 | self.inner.pop() 103 | } 104 | 105 | pub fn len(&self) -> usize { 106 | self.inner.len() 107 | } 108 | 109 | pub fn prefix(&self, end: usize) -> &OwnedComponents { 110 | unimplemented!() 111 | } 112 | } 113 | 114 | 115 | fn cstr(path: &Path) -> io::Result { 116 | Ok(CString::new(path.as_os_str().as_bytes())?) 117 | } 118 | 119 | // taken from https://benaaron.dev/rust-docs/src/std/sys/unix/fs.rs.html#1109-1134 120 | // TODO: the principled way to do this is to use our os spec 121 | pub fn readlinkat(fd: usize, p: &Path) -> io::Result { 122 | let c_path = cstr(p)?; 123 | let p = c_path.as_ptr(); 124 | 125 | let mut buf = Vec::with_capacity(4096); 126 | 127 | let buf_read = unsafe { libc::readlinkat(fd as i32, p, buf.as_mut_ptr() as *mut _, buf.capacity()) }; 128 | if buf_read == -1 { 129 | return Err(Error::new(ErrorKind::Other, "path translation readlink failure!")); 130 | } 131 | let buf_read = buf_read as usize; 132 | 133 | unsafe { 134 | buf.set_len(buf_read); 135 | } 136 | 137 | if buf_read != buf.capacity() { 138 | buf.shrink_to_fit(); 139 | } 140 | return Ok(PathBuf::from(OsString::from_vec(buf))); 141 | 142 | 143 | } 144 | 145 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/specifications/json.rs: -------------------------------------------------------------------------------- 1 | use super::untyped; 2 | use serde::{Deserialize, Serialize}; 3 | use super::common; 4 | 5 | #[derive(Serialize, Deserialize)] 6 | pub struct Assertion { 7 | pub kind: Box, 8 | } 9 | 10 | #[derive(Serialize, Deserialize)] 11 | pub enum AssertionKind { 12 | Expr(Expression), 13 | And(Vec), 14 | Implies(Assertion, Assertion), 15 | ForAll(QuantifierVars, Assertion, TriggerSet), 16 | Exists(QuantifierVars, Assertion, TriggerSet), 17 | SpecEntailment { 18 | closure: Expression, 19 | arg_binders: SpecEntailmentVars, 20 | pres: Vec, 21 | posts: Vec, 22 | }, 23 | } 24 | 25 | #[derive(Serialize, Deserialize)] 26 | pub struct Expression { 27 | /// Identifier of the specification to which this expression belongs. 28 | pub spec_id: untyped::SpecificationId, 29 | /// Identifier of the expression within the specification. 30 | pub expr_id: untyped::ExpressionId, 31 | } 32 | 33 | #[derive(Serialize, Deserialize)] 34 | pub struct QuantifierVars { 35 | pub spec_id: untyped::SpecificationId, 36 | pub expr_id: untyped::ExpressionId, 37 | pub count: usize, 38 | } 39 | 40 | #[derive(Serialize, Deserialize)] 41 | pub struct SpecEntailmentVars { 42 | pub spec_id: untyped::SpecificationId, 43 | pub pre_expr_id: untyped::ExpressionId, 44 | pub post_expr_id: untyped::ExpressionId, 45 | pub arg_count: usize, 46 | } 47 | 48 | #[derive(Serialize, Deserialize)] 49 | pub struct TriggerSet(pub Vec); 50 | 51 | #[derive(Serialize, Deserialize)] 52 | pub struct Trigger(pub Vec); 53 | 54 | impl untyped::Expression { 55 | fn to_structure(&self) -> Expression { 56 | Expression { 57 | spec_id: self.spec_id, 58 | expr_id: self.id, 59 | } 60 | } 61 | } 62 | 63 | impl common::QuantifierVars { 64 | fn to_structure(&self) -> QuantifierVars { 65 | QuantifierVars { 66 | spec_id: self.spec_id, 67 | count: self.vars.len(), 68 | expr_id: self.id, 69 | } 70 | } 71 | } 72 | 73 | impl common::SpecEntailmentVars { 74 | fn to_structure(&self) -> SpecEntailmentVars { 75 | SpecEntailmentVars { 76 | spec_id: self.spec_id, 77 | arg_count: self.args.len(), 78 | pre_expr_id: self.pre_id, 79 | post_expr_id: self.post_id, 80 | } 81 | } 82 | } 83 | 84 | impl untyped::TriggerSet { 85 | fn to_structure(&self) -> TriggerSet { 86 | TriggerSet(self.0.clone() 87 | .into_iter() 88 | .map(|x| x.to_structure()) 89 | .collect() 90 | ) 91 | } 92 | } 93 | 94 | impl common::Trigger { 95 | fn to_structure(&self) -> Trigger { 96 | Trigger(self.0 97 | .clone() 98 | .into_iter() 99 | .map(|x| x.to_structure()) 100 | .collect() 101 | ) 102 | } 103 | } 104 | 105 | impl untyped::AssertionKind { 106 | fn to_structure(&self) -> AssertionKind { 107 | use super::common::AssertionKind::*; 108 | match self { 109 | Expr(expr) => AssertionKind::Expr(expr.to_structure()), 110 | And(assertions) => { 111 | AssertionKind::And( 112 | assertions.iter() 113 | .map(|assertion| Assertion { kind: Box::new(assertion.kind.to_structure()) }) 114 | .collect() 115 | ) 116 | } 117 | Implies(lhs, rhs) => AssertionKind::Implies( 118 | lhs.to_structure(), 119 | rhs.to_structure() 120 | ), 121 | ForAll(vars, triggers, body) => AssertionKind::ForAll( 122 | vars.to_structure(), 123 | body.to_structure(), 124 | triggers.to_structure(), 125 | ), 126 | Exists(vars, triggers, body) => AssertionKind::Exists( 127 | vars.to_structure(), 128 | body.to_structure(), 129 | triggers.to_structure(), 130 | ), 131 | SpecEntailment {closure, arg_binders, pres, posts} => AssertionKind::SpecEntailment { 132 | closure: closure.to_structure(), 133 | arg_binders: arg_binders.to_structure(), 134 | pres: pres.iter().map(|pre| pre.to_structure()).collect(), 135 | posts: posts.iter().map(|post| post.to_structure()).collect(), 136 | }, 137 | x => { 138 | unimplemented!("{:?}", x); 139 | } 140 | } 141 | } 142 | } 143 | 144 | impl untyped::Assertion { 145 | fn to_structure(&self) -> Assertion { 146 | Assertion { 147 | kind: Box::new(self.kind.to_structure()), 148 | } 149 | } 150 | } 151 | 152 | pub fn to_json_string(assertion: &untyped::Assertion) -> String { 153 | serde_json::to_string(&assertion.to_structure()).unwrap() 154 | } 155 | 156 | impl Assertion { 157 | pub fn from_json_string(json: &str) -> Self { 158 | serde_json::from_str(json).unwrap() 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/tcb/os_specs/platform/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::iov::*; 2 | #[cfg(feature = "time_syscalls")] 3 | use crate::stats::timing::{push_syscall_result, start_timer, stop_timer}; 4 | use crate::syscall_spec_gen; 5 | use crate::tcb::misc::flag_set; 6 | use crate::tcb::sbox_mem::raw_ptr; 7 | #[cfg(feature = "verify")] 8 | use crate::tcb::verifier::*; 9 | use crate::types::NativeIoVecs; 10 | #[cfg(not(feature = "time_syscalls"))] 11 | use crate::verifier_interface::{push_syscall_result, start_timer, stop_timer}; 12 | use crate::{effect, effects, path_effect}; 13 | use prusti_contracts::*; 14 | use syscall::syscall; 15 | use wave_macros::{external_call, external_method, with_ghost_var}; 16 | 17 | use paste::paste; 18 | 19 | //https://man7.org/linux/man-pages/man2/pread.2.html 20 | syscall_spec_gen! { 21 | trace; 22 | requires((buf.len() >= cnt)); 23 | ensures((effects!(old(trace), trace, effect!(FdAccess), effect!(WriteMem, addr, count) if addr == old(raw_ptr(buf)) && count == cnt))); 24 | ensures((old(raw_ptr(buf)) == raw_ptr(buf))); 25 | syscall(pread64 ALIAS pread, fd: usize, buf: (&mut [u8]), cnt: usize, offset: usize) 26 | } 27 | 28 | //https://man7.org/linux/man-pages/man2/pwrite.2.html 29 | syscall_spec_gen! { 30 | trace; 31 | requires((buf.len() >= cnt)); 32 | ensures((effects!(old(trace), trace, effect!(FdAccess), effect!(ReadMem, addr, count) if addr == old(raw_ptr(buf)) && count == cnt))); 33 | syscall(pwrite64 ALIAS pwrite, fd: usize, buf: (&[u8]), cnt: usize, offset: usize) 34 | } 35 | 36 | //https://man7.org/linux/man-pages/man2/fadvise64.2.html 37 | syscall_spec_gen! { 38 | trace; 39 | ensures((effects!(old(trace), trace, effect!(FdAccess)))); 40 | syscall(fadvise64, fd: usize, offset: i64, len: i64, advice: i32) 41 | } 42 | 43 | // https://man7.org/linux/man-pages/man2/fallocate.2.html 44 | // hardcode mode to 0 to behave more like posix_fallocate 45 | syscall_spec_gen! { 46 | trace; 47 | ensures((effects!(old(trace), trace, effect!(FdAccess)))); 48 | syscall(fallocate, fd: usize, mode: i32, offset: i64, len: i64) 49 | } 50 | 51 | // https://man7.org/linux/man-pages/man2/fstatat.2.html 52 | // follows terminal symlink if O_NOFOLLOW are not set 53 | // this is the only lookupflags, so we just say flags == 0 54 | syscall_spec_gen! { 55 | trace; 56 | ensures((effects!(old(trace), trace, effect!(FdAccess), path_effect!(PathAccessAt, fd, p, f) if fd == dirfd && p == old(path) && f == !flag_set(flags, libc::AT_SYMLINK_NOFOLLOW)))); 57 | syscall(newfstatat ALIAS fstatat, dirfd: usize, path: [u8; 4096], stat: (&mut libc::stat), flags: i32) 58 | } 59 | 60 | //https://man7.org/linux/man-pages/man2/utimensat.2.html 61 | #[with_ghost_var(trace: &mut Trace)] 62 | #[requires(specs.len() >= 2)] 63 | #[trusted] 64 | #[ensures(effects!(old(trace), trace, effect!(FdAccess)))] 65 | pub fn os_futimens(fd: usize, specs: &Vec) -> isize { 66 | let __start_ts = start_timer(); 67 | // Linux impls futimens as UTIMENSAT with null path 68 | // source: https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/futimens.c.html 69 | let result = unsafe { syscall!(UTIMENSAT, fd, 0, specs.as_ptr(), 0) as isize }; 70 | let __end_ts = stop_timer(); 71 | push_syscall_result("futimens", __start_ts, __end_ts); 72 | result 73 | } 74 | 75 | // https://man7.org/linux/man-pages/man2/utimensat.2.html 76 | syscall_spec_gen! { 77 | trace; 78 | requires((specs.len() >= 2)); 79 | ensures((effects!(old(trace), trace, effect!(FdAccess), path_effect!(PathAccessAt, fd, p, f) if fd == dirfd && p == old(path) && f == !flag_set(flags, libc::AT_SYMLINK_NOFOLLOW)))); 80 | syscall(utimensat, dirfd: usize, path: [u8; 4096], specs: (&Vec), flags: (libc::c_int)) 81 | } 82 | 83 | //https://man7.org/linux/man-pages/man2/clock_gettime.2.html 84 | syscall_spec_gen! { 85 | trace; 86 | ensures((effects!(old(trace), trace))); 87 | syscall(clock_gettime, clock_id: (libc::clockid_t), spec: (&mut libc::timespec)) 88 | } 89 | 90 | //https://man7.org/linux/man-pages/man2/clock_getres.2.html 91 | syscall_spec_gen! { 92 | trace; 93 | ensures((effects!(old(trace), trace))); 94 | syscall(clock_getres, clock_id: (libc::clockid_t), spec: (&mut libc::timespec)) 95 | } 96 | 97 | syscall_spec_gen! { 98 | trace; 99 | requires((buf.len() >= cnt)); 100 | ensures((effects!(old(trace), trace, effect!(WriteMem, addr, count) if addr == old(raw_ptr(buf)) && count == cnt))); 101 | ensures((old(raw_ptr(buf)) == raw_ptr(buf))); 102 | syscall(getrandom, buf: (&mut [u8]), cnt: usize, flags: u32) 103 | } 104 | 105 | //https://man7.org/linux/man-pages/man2/nanosleep.2.html 106 | syscall_spec_gen! { 107 | trace; 108 | ensures((effects!(old(trace), trace))); 109 | syscall(nanosleep, req: (&libc::timespec), rem: (&mut libc::timespec)) 110 | } 111 | 112 | //https://man7.org/linux/man-pages/man2/getdents64.2.html 113 | #[with_ghost_var(trace: &mut Trace)] 114 | #[external_method(set_len)] 115 | #[trusted] 116 | #[requires(dirp.capacity() >= count)] 117 | #[ensures(effects!(old(trace), trace, effect!(FdAccess)))] 118 | pub fn os_getdents64(fd: usize, dirp: &mut Vec, count: usize) -> isize { 119 | let __start_ts = start_timer(); 120 | let result = unsafe { 121 | let result = syscall!(GETDENTS64, fd, dirp.as_mut_ptr(), count); 122 | if (result as isize) != -1 { 123 | dirp.set_len(result); 124 | } else { 125 | dirp.set_len(0); 126 | } 127 | result as isize 128 | }; 129 | let __end_ts = stop_timer(); 130 | push_syscall_result("getdents64", __start_ts, __end_ts); 131 | result 132 | } 133 | 134 | //https://man7.org/linux/man-pages/man2/fstat.2.html 135 | syscall_spec_gen! { 136 | trace; 137 | ensures((effects!(old(trace), trace, effect!(FdAccess)))); 138 | syscall(fstat, fd: usize, stat: (&mut libc::stat)) 139 | } 140 | -------------------------------------------------------------------------------- /src/writeback.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "verify")] 2 | use crate::tcb::verifier::*; 3 | use crate::types::*; 4 | use prusti_contracts::*; 5 | use wave_macros::with_ghost_var; 6 | 7 | #[with_ghost_var(trace: &mut Trace)] 8 | pub fn wasm2c_marshal(res: RuntimeResult) -> u32 { 9 | match res { 10 | Ok(r) => 0, 11 | Err(err) => err.into(), 12 | } 13 | } 14 | 15 | #[with_ghost_var(trace: &mut Trace)] 16 | #[requires(ctx_safe(ctx))] 17 | #[requires(trace_safe(trace, ctx))] 18 | #[ensures(ctx_safe(ctx))] 19 | #[ensures(trace_safe(trace, ctx))] 20 | pub fn wasm2c_marshal_and_writeback_u32( 21 | ctx: &mut VmCtx, 22 | addr: usize, 23 | res: RuntimeResult, 24 | ) -> u32 { 25 | if !ctx.fits_in_lin_mem_usize(addr, 4) { 26 | return RuntimeError::Eoverflow.into(); 27 | } 28 | //log::debug!("wasm2c_marshal_and_writeback_u32: {:?}", result); 29 | match res { 30 | Ok(r) => { 31 | ctx.write_u32(addr, r); // writeback result 32 | 0 33 | } 34 | Err(err) => err.into(), 35 | } 36 | } 37 | 38 | #[with_ghost_var(trace: &mut Trace)] 39 | #[requires(ctx_safe(ctx))] 40 | #[requires(trace_safe(trace, ctx))] 41 | #[ensures(ctx_safe(ctx))] 42 | #[ensures(trace_safe(trace, ctx))] 43 | pub fn wasm2c_marshal_and_writeback_prestat( 44 | ctx: &mut VmCtx, 45 | addr: usize, 46 | res: RuntimeResult, 47 | ) -> u32 { 48 | if !ctx.fits_in_lin_mem_usize(addr, 12) { 49 | return RuntimeError::Eoverflow.into(); 50 | } 51 | //log::debug!("wasm2c_marshal_and_writeback_prestat: {:?}", result); 52 | match res { 53 | Ok(r) => { 54 | ctx.write_u32(addr, 0); 55 | ctx.write_u64(addr + 4, r as u64); // writeback result 56 | 0 57 | } 58 | Err(err) => err.into(), 59 | } 60 | } 61 | 62 | #[with_ghost_var(trace: &mut Trace)] 63 | #[requires(ctx_safe(ctx))] 64 | #[requires(trace_safe(trace, ctx))] 65 | #[ensures(ctx_safe(ctx))] 66 | #[ensures(trace_safe(trace, ctx))] 67 | pub fn wasm2c_marshal_and_writeback_u64( 68 | ctx: &mut VmCtx, 69 | addr: usize, 70 | res: RuntimeResult, 71 | ) -> u32 { 72 | if !ctx.fits_in_lin_mem_usize(addr, 8) { 73 | return RuntimeError::Eoverflow.into(); 74 | } 75 | //log::debug!("wasm2c_marshal_and_writeback_u64: {:?}", result); 76 | match res { 77 | Ok(r) => { 78 | ctx.write_u64(addr, r); // writeback result 79 | 0 80 | } 81 | Err(err) => err.into(), 82 | } 83 | } 84 | 85 | #[with_ghost_var(trace: &mut Trace)] 86 | #[requires(ctx_safe(ctx))] 87 | #[requires(trace_safe(trace, ctx))] 88 | #[ensures(ctx_safe(ctx))] 89 | #[ensures(trace_safe(trace, ctx))] 90 | pub fn wasm2c_marshal_and_writeback_timestamp( 91 | ctx: &mut VmCtx, 92 | addr: usize, 93 | res: RuntimeResult, 94 | ) -> u32 { 95 | if !ctx.fits_in_lin_mem_usize(addr, 8) { 96 | return RuntimeError::Eoverflow.into(); 97 | } 98 | //log::debug!("wasm2c_marshal_and_writeback_timestamp: {:?}", result); 99 | match res { 100 | Ok(r) => { 101 | ctx.write_u64(addr, r.nsec()); // writeback result 102 | 0 103 | } 104 | Err(err) => err.into(), 105 | } 106 | } 107 | 108 | #[with_ghost_var(trace: &mut Trace)] 109 | #[requires(ctx_safe(ctx))] 110 | #[requires(trace_safe(trace, ctx))] 111 | #[ensures(ctx_safe(ctx))] 112 | #[ensures(trace_safe(trace, ctx))] 113 | pub fn wasm2c_marshal_and_writeback_fdstat( 114 | ctx: &mut VmCtx, 115 | addr: usize, 116 | res: RuntimeResult, 117 | ) -> u32 { 118 | if !ctx.fits_in_lin_mem_usize(addr, 24) { 119 | return RuntimeError::Eoverflow.into(); 120 | } 121 | //log::debug!("wasm2c_marshal_and_writeback_fdstat: {:?}", result); 122 | match res { 123 | Ok(r) => { 124 | ctx.write_u16(addr, r.fs_filetype.to_wasi() as u16); 125 | ctx.write_u16(addr + 2, r.fs_flags.to_posix() as u16); 126 | ctx.write_u64(addr + 8, r.fs_rights_base); 127 | ctx.write_u64(addr + 16, r.fs_rights_inheriting); 128 | 0 129 | } 130 | Err(err) => err.into(), 131 | } 132 | } 133 | 134 | #[with_ghost_var(trace: &mut Trace)] 135 | #[requires(ctx_safe(ctx))] 136 | #[requires(trace_safe(trace, ctx))] 137 | #[ensures(ctx_safe(ctx))] 138 | #[ensures(trace_safe(trace, ctx))] 139 | pub fn wasm2c_marshal_and_writeback_filestat( 140 | ctx: &mut VmCtx, 141 | addr: usize, 142 | res: RuntimeResult, 143 | ) -> u32 { 144 | if !ctx.fits_in_lin_mem_usize(addr, 64) { 145 | return RuntimeError::Eoverflow.into(); 146 | } 147 | //log::debug!("wasm2c_marshal_and_writeback_filestat: {:?}", result); 148 | match res { 149 | Ok(r) => { 150 | ctx.write_u64(addr, r.dev); 151 | ctx.write_u64(addr + 8, r.ino); 152 | ctx.write_u64(addr + 16, r.filetype.to_wasi() as u64); 153 | ctx.write_u64(addr + 24, r.nlink); 154 | ctx.write_u64(addr + 32, r.size); 155 | ctx.write_u64(addr + 40, r.atim.nsec()); 156 | ctx.write_u64(addr + 48, r.mtim.nsec()); 157 | ctx.write_u64(addr + 56, r.ctim.nsec()); 158 | 0 159 | } 160 | Err(err) => err.into(), 161 | } 162 | } 163 | 164 | #[with_ghost_var(trace: &mut Trace)] 165 | #[requires(ctx_safe(ctx))] 166 | #[requires(trace_safe(trace, ctx))] 167 | #[ensures(ctx_safe(ctx))] 168 | #[ensures(trace_safe(trace, ctx))] 169 | pub fn wasm2c_marshal_and_writeback_u32_pair( 170 | ctx: &mut VmCtx, 171 | addr0: usize, 172 | addr1: usize, 173 | res: RuntimeResult<(u32, u32)>, 174 | ) -> u32 { 175 | if !ctx.fits_in_lin_mem_usize(addr0, 4) { 176 | return RuntimeError::Eoverflow.into(); 177 | } 178 | if !ctx.fits_in_lin_mem_usize(addr1, 4) { 179 | return RuntimeError::Eoverflow.into(); 180 | } 181 | //log::debug!("wasm2c_marshal_and_writeback_u32_pair: {:?}", result); 182 | match res { 183 | Ok((v0, v1)) => { 184 | ctx.write_u32(addr0, v0); // writeback envc 185 | ctx.write_u32(addr1, v1); // writeback environ_buf 186 | 0 187 | } 188 | Err(err) => err.into(), 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/tcb/path.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "verify")] 2 | use crate::tcb::verifier::*; 3 | use crate::types::*; 4 | use prusti_contracts::*; 5 | use std::ffi::OsString; 6 | use std::os::unix::ffi::OsStringExt; 7 | use std::path::{Component, Path, PathBuf}; 8 | //use std::fs::read_link; 9 | use owned_components::{readlinkat, OwnedComponent, OwnedComponents}; 10 | use std::ffi::OsStr; 11 | use std::os::unix::ffi::OsStrExt; 12 | use std::str; 13 | 14 | const DEPTH_ERR: isize = i32::MIN as isize; 15 | 16 | // Uninterpreted functions 17 | 18 | #[pure] 19 | #[trusted] 20 | pub fn arr_is_relative(v: &HostPath) -> bool { 21 | panic!() 22 | } 23 | 24 | #[pure] 25 | #[trusted] 26 | pub fn arr_depth(components: &HostPath) -> isize { 27 | panic!() 28 | } 29 | 30 | #[pure] 31 | #[trusted] 32 | pub fn is_symlink(components: &OwnedComponents) -> bool { 33 | panic!() 34 | } 35 | 36 | #[pure] 37 | #[trusted] 38 | pub fn arr_is_symlink(components: &HostPath) -> bool { 39 | panic!() 40 | } 41 | 42 | #[pure] 43 | #[trusted] 44 | pub fn arr_has_no_symlink_prefixes(components: &HostPath) -> bool { 45 | panic!() 46 | } 47 | 48 | #[extern_spec] 49 | impl OwnedComponents { 50 | #[pure] 51 | fn len(&self) -> usize; 52 | 53 | pub fn new() -> OwnedComponents; 54 | 55 | #[ensures(self.len() == old(self.len()) + 1)] 56 | #[ensures(forall(|i: usize| 57 | (i < self.len() - 1) ==> 58 | (old(!is_symlink(self.prefix(i))) ==> 59 | !is_symlink(self.prefix(i)) )))] 60 | pub fn push(&mut self, value: OwnedComponent); 61 | 62 | #[ensures( 63 | match &result { 64 | Some(v) => old(is_relative(&self)) ==> arr_is_relative(&v) && 65 | old(min_depth(&self)) == arr_depth(&v) && 66 | old(is_symlink(&self)) == arr_is_symlink(&v) && 67 | old(has_no_symlink_prefixes(&self)) == arr_has_no_symlink_prefixes(&v), 68 | // forall(|i: usize| 69 | // (i < self.len()) ==> 70 | // (old(is_symlink(self.prefix(i))) == arr_is_symlink(v.prefix(i)) )) 71 | _ => true, 72 | } 73 | )] 74 | pub fn unparse(self) -> Option<[u8; 4096]>; 75 | 76 | #[pure] 77 | pub fn prefix(&self, end: usize) -> &OwnedComponents; 78 | } 79 | 80 | #[trusted] 81 | pub fn get_components(path: &PathBuf) -> Vec { 82 | path.components().collect() 83 | } 84 | 85 | #[requires(idx < 4)] 86 | #[pure] 87 | #[trusted] 88 | pub fn addr_matches_netlist_entry(netlist: &Netlist, addr: u32, port: u32, idx: usize) -> bool { 89 | addr == netlist[idx].addr && port == netlist[idx].port 90 | } 91 | 92 | // If the first component is not the rootdir or a prefix (like Windows C://) its relative 93 | #[requires(c.len() > 0)] 94 | #[pure] 95 | #[trusted] 96 | pub fn is_relative(c: &OwnedComponents) -> bool { 97 | let start = c.lookup(0); 98 | !(matches!(start, OwnedComponent::RootDir)) 99 | } 100 | 101 | // use really big negative number instead of option because the verifier does not like returning options from pure code 102 | // apparently I can make it pure or I can make it untrusted but I cannot do both 103 | #[pure] 104 | #[trusted] 105 | pub fn min_depth(components: &OwnedComponents) -> isize { 106 | let mut curr_depth = 0; 107 | let mut idx = 0; 108 | while idx < components.len() { 109 | body_invariant!(curr_depth >= 0); 110 | match components.lookup(idx) { 111 | OwnedComponent::RootDir => { 112 | return DEPTH_ERR; 113 | } // hacky, but fine for now 114 | OwnedComponent::CurDir => {} 115 | OwnedComponent::ParentDir => { 116 | curr_depth -= 1; 117 | } 118 | OwnedComponent::Normal(_) => { 119 | curr_depth += 1; 120 | } 121 | }; 122 | // if curr_depth ever dips below 0, it is illegal 123 | // this prevents paths like ../other_sandbox_home 124 | if curr_depth < 0 { 125 | return curr_depth; 126 | } 127 | idx += 1; 128 | } 129 | curr_depth 130 | } 131 | 132 | #[trusted] 133 | #[ensures(result.is_none() ==> old(!is_symlink(out_path)) )] 134 | fn read_linkat_h(dirfd: HostFd, out_path: &OwnedComponents) -> Option { 135 | readlinkat(dirfd.to_raw(), &out_path.as_pathbuf()) 136 | .ok() 137 | .map(|p| OwnedComponents::parse(p)) 138 | } 139 | 140 | // Looks at a single component of a path: 141 | // if it is a symlink, return the linkpath. 142 | // else, we just append the value to out_path 143 | #[trusted] 144 | #[requires(!is_symlink(out_path) )] 145 | // require that out_path does not contain any symlinks going in 146 | #[requires(forall(|i: usize| (i < out_path.len()) ==> !is_symlink(out_path.prefix(i)) ))] 147 | #[ensures(!is_symlink(out_path))] 148 | // ensures that out_path contains no symlinks on exit 149 | #[ensures(forall(|i: usize| (i < out_path.len()) ==> !is_symlink(out_path.prefix(i)) ))] 150 | pub fn maybe_expand_component( 151 | dirfd: HostFd, 152 | out_path: &mut OwnedComponents, 153 | comp: OwnedComponent, 154 | num_symlinks: &mut isize, 155 | ) -> Option { 156 | out_path.inner.push(comp); 157 | if let Some(linkpath) = read_linkat_h(dirfd, out_path) { 158 | out_path.inner.pop(); // pop the component we just added, since it is a symlink 159 | *num_symlinks += 1; 160 | return Some(linkpath); 161 | } 162 | return None; 163 | } 164 | 165 | // its an empty path, its not a symlink 166 | #[trusted] 167 | #[ensures(result.len() == 0)] 168 | #[ensures(!is_symlink(&result))] 169 | #[ensures(forall(|i: usize| (i < result.len()) ==> !is_symlink(result.prefix(i)) ))] // we should be able to solve this by knowing that length = 0 170 | pub fn fresh_components() -> OwnedComponents { 171 | OwnedComponents::new() 172 | } 173 | 174 | #[cfg(feature = "verify")] 175 | predicate! { 176 | pub fn has_no_symlink_prefixes(v: &OwnedComponents) -> bool { 177 | forall(|i: usize| (i < v.len() - 1) ==> !is_symlink(v.prefix(i))) 178 | } 179 | } 180 | 181 | #[cfg(feature = "verify")] 182 | predicate! { 183 | pub fn path_safe(v: &HostPath, should_follow: bool) -> bool { 184 | arr_is_relative(&v) && 185 | (arr_depth(&v) >= 0) && 186 | (should_follow ==> !arr_is_symlink(&v)) && 187 | arr_has_no_symlink_prefixes(&v) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/fdmap.rs: -------------------------------------------------------------------------------- 1 | use crate::tcb::misc::vec_checked_lookup; 2 | #[cfg(feature = "verify")] 3 | use crate::tcb::verifier::external_specs::vec::*; 4 | #[cfg(feature = "verify")] 5 | use crate::tcb::verifier::*; 6 | use crate::types::*; 7 | use prusti_contracts::*; 8 | use std::io::{stderr, stdin, stdout}; 9 | use std::os::unix::io::AsRawFd; 10 | use wave_macros::{external_calls, external_methods, with_ghost_var}; 11 | use RuntimeError::*; 12 | // use crate::os::trace_close; 13 | use crate::tcb::os_specs::os_close; 14 | 15 | /* 16 | Data structure to map sandbox file descriptors to host file descriptors. 17 | We will prove things about it's API as necessary. 18 | */ 19 | 20 | //TODO: should not be able to close stdin,stdout,stderr,homedir 21 | // homedir is hardcoded to 3. 22 | 23 | impl FdMap { 24 | // #[ensures (result.m.len() == MAX_SBOX_FDS)] 25 | #[ensures (result.reserve.len() == 0)] 26 | #[ensures (result.counter == 0)] 27 | pub fn new() -> Self { 28 | FdMap { 29 | m: vec![Err(Ebadf); MAX_SBOX_FDS as usize], 30 | sockinfo: vec![Err(Enotsock); MAX_SBOX_FDS as usize], // these are the host protocol domain/ty/family numbers 31 | reserve: Vec::new(), 32 | counter: 0, 33 | } 34 | } 35 | 36 | // #[with_ghost_var(trace: &mut Trace)] 37 | // #[external_call(stdin)] 38 | // #[external_call(stdout)] 39 | // #[external_call(stderr)] 40 | // #[external_method(as_raw_fd)] 41 | // #[external_method(into)] 42 | #[requires (self.counter == 0)] //should only be called on empty fdmap 43 | pub fn init_std_fds(&mut self) -> RuntimeResult<()> { 44 | let stdin_fd = stdin().as_raw_fd(); 45 | let stdout_fd = stdout().as_raw_fd(); 46 | let stderr_fd = stderr().as_raw_fd(); 47 | if (stdin_fd >= 0) && (stdout_fd >= 0) && (stderr_fd >= 0) { 48 | // upcasting i32 => usize is safe since we checked that it is positive 49 | // viper overflow checker would yell at us if this was not the case 50 | self.create(HostFd::from_raw(stdin_fd as usize)); 51 | self.create(HostFd::from_raw(stdout_fd as usize)); 52 | self.create(HostFd::from_raw(stderr_fd as usize)); 53 | return Ok(()); 54 | } 55 | Err(Emfile) // File descriptor failure 56 | } 57 | 58 | #[pure] 59 | #[requires (index < MAX_SBOX_FDS )] 60 | pub fn lookup(&self, index: SboxFd) -> RuntimeResult { 61 | vec_checked_lookup(&self.m, index) 62 | } 63 | 64 | #[with_ghost_var(trace: &Trace)] 65 | #[external_calls(vec_checked_lookup)] 66 | // #[pure] 67 | #[ensures(result.is_ok() ==> old(v_fd) < MAX_SBOX_FDS)] 68 | pub fn fd_to_native(&self, v_fd: SboxFd) -> RuntimeResult { 69 | if v_fd >= MAX_SBOX_FDS { 70 | return Err(Ebadf); 71 | } 72 | // self.m[idx as usize] 73 | vec_checked_lookup(&self.m, v_fd) 74 | } 75 | 76 | #[pure] 77 | #[requires(index < MAX_SBOX_FDS)] 78 | #[ensures(result == true ==> self.lookup(index).is_ok())] 79 | pub fn contains(&self, index: SboxFd) -> bool { 80 | matches!(self.lookup(index), Ok(_)) 81 | } 82 | 83 | // #[with_ghost_var(trace: &mut Trace)] 84 | // #[external_call(Ok)] 85 | // #[external_call(Err)] 86 | // #[external_method(pop)] 87 | fn pop_fd(&mut self) -> RuntimeResult { 88 | match self.reserve.pop() { 89 | Some(fd) => Ok(fd), 90 | None => { 91 | if self.counter < MAX_SBOX_FDS { 92 | self.counter += 1; 93 | return Ok(self.counter - 1); 94 | } 95 | Err(Emfile) 96 | } 97 | } 98 | } 99 | 100 | // #[requires(k < MAX_HOST_FDS)] 101 | // #[ensures (self.lookup(k) == result)] 102 | // #[ensures (forall(|i: usize| (i < MAX_SBOX_FDS && i != k) ==> 103 | // self.lookup(i) == old(self.lookup(i))))] 104 | // #[with_ghost_var(trace: &mut Trace)] 105 | // #[external_call(Ok)] 106 | // #[requires(trace_safe(ctx, trace))] 107 | // #[ensures(trace_safe(ctx, trace))] 108 | pub fn create(&mut self, k: HostFd) -> RuntimeResult { 109 | let s_fd = self.pop_fd()?; 110 | self.m[s_fd as usize] = Ok(k); 111 | Ok(s_fd) 112 | } 113 | 114 | pub fn create_sock(&mut self, k: HostFd, proto: WasiProto) -> RuntimeResult { 115 | let s_fd = self.pop_fd()?; 116 | self.m[s_fd as usize] = Ok(k); 117 | self.sockinfo[s_fd as usize] = Ok(proto); 118 | Ok(s_fd) 119 | } 120 | 121 | #[requires(k < MAX_SBOX_FDS)] 122 | // #[with_ghost_var(trace: &mut Trace)] 123 | // #[external_call(Err)] 124 | // #[external_call(init_std_fds)] 125 | // #[external_method(push)] 126 | // #[requires(trace_safe(ctx, trace))] 127 | // #[ensures(trace_safe(ctx, trace))] 128 | // #[ensures (self.lookup(k).is_err())] 129 | // #[ensures (forall(|i: usize| (i < MAX_SBOX_FDS && i != k) ==> 130 | // self.lookup(i) == old(self).lookup(i)))] 131 | pub fn delete(&mut self, k: SboxFd) { 132 | if let Ok(oldfd) = self.m[k as usize] { 133 | self.reserve.push(k); 134 | } 135 | self.m[k as usize] = Err(Ebadf); 136 | } 137 | 138 | #[requires(from < MAX_SBOX_FDS)] 139 | #[requires(to < MAX_SBOX_FDS)] 140 | pub fn shift(&mut self, from: SboxFd, to: SboxFd) { 141 | if let Ok(hostfd) = self.m[from as usize] { 142 | self.m[to as usize] = Ok(hostfd) 143 | } 144 | self.m[from as usize] = Err(Ebadf); 145 | } 146 | 147 | // // auto drop open file descriptors and shutdown sockets 148 | // #[with_ghost_var(trace: &mut Trace)] 149 | // // #[requires(ctx_safe(self))] 150 | // // #[requires(trace_safe(trace, self))] 151 | // // #[ensures(ctx_safe(self))] 152 | // // #[ensures(trace_safe(trace, self))] 153 | // #[external_methods(lookup)] 154 | // fn drop(&mut self) { 155 | // let mut idx = 3; // not stdin,stdout,stderr 156 | // while idx < MAX_SBOX_FDS { 157 | // // body_invariant!(ctx_safe(self)); 158 | // // body_invariant!(trace_safe(trace, self)); 159 | // match self.lookup(idx) { 160 | // Ok(fd) => { os_close(fd.to_raw()); } 161 | // _ => (), 162 | // } 163 | // idx += 1; 164 | // } 165 | // } 166 | } 167 | -------------------------------------------------------------------------------- /src/tcb/misc.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "verify")] 2 | use crate::tcb::verifier::*; 3 | use crate::types::*; 4 | use prusti_contracts::*; 5 | use std::os::unix::io::AsRawFd; 6 | use std::vec::Vec; 7 | use wave_macros::with_ghost_var; 8 | 9 | // Trusted because I can't get the verifier to understand that 10 | // this can't ever err and it is pretty clear it is correct. 11 | // Can be fixed with https://viperproject.github.io/prusti-dev/user-guide/verify/pledge.html 12 | // Used in fdmap implementation 13 | #[trusted] 14 | #[pure] 15 | #[requires(index < MAX_SBOX_FDS )] 16 | pub fn vec_checked_lookup( 17 | vec: &Vec>, 18 | index: SboxFd, 19 | ) -> RuntimeResult { 20 | vec[index as usize] 21 | } 22 | 23 | // Once again, Prusti does not accept that this is pure 24 | #[trusted] 25 | #[pure] 26 | #[requires(index < vec.len() )] 27 | pub fn iovs_checked_lookup(vec: &NativeIoVecs, index: usize) -> NativeIoVec { 28 | vec.iovs[index] 29 | } 30 | 31 | // Once again, Prusti does not accept that this is pure 32 | #[trusted] 33 | #[pure] 34 | #[requires(index < vec.len() )] 35 | pub fn wasm_iovs_checked_lookup(vec: &WasmIoVecs, index: usize) -> WasmIoVec { 36 | vec.iovs[index] 37 | } 38 | 39 | // // Trusted because I can't convince the verifier tha tthis will never panic. 40 | // // Used in specification in src/os.rs 41 | // #[trusted] 42 | // #[pure] 43 | // #[requires(r.is_ok())] 44 | // pub fn safe_unwrap(r: &RuntimeResult) -> isize { 45 | // r.unwrap() 46 | // } 47 | 48 | ///////////////////////////////// Bitwise Ops ///////////////////////////////// 49 | /// These operations are currently trusted because prusti does not handle 50 | /// bitwise operations. However, they have no #[ensures] annotations, so they 51 | /// cannot introduce unsoundness into our proof and so I don't expect any of 52 | /// these functions to cause any trouble. 53 | 54 | /// Check if The nth bit from the lsb is set (0 is lsb) 55 | #[trusted] 56 | #[pure] 57 | #[ensures(bv == 0 ==> result == false)] 58 | pub fn nth_bit_set(bv: u16, n: i32) -> bool { 59 | bv & (1 << n) != 0 60 | } 61 | 62 | #[trusted] 63 | #[pure] 64 | pub fn nth_bit_set_u32(bv: u32, n: u32) -> bool { 65 | bv & (1 << n) != 0 66 | } 67 | 68 | /// return bv with the nth bit from the lsb set (0 is lsb) 69 | #[trusted] 70 | #[pure] 71 | pub fn with_nth_bit_set(bv: u16, n: i32) -> u16 { 72 | bv | (1 << n) 73 | } 74 | 75 | #[trusted] 76 | #[pure] 77 | pub fn flag_set(bv: i32, flag: i32) -> bool { 78 | (bv & flag) == flag 79 | } 80 | 81 | #[trusted] 82 | #[pure] 83 | pub fn bitwise_and(bv1: i32, bv2: i32) -> i32 { 84 | bv1 & bv2 85 | } 86 | 87 | #[trusted] 88 | pub fn bitwise_and_i16(bv1: i16, bv2: i16) -> i16 { 89 | bv1 & bv2 90 | } 91 | 92 | #[trusted] 93 | pub fn bitwise_and_u16(bv1: u16, bv2: u16) -> u16 { 94 | bv1 & bv2 95 | } 96 | 97 | #[trusted] 98 | #[pure] 99 | pub fn bitwise_and_u32(bv1: u32, bv2: u32) -> u32 { 100 | bv1 & bv2 101 | } 102 | 103 | #[trusted] 104 | pub fn bitwise_and_u64(bv1: u64, bv2: u64) -> u64 { 105 | bv1 & bv2 106 | } 107 | 108 | #[trusted] 109 | #[pure] 110 | // this ensures is mostly to prove that or'ing a nonzero # returns a 111 | // nonzero result 112 | // #[with_ghost_var(trace: &mut Trace)] 113 | #[ensures(result >= bv1 && result >= bv2)] 114 | pub fn bitwise_or(bv1: i32, bv2: i32) -> i32 { 115 | bv1 | bv2 116 | } 117 | 118 | #[trusted] 119 | pub fn bitwise_or_u32(bv1: u32, bv2: u32) -> u32 { 120 | bv1 | bv2 121 | } 122 | 123 | // Unsafe necessary as libc::stat is opaque. It is safe but we can replace it by implementing 124 | // pub fn bitwise_or_u32(bv1: u32, bv2: u32) -> u32 { 125 | // the struct ourselves if we want to avoid as much unsafe as possible. 126 | // bv1 | bv2 127 | // Safety: Safe as libc::stat is valid with an all-zero byte-pattern (i.e. it is not a 128 | // } 129 | // reference) 130 | #[trusted] 131 | pub fn fresh_stat() -> libc::stat { 132 | unsafe { std::mem::zeroed() } 133 | } 134 | 135 | // Unsafe necessary as libc::rusage is opaque. It is safe but we can replace it by implementing 136 | // pub fn bitwise_or_u32(bv1: u32, bv2: u32) -> u32 { 137 | // the struct ourselves if we want to avoid as much unsafe as possible. 138 | // bv1 | bv2 139 | // Safety: Safe as libc::rusage is valid with an all-zero byte-pattern (i.e. it is not a 140 | // } 141 | // reference) 142 | #[trusted] 143 | pub fn fresh_rusage() -> libc::rusage { 144 | unsafe { std::mem::zeroed() } 145 | } 146 | 147 | #[trusted] 148 | #[requires(len >= offset)] 149 | #[requires(buf.len() >= start + len)] 150 | #[ensures(result < old(len))] 151 | pub fn first_null(buf: &Vec, start: usize, offset: usize, len: usize) -> usize { 152 | buf[start + offset..start + len] 153 | .iter() 154 | .position(|x| *x == 0) 155 | .unwrap() 156 | } 157 | 158 | #[trusted] 159 | // #[requires(buf.len() > start + len + 19)] 160 | pub fn push_dirent_name(out_buf: &mut Vec, buf: &Vec, start: usize, len: usize) { 161 | out_buf.extend_from_slice(&buf[start..start + len]) 162 | } 163 | 164 | // Trusted because I need to convince prusti that clone does not alter 165 | // the length of vectors 166 | #[trusted] 167 | #[ensures(result.len() == old(vec.len()))] 168 | pub fn clone_vec_u8(vec: &Vec) -> Vec { 169 | vec.clone() 170 | } 171 | 172 | // TODO: should probably fail more elegantly than this 173 | #[trusted] 174 | pub fn get_homedir_fd(s: &String) -> i32 { 175 | let homedir_file = std::fs::File::open(s).unwrap(); 176 | let homedir_fd = homedir_file.as_raw_fd(); 177 | 178 | //Need to forget file to make sure it does not get auto-closed 179 | //when it gets out of scope 180 | std::mem::forget(homedir_file); 181 | homedir_fd 182 | } 183 | 184 | #[trusted] 185 | pub fn string_to_vec_u8(s: &String) -> Vec { 186 | s.as_bytes().to_vec() 187 | } 188 | 189 | #[with_ghost_var(trace: &mut Trace)] 190 | #[trusted] 191 | pub fn empty_netlist() -> Netlist { 192 | let empty = NetEndpoint { 193 | protocol: WasiProto::Unknown, 194 | addr: 0, 195 | port: 0, 196 | }; 197 | 198 | [empty, empty, empty, empty] 199 | } 200 | 201 | // this shouldn't need to be trusted, but prusti does not casting an enum to an int 202 | #[trusted] 203 | pub fn as_u32(e: RuntimeError) -> u32 { 204 | e as u32 205 | } 206 | 207 | #[trusted] 208 | pub fn as_u16(e: RuntimeError) -> u16 { 209 | e as u16 210 | } 211 | 212 | // uninterpreted function 213 | #[trusted] 214 | #[pure] 215 | pub fn netlist_unmodified(n: &Netlist) -> bool { 216 | unimplemented!(); 217 | } 218 | -------------------------------------------------------------------------------- /src/stats/timing.rs: -------------------------------------------------------------------------------- 1 | use core::arch::x86_64::{__cpuid_count, __rdtscp, _rdtsc}; 2 | use std::cell::RefCell; 3 | use std::collections::HashMap; 4 | use std::thread; 5 | 6 | // name of hostcall -> Vec 7 | pub type ResultsType = HashMap>; 8 | 9 | fn wasi_results_init() -> RefCell { 10 | let mut h: ResultsType = HashMap::new(); 11 | h.insert("args_get".to_owned(), Vec::new()); 12 | h.insert("args_sizes_get".to_owned(), Vec::new()); 13 | h.insert("proc_exit".to_owned(), Vec::new()); 14 | h.insert("environ_sizes_get".to_owned(), Vec::new()); 15 | h.insert("environ_get".to_owned(), Vec::new()); 16 | h.insert("fd_prestat_get".to_owned(), Vec::new()); 17 | h.insert("fd_write".to_owned(), Vec::new()); 18 | h.insert("fd_read".to_owned(), Vec::new()); 19 | h.insert("fd_close".to_owned(), Vec::new()); 20 | h.insert("fd_seek".to_owned(), Vec::new()); 21 | h.insert("clock_time_get".to_owned(), Vec::new()); 22 | h.insert("clock_res_get".to_owned(), Vec::new()); 23 | h.insert("fd_advise".to_owned(), Vec::new()); 24 | h.insert("fd_allocate".to_owned(), Vec::new()); 25 | h.insert("fd_datasync".to_owned(), Vec::new()); 26 | h.insert("fd_fdstat_get".to_owned(), Vec::new()); 27 | h.insert("fd_fdstat_set_flags".to_owned(), Vec::new()); 28 | h.insert("fd_filestat_get".to_owned(), Vec::new()); 29 | h.insert("fd_filestat_set_size".to_owned(), Vec::new()); 30 | h.insert("fd_filestat_set_times".to_owned(), Vec::new()); 31 | h.insert("fd_pread".to_owned(), Vec::new()); 32 | h.insert("fd_prestat_dir_name".to_owned(), Vec::new()); 33 | h.insert("fd_pwrite".to_owned(), Vec::new()); 34 | h.insert("fd_readdir".to_owned(), Vec::new()); 35 | h.insert("fd_renumber".to_owned(), Vec::new()); 36 | h.insert("fd_sync".to_owned(), Vec::new()); 37 | h.insert("fd_tell".to_owned(), Vec::new()); 38 | h.insert("path_create_directory".to_owned(), Vec::new()); 39 | h.insert("path_filestat_get".to_owned(), Vec::new()); 40 | h.insert("path_filestat_set_times".to_owned(), Vec::new()); 41 | h.insert("path_link".to_owned(), Vec::new()); 42 | h.insert("path_open".to_owned(), Vec::new()); 43 | h.insert("path_readlink".to_owned(), Vec::new()); 44 | h.insert("path_remove_directory".to_owned(), Vec::new()); 45 | h.insert("path_rename".to_owned(), Vec::new()); 46 | h.insert("path_symlink".to_owned(), Vec::new()); 47 | h.insert("path_unlink_file".to_owned(), Vec::new()); 48 | h.insert("poll_oneoff".to_owned(), Vec::new()); 49 | h.insert("proc_raise".to_owned(), Vec::new()); 50 | h.insert("random_get".to_owned(), Vec::new()); 51 | h.insert("sched_yield".to_owned(), Vec::new()); 52 | h.insert("sock_recv".to_owned(), Vec::new()); 53 | h.insert("sock_send".to_owned(), Vec::new()); 54 | h.insert("sock_shutdown".to_owned(), Vec::new()); 55 | h.insert("socket".to_owned(), Vec::new()); 56 | h.insert("sock_connect".to_owned(), Vec::new()); 57 | RefCell::new(h) 58 | } 59 | 60 | fn syscall_results_init() -> RefCell { 61 | let mut h: ResultsType = HashMap::new(); 62 | h.insert("openat".to_owned(), Vec::new()); 63 | h.insert("close".to_owned(), Vec::new()); 64 | h.insert("read".to_owned(), Vec::new()); 65 | h.insert("pread".to_owned(), Vec::new()); 66 | h.insert("write".to_owned(), Vec::new()); 67 | h.insert("pwrite".to_owned(), Vec::new()); 68 | h.insert("readv".to_owned(), Vec::new()); 69 | h.insert("preadv".to_owned(), Vec::new()); 70 | h.insert("writev".to_owned(), Vec::new()); 71 | h.insert("pwritev".to_owned(), Vec::new()); 72 | h.insert("lseek".to_owned(), Vec::new()); // seek 73 | h.insert("advise".to_owned(), Vec::new()); 74 | h.insert("allocate".to_owned(), Vec::new()); 75 | h.insert("fsync".to_owned(), Vec::new()); // sync 76 | h.insert("fdatasync".to_owned(), Vec::new()); // datasync 77 | h.insert("fstat".to_owned(), Vec::new()); 78 | h.insert("newfstatat".to_owned(), Vec::new()); // fstatat 79 | h.insert("ftruncate".to_owned(), Vec::new()); 80 | h.insert("linkat".to_owned(), Vec::new()); 81 | h.insert("mkdirat".to_owned(), Vec::new()); 82 | h.insert("readlinkat".to_owned(), Vec::new()); 83 | h.insert("unlinkat".to_owned(), Vec::new()); 84 | h.insert("renameat".to_owned(), Vec::new()); 85 | h.insert("symlinkat".to_owned(), Vec::new()); 86 | h.insert("futimens".to_owned(), Vec::new()); 87 | h.insert("utimensat".to_owned(), Vec::new()); 88 | h.insert("clock_gettime".to_owned(), Vec::new()); 89 | h.insert("clock_getres".to_owned(), Vec::new()); 90 | h.insert("getrandom".to_owned(), Vec::new()); 91 | h.insert("recvfrom".to_owned(), Vec::new()); // recv 92 | h.insert("sendto".to_owned(), Vec::new()); // send 93 | h.insert("shutdown".to_owned(), Vec::new()); 94 | h.insert("nanosleep".to_owned(), Vec::new()); 95 | h.insert("poll".to_owned(), Vec::new()); 96 | h.insert("getdents64".to_owned(), Vec::new()); 97 | h.insert("socket".to_owned(), Vec::new()); 98 | h.insert("connect".to_owned(), Vec::new()); 99 | h.insert("fcntl".to_owned(), Vec::new()); // fsetfl, fgetfl 100 | RefCell::new(h) 101 | } 102 | 103 | thread_local! { 104 | pub static HOSTCALL_RESULTS: RefCell = wasi_results_init(); 105 | pub static SYSCALL_RESULTS: RefCell = syscall_results_init(); 106 | } 107 | 108 | #[inline] 109 | pub fn start_timer() -> u64 { 110 | unsafe { 111 | __cpuid_count(0, 0); 112 | _rdtsc() as u64 113 | } 114 | } 115 | 116 | #[inline] 117 | pub fn stop_timer() -> u64 { 118 | unsafe { 119 | let mut junk: u32 = 0; 120 | let ans: u64 = __rdtscp(&mut junk); 121 | __cpuid_count(0, 0); 122 | ans 123 | } 124 | } 125 | 126 | pub fn push_hostcall_result(name: &str, start: u64, end: u64) { 127 | // println!("name: {:?}", name); 128 | HOSTCALL_RESULTS.with(|r| { 129 | let mut index = r.borrow_mut(); 130 | // let vec = index.get_mut(&name.to_owned()).unwrap(); 131 | let vec = match index.get_mut(&name.to_owned()) { 132 | Some(v) => v, 133 | None => panic!("Unknown hostcall: {}", name), 134 | }; 135 | let ticks = end - start; 136 | vec.push(ticks as f64 / 2.1); // convert to nanoseconds using 2.1 GHZ clock (elk) 137 | }); 138 | } 139 | 140 | pub fn push_syscall_result(name: &str, start: u64, end: u64) { 141 | // println!("name: {:?}", name); 142 | SYSCALL_RESULTS.with(|r| { 143 | let mut index = r.borrow_mut(); 144 | let vec = match index.get_mut(&name.to_owned()) { 145 | Some(v) => v, 146 | None => panic!("Unknown syscall: {}", name), 147 | }; 148 | let ticks = end - start; 149 | vec.push(ticks as f64 / 2.1); // convert to nanoseconds using 2.1 GHZ clock (elk) 150 | }); 151 | } 152 | 153 | // let _start = start_timer() 154 | // let _end = stop_timer() 155 | // results["this_func"].push() 156 | -------------------------------------------------------------------------------- /src/tcb/verifier/trace.rs: -------------------------------------------------------------------------------- 1 | use prusti_contracts::*; 2 | 3 | #[cfg(feature = "verify")] 4 | predicate! { 5 | pub fn takes_n_steps(old_trace: &Trace, trace: &Trace, n: usize) -> bool { 6 | n >= 0 && 7 | // We added n more steps 8 | trace.len() == old_trace.len() + n && 9 | // But the other effects were not affected 10 | forall(|i: usize| (i < old_trace.len()) ==> 11 | trace.lookup(i) == old_trace.lookup(i)) 12 | } 13 | } 14 | 15 | // From https://danielkeep.github.io/tlborm/book/blk-counting.html 16 | // Simple counting macro for number of trace effects. We only have a couple 17 | // effects per computation so we don't need anything complicated. 18 | #[macro_export] 19 | macro_rules! count_guards { 20 | () => {0usize}; 21 | ($($pattern: pat_param)|+ $( if $guard: expr)?) => {1usize}; 22 | ($($pattern: pat_param)|+ $( if $guard: expr)?, $($tail:tt)*) => {1usize + $crate::count_guards!($($tail)*)}; 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! effects { 27 | ($old_trace:expr, $trace:expr) => { 28 | takes_n_steps($old_trace, $trace, 0) 29 | }; 30 | ($old_trace:expr, $trace:expr, $($tail:tt)*) => { 31 | takes_n_steps($old_trace, $trace, $crate::count_guards!($($tail)*)) && 32 | effects!(@munch $old_trace, $trace, $($tail)*) 33 | }; 34 | (@munch $old_trace:expr, $trace:expr) => { 35 | true 36 | }; 37 | (@munch $old_trace:expr, $trace:expr, $($pattern: pat_param)|+ $( if $guard: expr)?) => { 38 | match $trace.lookup($trace.len() - 1) { 39 | $( $pattern )|+ => $($guard &&)? true, 40 | _ => false, 41 | } 42 | }; 43 | (@munch $old_trace:expr, $trace:expr, $($pattern: pat_param)|+ $( if $guard: expr)?, $($tail:tt)*) => { 44 | match $trace.lookup($trace.len() - (1 + $crate::count_guards!($($tail)*))) { 45 | $( $pattern )|+ => $($guard &&)? true, 46 | _ => false, 47 | } && effects!(@munch $old_trace, $trace, $($tail)*) 48 | }; 49 | } 50 | 51 | #[macro_export] 52 | macro_rules! map_effects { 53 | ($old_trace:expr, $trace:expr, $struct_iter:expr, $cnt:expr, $($pattern: pat_param)|+ $( if $guard: expr)?) => { 54 | takes_n_steps($old_trace, $trace, $cnt) && 55 | forall(|idx: usize| (idx < $cnt) ==> 56 | let this = $struct_iter.lookup(idx); 57 | match $trace.lookup($old_trace.len() + idx) { 58 | $( $pattern )|+ => $($guard &&)? true, 59 | _ => false, 60 | } 61 | ) 62 | }; 63 | } 64 | 65 | // #[macro_export] 66 | // macro_rules! do_effect { 67 | // ($trace:expr, $input:expr) => { 68 | // if cfg!(feature = "verify") { 69 | // $trace.push($input); 70 | // } 71 | // }; 72 | // } 73 | 74 | #[cfg(feature = "test")] 75 | use crate::predicate; 76 | 77 | #[derive(PartialEq, Eq, Clone, Copy)] 78 | #[repr(usize)] 79 | pub enum EffectType { 80 | ReadMem, 81 | WriteMem, 82 | Shutdown, 83 | FdAccess, 84 | PathAccessAt, 85 | NetAccess, 86 | SockCreation, 87 | } 88 | 89 | #[derive(PartialEq, Eq, Clone, Copy)] 90 | pub struct Effect { 91 | pub typ: EffectType, 92 | pub f1: usize, 93 | pub f2: usize, 94 | pub f3: usize, 95 | pub p: Option<[u8; 4096]>, 96 | pub should_follow: Option, 97 | } 98 | 99 | // TODO: I think this has to become a proc macro if we don't wanna expand every case manually... 100 | #[macro_export] 101 | macro_rules! effect { 102 | ($typ:ident) => { 103 | Effect { 104 | typ: EffectType::$typ, 105 | f1: 0, 106 | f2: 0, 107 | f3: 0, 108 | p: None, 109 | should_follow: None, 110 | } 111 | }; 112 | ($typ:ident, $f1:pat) => { 113 | Effect { 114 | typ: EffectType::$typ, 115 | f1: $f1, 116 | f2: 0, 117 | f3: 0, 118 | p: None, 119 | should_follow: None, 120 | } 121 | }; 122 | ($typ:ident, $f1:pat, $f2:pat) => { 123 | Effect { 124 | typ: EffectType::$typ, 125 | f1: $f1, 126 | f2: $f2, 127 | f3: 0, 128 | p: None, 129 | should_follow: None, 130 | } 131 | }; 132 | ($typ:ident, $f1:pat, $f2:pat, $f3:pat) => { 133 | Effect { 134 | typ: EffectType::$typ, 135 | f1: $f1, 136 | f2: $f2, 137 | f3: $f3, 138 | p: None, 139 | should_follow: None, 140 | } 141 | }; 142 | } 143 | 144 | // macro for passing in paths 145 | #[macro_export] 146 | macro_rules! path_effect { 147 | ($typ:ident, $f1:pat, $f2:pat, $f3:pat) => { 148 | Effect { 149 | typ: EffectType::$typ, 150 | f1: $f1, 151 | f2: 0, 152 | f3: 0, 153 | p: Some($f2), 154 | should_follow: Some($f3), 155 | } 156 | }; 157 | } 158 | 159 | // #[trusted] 160 | // #[pure] 161 | // #[requires(index < MAX_SBOX_FDS )] 162 | // pub fn vec_u8_lookup( 163 | // vec: &Vec, 164 | // index: usize, 165 | // ) -> RuntimeResult { 166 | // vec[index as usize] 167 | // } 168 | 169 | // use crate::tcb::misc::vec_checked_lookup; 170 | // #[cfg(feature = "verify")] 171 | // predicate! { 172 | // pub fn vec_is_eq(v0: &Vec, v1: &Vec) -> bool { 173 | // v0.len() == v1.len() && 174 | // forall(|i: usize| 175 | // (i < v0.len() ==> ( 176 | // vec_u8_lookup(v0, i) == vec_u8_lookup(v1, i) 177 | // )) 178 | // ) 179 | // } 180 | // } 181 | 182 | pub struct Trace { 183 | v: Vec, 184 | } 185 | 186 | impl Trace { 187 | // Encoded as body-less Viper function 188 | #[trusted] 189 | #[pure] 190 | pub fn len(&self) -> usize { 191 | self.v.len() 192 | } 193 | 194 | // Encoded as body-less Viper method 195 | #[trusted] 196 | #[ensures(result.len() == 0)] 197 | pub fn new() -> Self { 198 | Trace { v: Vec::new() } 199 | } 200 | 201 | // Encoded as body-less Viper function 202 | #[trusted] 203 | #[pure] 204 | #[requires(index < self.len())] 205 | pub fn lookup(&self, index: usize) -> Effect { 206 | self.v[index] 207 | } 208 | 209 | #[trusted] 210 | #[ensures(self.len() == old(self.len()) + 1)] 211 | #[ensures(self.lookup(old(self.len())) == old(value))] 212 | #[ensures(forall(|i: usize| (i < old(self.len())) ==> 213 | self.lookup(i) == old(self.lookup(i))))] 214 | pub fn push(&mut self, value: Effect) { 215 | self.v.push(value); 216 | } 217 | 218 | // #[trusted] 219 | // #[ensures(self.num_paths() == old(self.num_paths()) + 1)] 220 | // #[ensures(self.lookup_path(old(self.num_paths())) == old(&value))] 221 | // #[ensures(forall(|i: usize| (i < old(self.num_paths())) ==> 222 | // self.lookup_path(i) == old(self.lookup_path(i))))] 223 | // pub fn push_path(&mut self, value: Vec) { 224 | // self.paths.push(value); 225 | // } 226 | } 227 | -------------------------------------------------------------------------------- /src/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use regex::Regex; 3 | use std::path::Path; 4 | 5 | // execute an example using our example/make infrastructure 6 | // returns stdout 7 | fn run_and_capture(example_path: &str) -> String { 8 | let output = Command::new("make") 9 | .arg("-s") // silent 10 | .arg("-C") // change directory to example dir 11 | .arg(example_path) 12 | .arg("run") 13 | .output() 14 | .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }); 15 | 16 | match String::from_utf8(output.stdout) { 17 | Ok(v) => v, 18 | Err(e) => panic!("Invalid UTF-8 sequence: {}", e), 19 | } 20 | } 21 | 22 | 23 | #[test] 24 | fn example_cat() { 25 | let s = run_and_capture("examples/cat"); 26 | assert!(s == "This is the contents of the file!\n"); 27 | } 28 | 29 | // #[test] 30 | // fn example_client() { 31 | // let s = run_and_capture("examples/client"); 32 | // // assert!(s == "This is the contents of the file!\n"); 33 | // } 34 | 35 | // #[test] 36 | // fn example_client_send_recv() { 37 | // let s = run_and_capture("examples/client_send_recv"); 38 | // // assert!(s == "This is the contents of the file!\n"); 39 | // } 40 | 41 | #[test] 42 | fn example_clock() { 43 | let s = run_and_capture("examples/clock"); 44 | let re = Regex::new(r"^time = [0-9]+$").unwrap(); 45 | let s_split = s.split("\n").collect::>(); 46 | assert!(re.is_match(s_split[0])); 47 | assert!(s_split[1] == "result = 0 res_get.tv_sec = 0 res_get.tv_nsec = 1"); 48 | assert!(s_split[2] == "Done!"); 49 | // assert!(s == "This is the contents of the file!\n"); 50 | } 51 | 52 | #[test] 53 | fn example_cp() { 54 | let s = run_and_capture("examples/cp"); 55 | assert!(Path::new("examples/cp/output/tmp.txt").exists()); 56 | // assert!(s == "This is the contents of the file!\n"); 57 | } 58 | 59 | // TODO: check if output file exists 60 | #[test] 61 | fn example_cp_and_insert() { 62 | let s = run_and_capture("examples/cp_and_insert"); 63 | let s_split = s.split("\n").collect::>(); 64 | assert!(s_split[0] == "position for lseek1 = 34"); 65 | assert!(s_split[1] == "position for lseek2 = 0"); 66 | assert!(s_split[2] == "results of pread = file"); 67 | assert!(s_split[3] == "Done!"); 68 | assert!(Path::new("examples/cp/output/tmp.txt").exists()); 69 | } 70 | 71 | // TODO: clean up? 72 | #[test] 73 | fn example_fallocate() { 74 | let s = run_and_capture("examples/fallocate"); 75 | assert!(Path::new("examples/fallocate/output/tmp.txt").exists()); 76 | // assert!(s == "This is the contents of the file!\n"); 77 | } 78 | 79 | #[test] 80 | fn example_hello() { 81 | let s = run_and_capture("examples/hello"); 82 | assert!(s == "Hello, World!\n"); 83 | } 84 | 85 | // TODO: setup/teardown 86 | #[test] 87 | fn example_link() { 88 | let s = run_and_capture("examples/link"); 89 | let s_split = s.split("\n").collect::>(); 90 | assert!(s_split[0] == "This is the contents of the file!"); 91 | assert!(s_split[1] == "Done!"); 92 | } 93 | 94 | #[test] 95 | fn example_ls() { 96 | let s = run_and_capture("examples/ls"); 97 | let s_split = s.split("\n").collect::>(); 98 | assert!(s_split[0] == "I'm doing an ls(.)!"); 99 | assert!(s_split[1] == "ls ls.c ls.wasm.h ls.wasm ls.wasm.c .. . Makefile "); 100 | assert!(s_split[2] == "Done!"); 101 | } 102 | 103 | // TODO: cleanup 104 | #[test] 105 | fn example_mkdir() { 106 | let s = run_and_capture("examples/mkdir"); 107 | assert!(Path::new("examples/mkdir/test_dir").exists()); 108 | // assert!(s == "This is the contents of the file!\n"); 109 | } 110 | 111 | #[test] 112 | fn example_permissions_regression() { 113 | let s = run_and_capture("examples/permissions_regression"); 114 | let s_split = s.split("\n").collect::>(); 115 | assert!(s_split[0] == "Hello, World!"); 116 | assert!(s_split[1] == "write returns 11"); 117 | } 118 | 119 | #[test] 120 | fn example_print_args_and_environ() { 121 | let s = run_and_capture("examples/print_args_and_environ"); 122 | let s_split = s.split("\n").collect::>(); 123 | assert!(s_split[0] == "Number of arguments passed: 3"); 124 | assert!(s_split[1] == "argv[0]: a"); 125 | assert!(s_split[2] == "argv[1]: b"); 126 | assert!(s_split[3] == "argv[2]: c"); 127 | assert!(s_split[4] == "Environment Variable: CC=clang"); 128 | assert!(s_split[5] == "Environment Variable: CXX=clang++"); 129 | assert!(s_split[6] == "Done!"); 130 | } 131 | 132 | #[test] 133 | fn example_raise() { 134 | let s = run_and_capture("examples/raise"); 135 | // all we expect is that it doesn't crash 136 | } 137 | 138 | #[test] 139 | fn example_random() { 140 | let s = run_and_capture("examples/random"); 141 | let re = Regex::new(r"^outbuf = [0-9a-fA-F]{8}$").unwrap(); 142 | let s_split = s.split("\n").collect::>(); 143 | assert!(re.is_match(s_split[0])); 144 | assert!(s_split[1] == "Done!"); 145 | // assert!(s == "This is the contents of the file!\n"); 146 | } 147 | 148 | // TODO: set up 149 | #[test] 150 | fn example_rename() { 151 | let s = run_and_capture("examples/rename"); 152 | assert!(s == "Done!\n"); 153 | assert!(Path::new("examples/rename/B.txt").exists()); 154 | // assert!(s == "This is the contents of the file!\n"); 155 | } 156 | 157 | #[test] 158 | fn example_renumber() { 159 | let s = run_and_capture("examples/renumber"); 160 | let s_split = s.split("\n").collect::>(); 161 | assert!(s_split[0] == "This is the contents of the file!"); 162 | assert!(s_split[1] == "Done!"); 163 | } 164 | 165 | // TODO: set up 166 | #[test] 167 | fn example_rmdir() { 168 | let s = run_and_capture("examples/rmdir"); 169 | assert!(!Path::new("examples/rmdir/remove_me").exists()); 170 | // assert!(s == "This is the contents of the file!\n"); 171 | } 172 | 173 | #[test] 174 | fn example_setfl() { 175 | let s = run_and_capture("examples/setfl"); 176 | let s_split = s.split("\n").collect::>(); 177 | assert!(s_split[0] == "oldflags = 8000000"); 178 | assert!(s_split[1] == "newflags = 8000400"); 179 | assert!(s_split[2] == "Done!"); 180 | // assert!(s == "This is the contents of the file!\n"); 181 | } 182 | 183 | #[test] 184 | fn example_sleep() { 185 | let s = run_and_capture("examples/sleep"); 186 | let s_split = s.split("\n").collect::>(); 187 | assert!(s_split[0] == "Please wait for 2 seconds..."); 188 | assert!(s_split[1] == "Thank you for waiting!"); 189 | // assert!(s == "This is the contents of the file!\n"); 190 | } 191 | 192 | #[test] 193 | fn example_stat() { 194 | let s = run_and_capture("examples/stat"); 195 | let s_split = s.split("\n").collect::>(); 196 | // Device containing file : 66306 197 | let re = Regex::new(r"^Device containing file : [0-9]*$").unwrap(); 198 | assert!(re.is_match(s_split[0])); 199 | // assert!(s == "This is the contents of the file!\n"); 200 | } 201 | 202 | // TODO: set up / tear down? 203 | #[test] 204 | fn example_symlink() { 205 | let s = run_and_capture("examples/symlink"); 206 | let s_split = s.split("\n").collect::>(); 207 | assert!(s_split[0] == "Contents of symlink = ./data/tmp.txt"); 208 | assert!(s_split[1] == "Done!"); 209 | // assert!(s == "This is the contents of the file!\n"); 210 | } 211 | 212 | #[test] 213 | fn example_sync() { 214 | let s = run_and_capture("examples/sync"); 215 | assert!(s == "Done!\n"); 216 | } -------------------------------------------------------------------------------- /src/os/platform/macos.rs: -------------------------------------------------------------------------------- 1 | //! Contains call implementations that are specific to MacOs 2 | //! See src/tcb/os_specs for the raw system calls. 3 | 4 | use crate::tcb::misc::{bitwise_or_u32, fresh_rusage}; 5 | use crate::tcb::os_specs::*; 6 | #[cfg(feature = "verify")] 7 | use crate::tcb::verifier::*; 8 | use crate::types::*; 9 | use crate::{effect, effects}; 10 | use prusti_contracts::*; 11 | use syscall::syscall; 12 | use wave_macros::{external_call, with_ghost_var}; 13 | 14 | use mach2::mach_time::mach_timebase_info_data_t; 15 | 16 | // Call does not exist on Mac. Do nothing... 17 | #[with_ghost_var(trace: &mut Trace)] 18 | #[requires(ctx_safe(ctx))] 19 | #[requires(trace_safe(trace, ctx))] 20 | #[ensures(ctx_safe(ctx))] 21 | #[ensures(trace_safe(trace, ctx))] 22 | #[ensures(effects!(old(trace), trace))] 23 | pub fn trace_advise( 24 | ctx: &VmCtx, 25 | fd: HostFd, 26 | offset: i64, 27 | len: i64, 28 | advice: i32, 29 | ) -> RuntimeResult { 30 | Ok(0) 31 | } 32 | 33 | //// FROM: https://lists.apple.com/archives/darwin-dev/2007/Dec/msg00040.html 34 | #[with_ghost_var(trace: &mut Trace)] 35 | #[external_call(bitwise_or_u32)] 36 | #[requires(ctx_safe(ctx))] 37 | #[requires(trace_safe(trace, ctx))] 38 | #[ensures(ctx_safe(ctx))] 39 | #[ensures(trace_safe(trace, ctx))] 40 | #[ensures(effects!(old(trace), trace, effect!(FdAccess)))] 41 | pub fn trace_allocate(ctx: &VmCtx, fd: HostFd, offset: i64, len: i64) -> RuntimeResult { 42 | let os_fd: usize = fd.to_raw(); 43 | let fstore = libc::fstore_t { 44 | // we want to allocate contiguous space, and we want to allocate all space or none (TODO: CHECK THIS) 45 | fst_flags: bitwise_or_u32(libc::F_ALLOCATECONTIG, libc::F_ALLOCATEALL), 46 | // .. there are only two modes F_PEOFPOSMODE and F_VOLPOSMODE 47 | // neither of them seem correct but unsure... 48 | fst_posmode: libc::F_PEOFPOSMODE, 49 | fst_offset: offset, 50 | fst_length: len, 51 | fst_bytesalloc: 0, 52 | }; 53 | let r = os_allocate(os_fd, &fstore); 54 | RuntimeError::from_syscall_ret(r) 55 | } 56 | 57 | // Inspired from https://opensource.apple.com/source/Libc/Libc-1158.1.2/gen/clock_gettime.c.auto.html 58 | #[with_ghost_var(trace: &mut Trace)] 59 | #[external_call(fresh_rusage)] 60 | #[requires(ctx_safe(ctx))] 61 | #[requires(trace_safe(trace, ctx))] 62 | #[ensures(ctx_safe(ctx))] 63 | #[ensures(trace_safe(trace, ctx))] 64 | #[ensures(effects!(old(trace), trace))] 65 | pub fn trace_clock_get_time( 66 | ctx: &VmCtx, 67 | clock_id: libc::clockid_t, 68 | spec: &mut libc::timespec, 69 | ) -> RuntimeResult { 70 | // TODO: redo this?? 71 | // look at https://opensource.apple.com/source/Libc/Libc-320.1.3/i386/mach/mach_absolute_time.c.auto.html 72 | let r = match clock_id { 73 | libc::CLOCK_REALTIME => { 74 | let mut tv = libc::timeval { 75 | tv_sec: 0, 76 | tv_usec: 0, 77 | }; 78 | let ret = os_gettimeofday(&mut tv); 79 | // TODO: refactor -> timeval_to_timespec function or macro... 80 | spec.tv_sec = tv.tv_sec; 81 | spec.tv_nsec = (tv.tv_usec * 1000) as i64; 82 | ret 83 | } 84 | libc::CLOCK_MONOTONIC => { 85 | // Computes a monotonic clock by subtracting the real_time with the boot_time 86 | // from https://opensource.apple.com/source/xnu/xnu-3789.41.3/tools/tests/darwintests/mach_boottime_usec.c.auto.html 87 | let mut boot_tv = libc::timeval { 88 | tv_sec: 0, 89 | tv_usec: 0, 90 | }; 91 | let ret = os_getboottime(&mut boot_tv); 92 | if ret != 0 { 93 | return RuntimeError::from_syscall_ret(ret); 94 | } 95 | let mut real_tv = libc::timeval { 96 | tv_sec: 0, 97 | tv_usec: 0, 98 | }; 99 | let ret = os_gettimeofday(&mut real_tv); 100 | // from https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwj-rZepot_0AhVtFjQIHasdDq4QFnoECAMQAQ&url=https%3A%2F%2Fopensource.apple.com%2Fsource%2Fxnu%2Fxnu-344%2Fbsd%2Fsys%2Ftime.h&usg=AOvVaw3WH-hjCN8NBpw9CTx_3Eer 101 | let mut diff_sec = real_tv.tv_sec - boot_tv.tv_sec; 102 | let mut diff_usec = real_tv.tv_usec - boot_tv.tv_usec; 103 | if diff_usec < 0 { 104 | diff_sec -= 1; 105 | diff_usec += 1_000_000; 106 | } 107 | spec.tv_sec = diff_sec; 108 | spec.tv_nsec = (diff_usec * 1000) as i64; 109 | ret 110 | } 111 | libc::CLOCK_PROCESS_CPUTIME_ID => { 112 | let mut ru: libc::rusage = fresh_rusage(); 113 | let ret = os_rusageself(&mut ru); 114 | if ret != 0 { 115 | return RuntimeError::from_syscall_ret(ret); 116 | } 117 | // from https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwj-rZepot_0AhVtFjQIHasdDq4QFnoECAMQAQ&url=https%3A%2F%2Fopensource.apple.com%2Fsource%2Fxnu%2Fxnu-344%2Fbsd%2Fsys%2Ftime.h&usg=AOvVaw3WH-hjCN8NBpw9CTx_3Eer 118 | let mut sum_sec = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec; 119 | let mut sum_usec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec; 120 | if sum_usec > 1_000_000 { 121 | sum_sec += 1; 122 | sum_usec -= 1_000_000; 123 | } 124 | spec.tv_sec = sum_sec; 125 | spec.tv_nsec = (sum_usec * 1000) as i64; 126 | ret 127 | } 128 | libc::CLOCK_THREAD_CPUTIME_ID => { 129 | let ret = os_thread_selfusage(); 130 | if ret == 0 { 131 | // TODO: -1 probably wrong... 132 | return RuntimeError::from_syscall_ret(-1); 133 | } 134 | spec.tv_sec = ret as i64 / 1_000_000_000; 135 | spec.tv_nsec = ret as i64 % 1_000_000_000; 136 | 0 137 | } 138 | _ => { 139 | return Err(RuntimeError::Einval); 140 | } 141 | }; 142 | RuntimeError::from_syscall_ret(r) 143 | } 144 | 145 | ////From: https://opensource.apple.com/source/Libc/Libc-1158.1.2/gen/clock_gettime.c.auto.html 146 | #[with_ghost_var(trace: &mut Trace)] 147 | #[requires(ctx_safe(ctx))] 148 | #[requires(trace_safe(trace, ctx))] 149 | #[ensures(ctx_safe(ctx))] 150 | #[ensures(trace_safe(trace, ctx))] 151 | #[ensures(effects!(old(trace), trace))] 152 | pub fn trace_clock_get_res( 153 | ctx: &VmCtx, 154 | clock_id: libc::clockid_t, 155 | spec: &mut libc::timespec, 156 | ) -> RuntimeResult { 157 | match clock_id { 158 | libc::CLOCK_REALTIME 159 | | libc::CLOCK_MONOTONIC 160 | | libc::CLOCK_PROCESS_CPUTIME_ID 161 | | libc::CLOCK_THREAD_CPUTIME_ID => { 162 | spec.tv_nsec = 1_000; 163 | spec.tv_sec = 0; 164 | Ok(0) 165 | } 166 | _ => Err(RuntimeError::Einval), 167 | } 168 | } 169 | 170 | // based on https://opensource.apple.com/source/Libc/Libc-1158.50.2/gen/nanosleep.c.auto.html 171 | #[with_ghost_var(trace: &mut Trace)] 172 | #[requires(ctx_safe(ctx))] 173 | #[requires(trace_safe(trace, ctx))] 174 | #[ensures(ctx_safe(ctx))] 175 | #[ensures(trace_safe(trace, ctx))] 176 | #[ensures(effects!(old(trace), trace))] 177 | pub fn trace_nanosleep( 178 | ctx: &VmCtx, 179 | req: &libc::timespec, 180 | rem: &mut libc::timespec, 181 | ) -> RuntimeResult { 182 | let nanos = req.tv_sec * 1_000_000_000 + req.tv_nsec; 183 | let mut timebase_info = mach_timebase_info_data_t { numer: 0, denom: 0 }; 184 | // TODO: handle errors 185 | os_timebase_info(&mut timebase_info); 186 | // TODO: do we need to worry about overflow? 187 | let mach_ticks = (nanos * timebase_info.numer as i64) * timebase_info.denom as i64; 188 | // TODO: handle errors and type cast 189 | let current_ticks = os_absolute_time(); 190 | os_wait_until(current_ticks + (mach_ticks as u64)); 191 | Ok(0) 192 | } 193 | -------------------------------------------------------------------------------- /src/tcb/os_specs/platform/macos.rs: -------------------------------------------------------------------------------- 1 | use crate::tcb::misc::flag_set; 2 | use crate::tcb::sbox_mem::raw_ptr; 3 | #[cfg(feature = "verify")] 4 | use crate::tcb::verifier::*; 5 | use crate::verifier_interface::{push_syscall_result, start_timer, stop_timer}; 6 | use crate::{effect, effects, path_effect}; 7 | use core::ffi::c_void; 8 | use libc::{__error, utimensat}; 9 | use prusti_contracts::*; 10 | use syscall::syscall; 11 | use wave_macros::{external_calls, external_methods, with_ghost_var}; 12 | use crate::syscall_spec_gen; 13 | 14 | use mach2::mach_time::{ 15 | mach_absolute_time, mach_timebase_info, mach_timebase_info_data_t, mach_timebase_info_t, 16 | mach_wait_until, 17 | }; 18 | use security_framework_sys::random::{kSecRandomDefault, SecRandomCopyBytes}; 19 | 20 | //https://man7.org/linux/man-pages/man2/pread.2.html 21 | syscall_spec_gen! { 22 | trace; 23 | requires((buf.len() >= cnt)); 24 | ensures((effects!(old(trace), trace, effect!(FdAccess), effect!(WriteMem, addr, count) if addr == old(raw_ptr(buf)) && count == cnt))); 25 | ensures((old(raw_ptr(buf)) == raw_ptr(buf))); 26 | syscall(pread, fd: usize, buf: &mut [u8], cnt: usize, offset: usize) 27 | } 28 | 29 | //https://man7.org/linux/man-pages/man2/pwrite.2.html 30 | syscall_spec_gen! { 31 | trace; 32 | requires((buf.len() >= cnt)); 33 | ensures((effects!(old(trace), trace, effect!(FdAccess), effect!(ReadMem, addr, count) if addr == old(raw_ptr(buf)) && count == cnt))); 34 | syscall(pwrite, fd: usize, buf: &[u8], cnt: usize, offset: usize) 35 | } 36 | 37 | syscall_spec_gen! { 38 | trace; 39 | ensures((effects!(old(trace), trace, effect!(FdAccess)))); 40 | syscall(allocate, fd: usize, fstore: &libc::fstore_t) 41 | } 42 | 43 | // https://man7.org/linux/man-pages/man2/fstatat.2.html 44 | syscall_spec_gen! { 45 | trace; 46 | ensures((effects!(old(trace), trace, effect!(FdAccess), path_effect!(PathAccessAt, fd, p, f) if fd == dirfd && p == old(path) && f == (flags == 0)))); 47 | syscall(fstatat64, dirfd: usize, path: [u8; 4096], stat: &mut libc::stat, flags: i32) 48 | } 49 | 50 | syscall_spec_gen! { 51 | trace; 52 | requires((specs.len() >= 2)); 53 | ensures((effects!(old(trace), trace, effect!(FdAccess)))); 54 | syscall(futimens, fd: usize, specs: &Vec) 55 | } 56 | 57 | //https://man7.org/linux/man-pages/man2/utimensat.2.html 58 | #[with_ghost_var(trace: &mut Trace)] 59 | #[external_calls(utimensat, __error)] 60 | #[requires(specs.len() >= 2)] 61 | #[trusted] 62 | #[ensures(effects!(old(trace), trace, effect!(FdAccess), path_effect!(PathAccessAt, fd, p, f) if fd == dirfd && p == old(path) && f == !flag_set(flags, libc::AT_SYMLINK_NOFOLLOW)))] 63 | pub fn os_utimensat( 64 | dirfd: usize, 65 | path: [u8; 4096], 66 | specs: &Vec, 67 | flags: libc::c_int, 68 | ) -> isize { 69 | // TODO: There is no direct utimensat syscall on osx. Instead, we will just call the 70 | // libc wrapper 71 | let res = unsafe { 72 | utimensat( 73 | dirfd as i32, 74 | path.as_ptr() as *const i8, 75 | specs.as_ptr(), 76 | flags, 77 | ) as isize 78 | }; 79 | if res == -1 { 80 | // convert errno to -errno to conform to our expected syscall api 81 | -1 * unsafe { *__error() } as isize 82 | } else { 83 | res 84 | } 85 | } 86 | 87 | syscall_spec_gen! { 88 | trace; 89 | ensures((effects!(old(trace), trace))); 90 | syscall(gettimeofday, timeval: &mut libc::timeval, zero: usize) 91 | } 92 | 93 | #[with_ghost_var(trace: &mut Trace)] 94 | #[external_calls(size_of)] 95 | #[external_methods(into)] 96 | #[trusted] 97 | #[ensures(effects!(old(trace), trace))] 98 | pub fn os_getboottime(timeval: &mut libc::timeval) -> isize { 99 | let __start_ts = start_timer(); 100 | // boot time is available through sysctl 101 | // should these conversions happen in trace_clock_get_time instead? 102 | let sysctl_name = vec![libc::CTL_KERN, libc::KERN_BOOTTIME]; 103 | let sysctl_len: libc::size_t = sysctl_name.len().into(); 104 | let tv_size: libc::size_t = std::mem::size_of::().into(); 105 | // T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.boottime", &bt_tv, &len, NULL, 0), NULL); 106 | let result = unsafe { 107 | syscall!( 108 | __SYSCTL, 109 | sysctl_name.as_ptr(), 110 | &sysctl_len as *const libc::size_t, 111 | timeval as *mut libc::timeval, 112 | &tv_size as *const usize, 113 | 0, 114 | 0 115 | ) as isize 116 | }; 117 | let __end_ts = stop_timer(); 118 | push_syscall_result("getboottime", __start_ts, __end_ts); 119 | result 120 | } 121 | 122 | syscall_spec_gen! { 123 | trace; 124 | ensures((effects!(old(trace), trace))); 125 | syscall(rusageself, rusage: &mut libc::rusage) 126 | } 127 | 128 | // TODO: unclear to me that the raw syscall! will handle return values correctly. 129 | // e.g. from https://opensource.apple.com/source/xnu/xnu-792.25.20/bsd/kern/syscalls.master 130 | // it seems that this directly returns the value as ret val. 131 | syscall_spec_gen! { 132 | trace; 133 | ensures((effects!(old(trace), trace))); 134 | syscall(thread_selfusage) 135 | } 136 | 137 | #[with_ghost_var(trace: &mut Trace)] 138 | #[external_calls(SecRandomCopyBytes)] 139 | #[requires(buf.len() >= cnt)] 140 | #[trusted] 141 | #[ensures(old(raw_ptr(buf)) == raw_ptr(buf))] 142 | #[ensures(effects!(old(trace), trace, effect!(WriteMem, addr, count) if addr == old(raw_ptr(buf)) && count == cnt))] 143 | pub fn os_getrandom(buf: &mut [u8], cnt: usize, flags: u32) -> isize { 144 | // no native syscall, use mac's secure random framework. 145 | // May also just read from /dev/random, but then its subject to File Descriptor exhaustion. 146 | 147 | // TODO: handle return value 148 | unsafe { 149 | SecRandomCopyBytes(kSecRandomDefault, cnt, buf.as_mut_ptr() as *mut c_void); 150 | } 151 | 0 152 | } 153 | 154 | // https://opensource.apple.com/source/xnu/xnu-7194.81.3/osfmk/kern/clock.c.auto.html 155 | // Waits until the deadline (in absolute time, mach ticks) has passed 156 | // To use, you should call os_timebase_info for the conversion between mach_ticks and 157 | // nanoseconds first. 158 | #[with_ghost_var(trace: &mut Trace)] 159 | #[external_calls(mach_wait_until)] 160 | #[trusted] 161 | #[ensures(effects!(old(trace), trace))] 162 | pub fn os_wait_until(deadline: u64) -> isize { 163 | let result = unsafe { mach_wait_until(deadline) as isize }; 164 | result 165 | } 166 | 167 | #[with_ghost_var(trace: &mut Trace)] 168 | #[external_calls(mach_wait_until)] 169 | #[trusted] 170 | #[ensures(effects!(old(trace), trace))] 171 | pub fn os_absolute_time() -> u64 { 172 | let result = unsafe { mach_absolute_time() }; 173 | result 174 | } 175 | 176 | #[with_ghost_var(trace: &mut Trace)] 177 | #[external_calls(mach_timebase_info)] 178 | #[trusted] 179 | #[ensures(effects!(old(trace), trace))] 180 | pub fn os_timebase_info(info: &mut mach_timebase_info_data_t) -> isize { 181 | // TODO: handle return value 182 | let result = unsafe { mach_timebase_info(info as mach_timebase_info_t) as isize }; 183 | result 184 | } 185 | 186 | #[with_ghost_var(trace: &mut Trace)] 187 | #[external_methods(set_len)] 188 | #[trusted] 189 | #[requires(dirp.capacity() >= count)] 190 | #[ensures(effects!(old(trace), trace, effect!(FdAccess)))] 191 | pub fn os_getdents64(fd: usize, dirp: &mut Vec, count: usize) -> isize { 192 | let __start_ts = start_timer(); 193 | // TODO: safe to put 0 in for basep? TODO... 194 | // TODO: ensure directory entry format is correct... 195 | let mut basep: u64 = 0; 196 | let result = unsafe { 197 | let result = syscall!( 198 | GETDIRENTRIES, 199 | fd, 200 | dirp.as_mut_ptr(), 201 | count, 202 | &mut basep as *mut u64 203 | ); 204 | if (result as isize) != -1 { 205 | dirp.set_len(result); 206 | } else { 207 | dirp.set_len(0); 208 | } 209 | result as isize 210 | }; 211 | let __end_ts = stop_timer(); 212 | push_syscall_result("getdirentries", __start_ts, __end_ts); 213 | result 214 | } 215 | 216 | //https://man7.org/linux/man-pages/man2/fstat.2.html 217 | syscall_spec_gen! { 218 | trace; 219 | ensures((effects!(old(trace), trace, effect!(FdAccess)))); 220 | syscall(fstat64 ALIAS fstat, fd: usize, stat: &mut libc::stat) 221 | } 222 | -------------------------------------------------------------------------------- /tools/fuzz-gen/src/rewriter.rs: -------------------------------------------------------------------------------- 1 | use crate::specifications::common::{ExpressionIdGenerator, SpecificationIdGenerator}; 2 | use crate::specifications::untyped::{self, EncodeTypeCheck}; 3 | use proc_macro2::{Span, TokenStream}; 4 | use quote::{quote_spanned, format_ident}; 5 | use syn::spanned::Spanned; 6 | use syn::{Type, punctuated::Punctuated, Pat, Token}; 7 | 8 | pub(crate) struct AstRewriter { 9 | expr_id_generator: ExpressionIdGenerator, 10 | spec_id_generator: SpecificationIdGenerator, 11 | } 12 | 13 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 14 | pub enum SpecItemType { 15 | Precondition, 16 | Postcondition, 17 | Predicate, 18 | } 19 | 20 | impl std::fmt::Display for SpecItemType { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | match self { 23 | SpecItemType::Precondition => write!(f, "pre"), 24 | SpecItemType::Postcondition => write!(f, "post"), 25 | SpecItemType::Predicate => write!(f, "pred"), 26 | } 27 | } 28 | } 29 | 30 | impl AstRewriter { 31 | pub(crate) fn new() -> Self { 32 | Self { 33 | expr_id_generator: ExpressionIdGenerator::new(), 34 | spec_id_generator: SpecificationIdGenerator::new(), 35 | } 36 | } 37 | 38 | pub fn generate_spec_id(&mut self) -> untyped::SpecificationId { 39 | self.spec_id_generator.generate() 40 | } 41 | 42 | /// Parse an assertion. 43 | pub fn parse_assertion( 44 | &mut self, 45 | spec_id: untyped::SpecificationId, 46 | tokens: TokenStream, 47 | ) -> syn::Result { 48 | untyped::Assertion::parse(tokens, spec_id, &mut self.expr_id_generator) 49 | } 50 | 51 | /// Parse a pledge. 52 | pub fn parse_pledge( 53 | &mut self, 54 | spec_id_lhs: Option, 55 | spec_id_rhs: untyped::SpecificationId, 56 | tokens: TokenStream 57 | ) -> syn::Result { 58 | untyped::Pledge::parse(tokens, spec_id_lhs, spec_id_rhs, &mut self.expr_id_generator) 59 | } 60 | 61 | /// Check whether function `item` contains a parameter called `keyword`. If 62 | /// yes, return its span. 63 | fn check_contains_keyword_in_params(&self, item: &untyped::AnyFnItem, keyword: &str) -> Option { 64 | for param in &item.sig().inputs { 65 | if let syn::FnArg::Typed(syn::PatType { pat, .. }) = param { 66 | if let syn::Pat::Ident(syn::PatIdent { ident, .. }) = &**pat { 67 | if ident == keyword { 68 | return Some(param.span()); 69 | } 70 | } 71 | } 72 | } 73 | None 74 | } 75 | fn generate_result_arg(&self, item: &untyped::AnyFnItem) -> syn::FnArg { 76 | let item_span = item.span(); 77 | let output_ty = match &item.sig().output { 78 | syn::ReturnType::Default => parse_quote_spanned!(item_span=> ()), 79 | syn::ReturnType::Type(_, ty) => ty.clone(), 80 | }; 81 | let fn_arg = syn::FnArg::Typed( 82 | syn::PatType { 83 | attrs: Vec::new(), 84 | pat: Box::new(parse_quote_spanned!(item_span=> result)), 85 | colon_token: syn::Token![:](item.sig().output.span()), 86 | ty: output_ty, 87 | } 88 | ); 89 | fn_arg 90 | } 91 | 92 | /// Generate a dummy function for checking the given precondition, postcondition or predicate. 93 | /// 94 | /// `spec_type` should be either `"pre"`, `"post"` or `"pred"`. 95 | pub fn generate_spec_item_fn( 96 | &mut self, 97 | spec_type: SpecItemType, 98 | spec_id: untyped::SpecificationId, 99 | assertion: untyped::Assertion, 100 | item: &untyped::AnyFnItem, 101 | ) -> syn::Result { 102 | if let Some(span) = self.check_contains_keyword_in_params(item, "result") { 103 | return Err(syn::Error::new( 104 | span, 105 | "it is not allowed to use the keyword `result` as a function argument".to_string(), 106 | )); 107 | } 108 | let item_span = item.span(); 109 | let item_name = syn::Ident::new( 110 | &format!("prusti_{}_item_{}_{}", spec_type, item.sig().ident, spec_id), 111 | item_span, 112 | ); 113 | let mut statements = TokenStream::new(); 114 | assertion.encode_type_check(&mut statements); 115 | let spec_id_str = spec_id.to_string(); 116 | let assertion_json = crate::specifications::json::to_json_string(&assertion); 117 | 118 | let mut spec_item: syn::ItemFn = parse_quote_spanned! {item_span=> 119 | #[allow(unused_must_use, unused_variables, dead_code)] 120 | #[prusti::spec_only] 121 | #[prusti::spec_id = #spec_id_str] 122 | #[prusti::assertion = #assertion_json] 123 | fn #item_name() { 124 | #statements 125 | } 126 | }; 127 | spec_item.sig.generics = item.sig().generics.clone(); 128 | spec_item.sig.inputs = item.sig().inputs.clone(); 129 | if spec_type == SpecItemType::Postcondition { 130 | let fn_arg = self.generate_result_arg(item); 131 | spec_item.sig.inputs.push(fn_arg); 132 | } 133 | Ok(syn::Item::Fn(spec_item)) 134 | } 135 | 136 | /// Generate statements for checking the given loop invariant. 137 | pub fn generate_spec_loop( 138 | &mut self, 139 | spec_id: untyped::SpecificationId, 140 | assertion: untyped::Assertion, 141 | ) -> TokenStream { 142 | let mut statements = TokenStream::new(); 143 | assertion.encode_type_check(&mut statements); 144 | let spec_id_str = spec_id.to_string(); 145 | let assertion_json = crate::specifications::json::to_json_string(&assertion); 146 | let callsite_span = Span::call_site(); 147 | quote_spanned! {callsite_span=> 148 | #[allow(unused_must_use, unused_variables)] 149 | { 150 | #[prusti::spec_only] 151 | #[prusti::loop_body_invariant_spec] 152 | #[prusti::spec_id = #spec_id_str] 153 | #[prusti::assertion = #assertion_json] 154 | || { 155 | #statements 156 | }; 157 | } 158 | } 159 | } 160 | 161 | /// Generate statements for checking a closure specification. 162 | /// TODO: arguments, result (types are typically not known yet after parsing...) 163 | pub fn generate_cl_spec( 164 | &mut self, 165 | inputs: Punctuated, 166 | output: Type, 167 | preconds: Vec<(untyped::SpecificationId, untyped::Assertion)>, 168 | postconds: Vec<(untyped::SpecificationId, untyped::Assertion)> 169 | ) -> (TokenStream, TokenStream) { 170 | let process_cond = |is_post: bool, id: &untyped::SpecificationId, 171 | assertion: &untyped::Assertion| -> TokenStream 172 | { 173 | let spec_id_str = id.to_string(); 174 | let mut encoded = TokenStream::new(); 175 | assertion.encode_type_check(&mut encoded); 176 | let assertion_json = crate::specifications::json::to_json_string(assertion); 177 | let name = format_ident!("prusti_{}_closure_{}", if is_post { "post" } else { "pre" }, spec_id_str); 178 | let callsite_span = Span::call_site(); 179 | let result = if is_post && !inputs.empty_or_trailing() { 180 | quote_spanned! { callsite_span => , result: #output } 181 | } else if is_post { 182 | quote_spanned! { callsite_span => result: #output } 183 | } else { 184 | TokenStream::new() 185 | }; 186 | quote_spanned! { callsite_span => 187 | #[prusti::spec_only] 188 | #[prusti::spec_id = #spec_id_str] 189 | #[prusti::assertion = #assertion_json] 190 | fn #name(#inputs #result) { 191 | #encoded 192 | } 193 | } 194 | }; 195 | 196 | let mut pre_ts = TokenStream::new(); 197 | for (id, precond) in preconds { 198 | pre_ts.extend(process_cond(false, &id, &precond)); 199 | } 200 | 201 | let mut post_ts = TokenStream::new(); 202 | for (id, postcond) in postconds { 203 | post_ts.extend(process_cond(true, &id, &postcond)); 204 | } 205 | 206 | (pre_ts, post_ts) 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/tcb/sbox_mem.rs: -------------------------------------------------------------------------------- 1 | // use crate::do_effect; 2 | #[cfg(feature = "verify")] 3 | use crate::tcb::verifier::*; 4 | use crate::types::*; 5 | use crate::{effect, effects}; 6 | use prusti_contracts::*; 7 | use std::ptr::{copy, copy_nonoverlapping}; 8 | use wave_macros::{external_calls, external_methods, with_ghost_var}; 9 | 10 | // use libc::{mmap, mprotect, munmap}; 11 | use crate::tcb::misc::bitwise_or; 12 | use libc::c_void; 13 | use libc::{MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE}; 14 | use libc::{PROT_NONE, PROT_READ, PROT_WRITE}; 15 | use prusti_contracts::*; 16 | use std::ptr; 17 | 18 | // 1 << 32 = 4GB 19 | const FOUR_GB: usize = 1 << 32; 20 | // 1 << 33 = 8GB 21 | const EIGHT_GB: usize = 1 << 33; 22 | 23 | // Uninterpreted predicate meant to accompany slice_mem_mut 24 | // result is equal to the offset into memory that slice came from, i.e. 25 | // slice.start - mem.start 26 | // if the slice did not come from memory, then the return value will be unconstrained (i.e., any pointer) 27 | #[pure] 28 | #[trusted] 29 | pub fn as_sbox_ptr(slice: &[u8]) -> usize { 30 | unimplemented!() 31 | } 32 | 33 | #[pure] 34 | #[trusted] 35 | #[ensures(result >= 0)] 36 | pub fn raw_ptr(memptr: &[u8]) -> HostPtr { 37 | unimplemented!() 38 | } 39 | 40 | impl VmCtx { 41 | /// Function for memcpy from sandbox to host 42 | /// Overwrites contents of vec 43 | #[with_ghost_var(trace: &mut Trace)] 44 | #[external_calls(copy_nonoverlapping)] 45 | #[external_methods(set_len)] 46 | //#[requires(n as usize <= self.memlen)] 47 | #[requires(dst.capacity() >= (n as usize) )] 48 | #[requires(self.fits_in_lin_mem(src, n, trace))] 49 | #[requires(ctx_safe(self))] 50 | #[requires(trace_safe(trace, self))] 51 | #[ensures(ctx_safe(self))] 52 | // #[ensures(trace_safe(trace, self))] 53 | #[ensures(dst.len() == (n as usize) )] 54 | // #[ensures(old(raw_ptr(self.mem.as_slice())) == raw_ptr(self.mem.as_slice()))] 55 | #[ensures(effects!(old(trace), trace, effect!(ReadMem, addr, count) if 56 | addr == raw_ptr(self.mem.as_slice()) + src as usize && 57 | count == n as usize 58 | ))] 59 | #[trusted] 60 | pub fn memcpy_from_sandbox(&self, dst: &mut Vec, src: SboxPtr, n: u32) { 61 | unsafe { 62 | copy_nonoverlapping( 63 | self.mem.as_ptr().offset(src as isize), 64 | dst.as_mut_ptr(), 65 | n as usize, 66 | ); 67 | dst.set_len(n as usize); // TODO: wrong, need to make sure copy_nonoverlapping actually copied it 68 | }; 69 | // do_effect!(effect!(ReadMem, src, n)); 70 | } 71 | 72 | /// Function for memcpy from sandbox to host 73 | #[with_ghost_var(trace: &mut Trace)] 74 | #[external_calls(copy_nonoverlapping)] 75 | // #[requires(src.len() >= (n as usize) )] 76 | #[requires(self.fits_in_lin_mem(dst, n, trace))] 77 | #[requires(ctx_safe(self))] 78 | #[requires(trace_safe(trace, self))] 79 | #[ensures(ctx_safe(self))] 80 | #[ensures(trace_safe(trace, self))] 81 | // #[ensures(old(raw_ptr(self.mem.as_slice())) == raw_ptr(self.mem.as_slice()))] 82 | #[ensures(effects!(old(trace), trace, effect!(WriteMem, addr, count) if 83 | addr == raw_ptr(self.mem.as_slice()) + dst as usize && 84 | count == n as usize 85 | ))] 86 | #[trusted] 87 | pub fn memcpy_to_sandbox(&mut self, dst: SboxPtr, src: &Vec, n: u32) { 88 | unsafe { 89 | copy_nonoverlapping( 90 | src.as_ptr(), 91 | self.mem.as_mut_ptr().offset(dst as isize), 92 | n as usize, 93 | ) 94 | }; 95 | } 96 | 97 | // Currently trusted because it causes a fold-unfold error 98 | #[with_ghost_var(trace: &mut Trace)] 99 | #[requires(self.fits_in_lin_mem(ptr, len, trace))] 100 | #[requires(trace_safe(trace, self))] 101 | // #[ensures(trace_safe(trace, old(self).memlen))] 102 | #[ensures(result.len() == (len as usize))] 103 | #[ensures(effects!(old(trace), trace))] 104 | #[ensures(raw_ptr(result) == old(raw_ptr(self.mem.as_slice())) + ptr as usize)] 105 | //#[ensures(raw_ptr(result) == raw_ptr(self.mem.as_slice()) + ptr as usize)] 106 | // #[ensures(result == old(self.mem.as_slice()))] 107 | //#[ensures(old(raw_ptr(self.mem.as_slice())) == raw_ptr(self.mem.as_slice()))] 108 | //#[after_expiry(old(self.netlist) == self.netlist)] 109 | #[after_expiry( 110 | ctx_safe(self) && 111 | old(raw_ptr(self.mem.as_slice()) + ptr as usize) == before_expiry(raw_ptr(result)) && 112 | raw_ptr(self.mem.as_slice()) + ptr as usize == before_expiry(raw_ptr(result)) && 113 | old(self.netlist) == self.netlist && 114 | old(self.homedir_host_fd) == self.homedir_host_fd)] 115 | #[trusted] 116 | pub fn slice_mem_mut(&mut self, ptr: SboxPtr, len: u32) -> &mut [u8] { 117 | let start = ptr as usize; 118 | let end = ptr as usize + len as usize; 119 | &mut self.mem[start..end] 120 | } 121 | 122 | // This needs to be trusted only because I can't seem to convice Prusti 123 | // that these safe memory writes do not update the linmem ptr 124 | #[with_ghost_var(trace: &mut Trace)] 125 | #[requires(self.fits_in_lin_mem_usize(offset, 1, trace))] 126 | #[requires(ctx_safe(self))] 127 | #[requires(trace_safe(trace, self))] 128 | #[ensures(ctx_safe(self))] 129 | #[ensures(trace_safe(trace, self))] 130 | #[trusted] 131 | pub fn write_u8(&mut self, offset: usize, v: u8) { 132 | self.mem[offset] = v; 133 | } 134 | 135 | #[with_ghost_var(trace: &mut Trace)] 136 | #[requires(ctx_safe(self))] 137 | #[requires(trace_safe(trace, self))] 138 | #[ensures(ctx_safe(self))] 139 | #[ensures(trace_safe(trace, self))] 140 | // #[ensures(self.fits_in_lin_mem_usize(result.iov_base, result.iov_len, trace))] 141 | #[ensures( 142 | result.iov_base == raw_ptr(self.mem.as_slice()) + (iov.iov_base as usize) && 143 | result.iov_len == (iov.iov_len as usize) 144 | )] 145 | #[external_methods(as_ptr, offset)] 146 | #[trusted] 147 | pub fn translate_iov(&self, iov: WasmIoVec) -> NativeIoVec { 148 | let swizzled_base = unsafe { self.mem.as_ptr().offset(iov.iov_base as isize) as usize }; 149 | NativeIoVec { 150 | iov_base: swizzled_base, 151 | iov_len: iov.iov_len as usize, 152 | } 153 | } 154 | } 155 | 156 | impl NativeIoVecs { 157 | #[trusted] 158 | #[ensures(self.len() == old(self.len()) + 1)] 159 | #[ensures(self.lookup(old(self.len())) == old(value))] 160 | #[ensures(forall(|i: usize| (i < old(self.len())) ==> 161 | self.lookup(i) == old(self.lookup(i))))] 162 | pub fn push(&mut self, value: NativeIoVec) { 163 | self.iovs.push(value); 164 | } 165 | } 166 | 167 | impl WasmIoVecs { 168 | #[trusted] 169 | #[ensures(self.len() == old(self.len()) + 1)] 170 | #[ensures(self.lookup(old(self.len())) == old(value))] 171 | #[ensures(forall(|i: usize| (i < old(self.len())) ==> 172 | self.lookup(i) == old(self.lookup(i))))] 173 | pub fn push(&mut self, value: WasmIoVec) { 174 | self.iovs.push(value); 175 | } 176 | } 177 | 178 | // Linear memory allocation stuff 179 | 180 | #[trusted] 181 | // #[requires(n <= dest.len())] 182 | // #[ensures(forall(|i: usize| (i < n) ==> dest[i] == c))] 183 | pub fn memset(dest: usize, c: u8, n: usize) { 184 | unsafe { 185 | libc::memset(dest as *mut c_void, c as i32, n); 186 | } 187 | } 188 | 189 | // // #[requires(addr == 0)] 190 | // // #[requires()] 191 | #[trusted] 192 | //#[ensures((result != MAP_FAILED) ==> mapping(result) == Some(Mmapping(len,prot)) ] 193 | pub fn mmap( 194 | addr: usize, 195 | len: usize, 196 | prot: i32, 197 | flags: i32, 198 | fd: i32, // fd to back to 199 | offset: i64, // offset into file to back to 200 | ) -> usize { 201 | unsafe { libc::mmap(addr as *mut c_void, len, prot, flags, fd, offset) as usize } 202 | } 203 | 204 | // #[ensures((result == 0) ==> mapping(addr) == None)] 205 | #[trusted] 206 | pub fn munmap(addr: usize, len: usize) -> i32 { 207 | unsafe { libc::munmap(addr as *mut libc::c_void, len) } 208 | } 209 | 210 | // #[ensures((result == 0) ==> )] 211 | #[trusted] 212 | pub fn mprotect(addr: usize, len: usize, prot: i32) -> i32 { 213 | unsafe { libc::mprotect(addr as *mut c_void, len, prot) } 214 | } 215 | 216 | // bodyless viper function 217 | #[pure] 218 | #[trusted] 219 | pub fn valid_linmem(memptr: usize) -> bool { 220 | unimplemented!() 221 | } 222 | 223 | #[trusted] 224 | #[ensures(valid_linmem(result))] 225 | fn wave_alloc_linmem() -> usize { 226 | let linmem_ptr = mmap( 227 | 0, // let the kernel place the region anywhere 228 | EIGHT_GB, // Linmem + guard page = 8GB 229 | bitwise_or(PROT_READ, PROT_WRITE), // its read/write 230 | bitwise_or(MAP_PRIVATE, MAP_ANONYMOUS), // should not be shared or backed-up to a file 231 | -1, // no file descrptor since we aren't backing to a file 232 | 0, // this arg doesn't matter since we aren't backing to a file 233 | ); 234 | // let x: [u8; 4] = [0,1,2,3]; 235 | // assert!( cool_ptr((&x).as_ptr()) ); 236 | mprotect(linmem_ptr + FOUR_GB, FOUR_GB, PROT_NONE); // Make second 4GB of linear memory a guard page 237 | memset(linmem_ptr, 0, FOUR_GB); // memzero 238 | linmem_ptr 239 | } 240 | -------------------------------------------------------------------------------- /src/poll.rs: -------------------------------------------------------------------------------- 1 | // use crate::os::trace_fionread; 2 | use crate::os::trace_fionread; 3 | use crate::runtime::*; 4 | #[cfg(feature = "verify")] 5 | use crate::tcb::verifier::external_specs::result::*; 6 | #[cfg(feature = "verify")] 7 | use crate::tcb::verifier::*; 8 | use crate::types::*; 9 | use crate::wrappers::wasi_clock_time_get; // TODO: remove this circular reference 10 | use prusti_contracts::*; 11 | use std::convert::{TryFrom, TryInto}; 12 | use wave_macros::{external_calls, external_methods, with_ghost_var}; 13 | use RuntimeError::*; 14 | 15 | #[with_ghost_var(trace: &mut Trace)] 16 | #[requires(ctx_safe(ctx))] 17 | #[requires(trace_safe(trace, ctx))] 18 | #[ensures(ctx_safe(ctx))] 19 | #[ensures(trace_safe(trace, ctx))] 20 | #[external_methods(push, checked_sub, try_into, subscription_clock_abstime)] 21 | #[external_calls(Some)] 22 | pub fn poll_parse_clock( 23 | ctx: &VmCtx, 24 | sub_clock: SubscriptionClock, 25 | precision: u64, 26 | min_timeout: &mut Option, 27 | timeouts: &mut Vec<(u64, Timestamp)>, 28 | userdata: u64, 29 | ) -> RuntimeResult<()> { 30 | // if the subscription is a clock, check if it is the shortest timeout. 31 | // let clock = subscription_clock.id; 32 | match sub_clock.id.try_into()? { 33 | // TODO: what clock source does posix poll use for timeouts? Will a relative 34 | // realtime be significantly different than monotonic? 35 | ClockId::Monotonic | ClockId::Realtime => { 36 | let now = wasi_clock_time_get(ctx, sub_clock.id, precision)?; 37 | let timeout: Timestamp = if sub_clock.flags.subscription_clock_abstime() { 38 | // if this is an absolute timeout, we need to wait the difference 39 | // between now and the timeout 40 | // This will also perform a checked cast to an i32, which will 41 | sub_clock.timeout.checked_sub(now).ok_or(Eoverflow)? 42 | } else { 43 | sub_clock.timeout 44 | }; 45 | 46 | if let Some(m_timeout) = min_timeout { 47 | if timeout < *m_timeout { 48 | *min_timeout = Some(timeout); 49 | } 50 | } else { 51 | *min_timeout = Some(timeout); 52 | } 53 | 54 | timeouts.push((userdata, timeout)); 55 | Ok(()) 56 | } 57 | // we don't support timeouts on other clock types 58 | _ => { 59 | return Err(Einval); 60 | } 61 | } 62 | } 63 | 64 | #[with_ghost_var(trace: &mut Trace)] 65 | #[requires(ctx_safe(ctx))] 66 | #[requires(trace_safe(trace, ctx))] 67 | #[ensures(ctx_safe(ctx))] 68 | #[ensures(trace_safe(trace, ctx))] 69 | #[external_methods(push, to_posix)] 70 | pub fn poll_parse_fds( 71 | ctx: &VmCtx, 72 | pollfds: &mut Vec, 73 | fd_data: &mut Vec<(u64, SubscriptionFdType)>, 74 | userdata: u64, 75 | subscription_readwrite: SubscriptionFdReadWrite, 76 | ) -> RuntimeResult<()> { 77 | let fd = ctx.fdmap.fd_to_native(subscription_readwrite.v_fd)?; 78 | let os_fd: usize = fd.to_raw(); 79 | // let event = match subscription_readwrite.typ { 80 | // SubscriptionFdType::Read => libc::POLLIN, 81 | // SubscriptionFdType::Write => libc::POLLOUT, 82 | // }; 83 | let event = subscription_readwrite.typ.to_posix(); 84 | // convert FD subscriptions to their libc versions 85 | let pollfd = libc::pollfd { 86 | fd: os_fd as i32, 87 | events: event, 88 | revents: 0, 89 | }; 90 | pollfds.push(pollfd); 91 | fd_data.push((userdata, subscription_readwrite.typ)); 92 | Ok(()) 93 | } 94 | 95 | #[with_ghost_var(trace: &mut Trace)] 96 | #[requires(ctx_safe(ctx))] 97 | #[requires(trace_safe(trace, ctx))] 98 | #[ensures(ctx_safe(ctx))] 99 | #[ensures(trace_safe(trace, ctx))] 100 | // #[external_calls(poll_handle_fds, poll_handle_clock)] 101 | pub fn parse_subscriptions( 102 | ctx: &VmCtx, 103 | in_ptr: u32, 104 | nsubscriptions: u32, 105 | precision: u64, 106 | min_timeout: &mut Option, 107 | timeouts: &mut Vec<(u64, Timestamp)>, 108 | pollfds: &mut Vec, 109 | fd_data: &mut Vec<(u64, SubscriptionFdType)>, 110 | ) -> RuntimeResult<()> { 111 | let mut i = 0; 112 | while i < nsubscriptions { 113 | body_invariant!(ctx_safe(ctx)); 114 | body_invariant!(trace_safe(trace, ctx)); 115 | 116 | let sub_offset = i * Subscription::WASI_SIZE; 117 | 118 | if !ctx.fits_in_lin_mem_usize( 119 | (in_ptr + sub_offset) as usize, 120 | Subscription::WASI_SIZE as usize, 121 | ) { 122 | return Err(Eoverflow); 123 | } 124 | 125 | let subscription = Subscription::read(ctx, in_ptr + sub_offset)?; 126 | 127 | match subscription.subscription_u { 128 | SubscriptionInner::Clock(subscription_clock) => { 129 | poll_parse_clock( 130 | ctx, 131 | subscription_clock, 132 | precision, 133 | min_timeout, 134 | timeouts, 135 | subscription.userdata, 136 | )?; 137 | } 138 | SubscriptionInner::Fd(subscription_readwrite) => { 139 | poll_parse_fds( 140 | ctx, 141 | pollfds, 142 | fd_data, 143 | subscription.userdata, 144 | subscription_readwrite, 145 | )?; 146 | } 147 | } 148 | 149 | i += 1; 150 | } 151 | Ok(()) 152 | } 153 | 154 | #[with_ghost_var(trace: &mut Trace)] 155 | #[requires(ctx_safe(ctx))] 156 | #[requires(trace_safe(trace, ctx))] 157 | #[ensures(ctx_safe(ctx))] 158 | #[ensures(trace_safe(trace, ctx))] 159 | pub fn writeback_timeouts( 160 | ctx: &mut VmCtx, 161 | out_ptr: u32, 162 | timeouts: &Vec<(u64, Timestamp)>, 163 | min_timeout: &Option, 164 | ) -> RuntimeResult { 165 | let mut num_events_written = 0; 166 | let mut event_idx = 0; 167 | while event_idx < timeouts.len() { 168 | body_invariant!(ctx_safe(ctx)); 169 | body_invariant!(trace_safe(trace, ctx)); 170 | 171 | let (userdata, timeout) = timeouts[event_idx]; 172 | let event_offset = (num_events_written * Event::WASI_SIZE) as usize; 173 | if !ctx.fits_in_lin_mem_usize(out_ptr as usize + event_offset, Event::WASI_SIZE as usize) { 174 | return Err(Eoverflow); 175 | } 176 | // Technically we know there must be a min_timeout, but use if let to be safe 177 | if let Some(m_timeout) = min_timeout { 178 | if timeout == *m_timeout { 179 | let event = Event { 180 | userdata, 181 | error: RuntimeError::Success, 182 | typ: EventType::Clock, 183 | fd_readwrite: None, 184 | }; 185 | event.write(ctx, out_ptr + event_offset as u32); 186 | num_events_written += 1; 187 | } 188 | } 189 | event_idx += 1; 190 | } 191 | 192 | return Ok(num_events_written); 193 | } 194 | 195 | #[with_ghost_var(trace: &mut Trace)] 196 | #[requires(ctx_safe(ctx))] 197 | #[requires(trace_safe(trace, ctx))] 198 | #[ensures(ctx_safe(ctx))] 199 | #[ensures(trace_safe(trace, ctx))] 200 | #[external_calls(from_posix, from_poll_revents, Some)] 201 | #[external_methods(to_event_type)] 202 | pub fn writeback_fds( 203 | ctx: &mut VmCtx, 204 | out_ptr: u32, 205 | pollfds: &Vec, 206 | fd_data: &Vec<(u64, SubscriptionFdType)>, 207 | ) -> RuntimeResult { 208 | let mut num_events_written = 0; 209 | let mut event_idx = 0; 210 | while event_idx < fd_data.len() { 211 | body_invariant!(ctx_safe(ctx)); 212 | body_invariant!(trace_safe(trace, ctx)); 213 | 214 | let (userdata, sub_type) = fd_data[event_idx]; 215 | // let typ = match sub_type { 216 | // SubscriptionFdType::Read => EventType::FdRead, 217 | // SubscriptionFdType::Write => EventType::FdWrite, 218 | // }; 219 | let typ = sub_type.to_event_type(); 220 | 221 | let pollfd = pollfds[event_idx]; 222 | 223 | // if no event ocurred, continue 224 | if pollfd.revents == 0 { 225 | continue; 226 | } 227 | 228 | let event_offset = (num_events_written * Event::WASI_SIZE) as usize; 229 | if !ctx.fits_in_lin_mem_usize(out_ptr as usize + event_offset, Event::WASI_SIZE as usize) { 230 | return Err(Eoverflow); 231 | } 232 | 233 | // get the number of bytes for reading... 234 | // TODO: If we want, we can remove this, and always return 1 for reads 235 | let nbytes = match typ { 236 | EventType::FdRead => trace_fionread(ctx, HostFd::from_raw(pollfd.fd as usize))? as u64, 237 | // no way to check number of bytes for writing 238 | _ => 0, 239 | }; 240 | 241 | let fd_readwrite = EventFdReadWrite { 242 | nbytes, 243 | flags: EventRwFlags::from_posix(pollfd.revents), 244 | }; 245 | 246 | let error = RuntimeError::from_poll_revents(pollfd.revents); 247 | 248 | let event = Event { 249 | userdata, 250 | error, 251 | typ, 252 | fd_readwrite: Some(fd_readwrite), 253 | }; 254 | event.write(ctx, out_ptr + event_offset as u32); 255 | num_events_written += 1; 256 | 257 | event_idx += 1; 258 | } 259 | return Ok(num_events_written); 260 | } 261 | --------------------------------------------------------------------------------