From eb74a306ff87fd39d1f8b0d4b8f1b63c5d515ea1 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 15 Feb 2022 17:11:08 -0500 Subject: [PATCH] Wasm traps (#301) * feat: Preliminary WASI with fib workload * refactor: Clarify initialize globals * chore: Update empty to WASI * chore: cleanup fib test * chore: cleanup build tooling * chore: cleanup test Makefiles and some nits * chore: Update LLVM and install WASI-SDK * chore: Update build tools and specs * docs: Update example module spec in README * refactor: Clean up HTTP handling * feat: Implement exit WASI call * style: apply clang-format * ci: rewrite compile sledge step * build: Remove LLVM install shims * build: Try manually adding libunwind * build: Try adding libunwind-dev * ci: break out aWsm compile step * fix: Correct test build error * fix: Correct error in WASI fd_write * chore: Increase gocr http buffer size * test: Correct image resize test * chore: Remove zombie wasmception functions * chore: Reduce dummy args to single arg * chore: Add debugging makefile fivebyeight * chore: Remove erronious PHONYs in tests Makefile * ci: Disable gocr tests * chore: Add wat Make rule to fibonacci test * chore: fix apt package name * chore: Enable clean of failed ck install * chore: use LLVM 12 * test: Disable gocr tests * chore: Enhance test makefile * chore: Add CFILES as sledgert dep * chore: Add NULL check for function table pointer * chore: Add missing header * chore: uncomment cleanup in imageresize test * refactor: Remove unused linear memory functions * build: Add bimodal debug makefile * chore: Add linear memory debug logs * refactor: Cleanup region initialization * build: Correct PHONY in runtime Makefile * chore: deb install script for outside of container * refactor: Remove zombie extern. * feat: WebAssembly traps * refactor: Use C18 features * chore: Remove git diff annotations * fix: tweaks to run all sample apps with WASI * test: convert shell script to Makefile * build: clean generated ck Makefile * chore: Use awsm branch with fixes * chore: Revert name changes * fix: Correct type issues * refactor: Reverse additional name change * refactor: Remove awsm compat shims * chore: Remove libc association * build: Better detect header file changes * refactor: current_wasm_module_instance_trap * test: reenable tests * chore: Delete copied script * build: Fix test workloads * fix: Implement HTTP 500 * fix: Protect against overflow on comparison * build: Replace test.sh with makefile * refactor: blow away tmp directory conflicts * refactor: centralize wasm in single submodule * feat: libsledge and sledge ABI * chore: move tests * refactor: tests * chore: update wasm_apps with new sample data * doc: Initial ABI README * feat: globals table * docs: Merge aWsm ABI docs * docs: libsledge ABI * build: rename apps to keep consistent * build: Disable wasm proposals * build: Update wasm apps and fix typo * test: test makefiles * test: Additional test makefiles * build: top-level build and install rules wo Docker * docs: Add wasm lld comment * build: top level makefile * chore: merge debug flags * fix: Correct out of bounds error * feat: indirection to awsm ABI * fix: Correct link hack with proper flag * fix: gps typo * chore: format nit * ci: update makefile rules * ci: check WASI_SDK_PATH * fix: Adjust paths * ci: fix make rule name * refactor: Attempt to use generic vec * refactor: Remove type-specific vec * fix: Resolve assorted TODOs * chore: fix clang format issue * ci: Invalidate app cache on libsledge changes * fix: Correct wasm trap check * fix: free wasm globals * docs: example of running top level tests via make * chore: option to log unsupported wasi * test: add preempt client generator for fib bimodal * refactor: Allocate wasm memory with 4096 align * fix: Handle build without runtime globals * refactor: bypass runtime call for first global * fix: Correct sandbox logging * test: fix incorrect paths in test.mk * refactor: cleanup basic traps logic * test: trap_divzero * fix: Correct inverted trap control flow * ci: Invalidate awsm cache * ci: divide by zero trap * chore: Assorted cleanup * ci: Install httpie * chore: Remove stdio calls in libsledge * chore: update submodules --- .github/workflows/main.yaml | 5 ++ applications/Makefile | 3 + libsledge/README.md | 6 +- libsledge/include/sledge_abi.h | 18 ++++- libsledge/src/memory_instructions.c | 85 +++++++++++++++++++--- libsledge/src/numeric_instructions.c | 44 +++++++---- libsledge/src/table_instructions.c | 17 +++-- libsledge/src/variable_instructions.c | 6 +- runtime/include/arch/arch_context_t.h | 2 + runtime/include/current_sandbox.h | 13 ++++ runtime/include/sandbox_set_as_error.h | 2 - runtime/include/wasm_globals.h | 33 +++++---- runtime/include/wasm_table.h | 17 ++++- runtime/src/current_sandbox.c | 62 ++++++++++++++-- runtime/src/current_wasm_module_instance.c | 13 ++++ runtime/src/libc/wasi_impl_serverless.c | 9 +-- runtime/src/sledge_abi.c | 33 +++++++-- test.mk | 7 ++ tests/traps/.gitignore | 4 + tests/traps/Makefile | 61 ++++++++++++++++ tests/traps/edf_nopreemption.env | 2 + tests/traps/edf_preemption.env | 3 + tests/traps/fifo_nopreemption.env | 2 + tests/traps/fifo_preemption.env | 2 + tests/traps/run.sh | 32 ++++++++ tests/traps/spec.json | 13 ++++ 26 files changed, 414 insertions(+), 80 deletions(-) create mode 100644 tests/traps/.gitignore create mode 100644 tests/traps/Makefile create mode 100644 tests/traps/edf_nopreemption.env create mode 100644 tests/traps/edf_preemption.env create mode 100644 tests/traps/fifo_nopreemption.env create mode 100644 tests/traps/fifo_preemption.env create mode 100755 tests/traps/run.sh create mode 100644 tests/traps/spec.json diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c0e6951..452cf24 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -72,6 +72,7 @@ jobs: fonts-cascadia-code \ fonts-roboto \ gnuplot \ + httpie \ imagemagick \ netpbm \ pango1.0-tools \ @@ -177,3 +178,7 @@ jobs: # run: | # make -f test.mk TODO # if: success() || failure() + - name: Wasm Trap Divide by Zero + run: | + make -f test.mk trap_divzero + if: success() || failure() diff --git a/applications/Makefile b/applications/Makefile index af4d660..d243c70 100644 --- a/applications/Makefile +++ b/applications/Makefile @@ -79,3 +79,6 @@ gps_ekf.install: ../runtime/bin/gps_ekf.wasm.so .PHONY: license_plate_detection.install license_plate_detection.install: ../runtime/bin/license_plate_detection.wasm.so + +.PHONY: trap_divzero.install +trap_divzero.install: ../runtime/bin/trap_divzero.wasm.so diff --git a/libsledge/README.md b/libsledge/README.md index 254adad..d40bdd7 100644 --- a/libsledge/README.md +++ b/libsledge/README.md @@ -34,9 +34,9 @@ Here is a list of WebAssembly instructions that depend on symbols from libsledge The ABI includes the -| Instruction | aWsm ABI | libc Dependencies | SLEdge ABI | -| ------------- | ------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| call_indirect | `get_function_from_table` | `stderr`, `fprintf` | `sledge_abi__current_wasm_module_instance.table`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_INVALID_INDEX`, `WASM_TRAP_MISMATCHED_FUNCTION_TYPE` | +| Instruction | aWsm ABI | libc Dependencies | SLEdge ABI | +| ------------- | ------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| call_indirect | `get_function_from_table` | `stderr`, `fprintf` | `sledge_abi__current_wasm_module_instance.table`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_INVALID_INDEX`, `WASM_TRAP_MISMATCHED_TYPE` | ### [Variable Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#variable-instructions) diff --git a/libsledge/include/sledge_abi.h b/libsledge/include/sledge_abi.h index c72c87b..11b16a0 100644 --- a/libsledge/include/sledge_abi.h +++ b/libsledge/include/sledge_abi.h @@ -3,7 +3,6 @@ #include #include #include -#include #include /* Do not include runtime headers here! */ @@ -43,16 +42,29 @@ struct sledge_abi__wasm_module_instance { /* Private runtime state follows */ }; +/* Based on example traps listed at https://webassembly.org/docs/security/ */ +enum sledge_abi__wasm_trap +{ + WASM_TRAP_EXIT = 1, + WASM_TRAP_INVALID_INDEX = 2, + WASM_TRAP_MISMATCHED_TYPE = 3, + WASM_TRAP_PROTECTED_CALL_STACK_OVERFLOW = 4, + WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY = 5, + WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION = 6, + WASM_TRAP_COUNT +}; + /* Symbols expected from sledgert */ +extern void sledge_abi__wasm_trap_raise(enum sledge_abi__wasm_trap trapno); extern int32_t sledge_abi__wasm_memory_expand(struct sledge_abi__wasm_memory *wasm_memory, uint32_t page_count); void sledge_abi__wasm_memory_initialize_region(struct sledge_abi__wasm_memory *wasm_memory, uint32_t offset, uint32_t region_size, uint8_t region[]); extern int32_t sledge_abi__wasm_globals_get_i32(uint32_t idx); extern int64_t sledge_abi__wasm_globals_get_i64(uint32_t idx); -extern int32_t sledge_abi__wasm_globals_set_i32(uint32_t idx, int32_t value, bool is_mutable); -extern int32_t sledge_abi__wasm_globals_set_i64(uint32_t idx, int64_t value, bool is_mutable); +extern void sledge_abi__wasm_globals_set_i32(uint32_t idx, int32_t value, bool is_mutable); +extern void sledge_abi__wasm_globals_set_i64(uint32_t idx, int64_t value, bool is_mutable); /* Wasm initialization functions generated by the aWsm compiler */ extern void sledge_abi__init_globals(void); diff --git a/libsledge/src/memory_instructions.c b/libsledge/src/memory_instructions.c index 6b788ad..bf36bdf 100644 --- a/libsledge/src/memory_instructions.c +++ b/libsledge/src/memory_instructions.c @@ -18,7 +18,12 @@ INLINE float get_f32(uint32_t offset) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(float) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(float) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + return *(float *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset]; } @@ -26,7 +31,12 @@ INLINE double get_f64(uint32_t offset) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(double) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(double) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + return *(double *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset]; } @@ -34,7 +44,12 @@ INLINE int8_t get_i8(uint32_t offset) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int8_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int8_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + return *(int8_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset]; } @@ -42,7 +57,12 @@ INLINE int16_t get_i16(uint32_t offset) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int16_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int16_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + return *(int16_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset]; } @@ -50,7 +70,12 @@ INLINE int32_t get_i32(uint32_t offset) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int32_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int32_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + return *(int32_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset]; } @@ -58,7 +83,12 @@ INLINE int64_t get_i64(uint32_t offset) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int64_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int64_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + return *(int64_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset]; } @@ -67,7 +97,12 @@ INLINE void set_f32(uint32_t offset, float value) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(float) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(float) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + *(float *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value; } @@ -75,7 +110,12 @@ INLINE void set_f64(uint32_t offset, double value) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(double) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(double) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + *(double *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value; } @@ -83,7 +123,12 @@ INLINE void set_i8(uint32_t offset, int8_t value) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int8_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int8_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + *(int8_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value; } @@ -91,7 +136,12 @@ INLINE void set_i16(uint32_t offset, int16_t value) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int16_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int16_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + *(int16_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value; } @@ -99,7 +149,12 @@ INLINE void set_i32(uint32_t offset, int32_t value) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int32_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int32_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + *(int32_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value; } @@ -107,10 +162,16 @@ INLINE void set_i64(uint32_t offset, int64_t value) { assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL); - assert((uint64_t)offset + sizeof(int64_t) <= sledge_abi__current_wasm_module_instance.memory.size); + + if (unlikely((uint64_t)offset + (uint64_t)sizeof(int64_t) + > sledge_abi__current_wasm_module_instance.memory.size)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY); + } + *(int64_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value; } + /** * @brief Stub that implements the WebAssembly memory.grow instruction * diff --git a/libsledge/src/numeric_instructions.c b/libsledge/src/numeric_instructions.c index ca2d59f..3f0585f 100644 --- a/libsledge/src/numeric_instructions.c +++ b/libsledge/src/numeric_instructions.c @@ -54,56 +54,60 @@ rotr_u64(uint64_t n, uint64_t c_u64) INLINE uint32_t u32_div(uint32_t a, uint32_t b) { - assert(b); + if (unlikely(b == 0)) sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a / b; } INLINE uint32_t u32_rem(uint32_t a, uint32_t b) { - assert(b); + if (unlikely(b == 0)) sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a % b; } INLINE int32_t i32_div(int32_t a, int32_t b) { - assert(b && (a != INT32_MIN || b != -1)); + if (unlikely(b == 0 || (a == INT32_MIN && b == -1))) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a / b; } INLINE int32_t i32_rem(int32_t a, int32_t b) { - assert(b && (a != INT32_MIN || b != -1)); + if (unlikely(b == 0 || (a == INT32_MIN && b == -1))) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a % b; } INLINE uint64_t u64_div(uint64_t a, uint64_t b) { - assert(b); + if (unlikely(b == 0)) sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a / b; } INLINE uint64_t u64_rem(uint64_t a, uint64_t b) { - assert(b); + if (unlikely(b == 0)) sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a % b; } INLINE int64_t i64_div(int64_t a, int64_t b) { - assert(b && (a != INT64_MIN || b != -1)); + if (unlikely(b == 0 || (a == INT64_MIN && b == -1))) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a / b; } INLINE int64_t i64_rem(int64_t a, int64_t b) { - assert(b && (a != INT64_MIN || b != -1)); + if (unlikely(b == 0 || (a == INT64_MIN && b == -1))) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return a % b; } @@ -113,56 +117,64 @@ i64_rem(int64_t a, int64_t b) uint32_t u32_trunc_f32(float f) { - assert(0 <= f && f <= (float)UINT32_MAX); + if (unlikely(f < 0 || f > (float)UINT32_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (uint32_t)f; } int32_t i32_trunc_f32(float f) { - assert(INT32_MIN <= f && f <= (float)INT32_MAX); + if (unlikely(f < INT32_MIN || f > (float)INT32_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (int32_t)f; } uint32_t u32_trunc_f64(double f) { - assert(0 <= f && f <= (double)UINT32_MAX); + if (unlikely(f < 0 || f > (double)UINT32_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (uint32_t)f; } int32_t i32_trunc_f64(double f) { - assert(INT32_MIN <= f && f <= (double)INT32_MAX); + if (unlikely(f < (double)INT32_MIN || f > (double)INT32_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (int32_t)f; } uint64_t u64_trunc_f32(float f) { - assert(0 <= f && f <= (float)UINT64_MAX); + if (unlikely(f < 0 || f > (float)UINT64_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (uint64_t)f; } int64_t i64_trunc_f32(float f) { - assert(INT64_MIN <= f && f <= (float)INT64_MAX); + if (unlikely(f < (float)INT64_MIN || f > (float)INT64_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (int64_t)f; } uint64_t u64_trunc_f64(double f) { - assert(0 <= f && f <= (double)UINT64_MAX); + if (unlikely(f < 0 || f > (float)UINT64_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (uint64_t)f; } int64_t i64_trunc_f64(double f) { - assert(INT64_MIN <= f && f <= (double)INT64_MAX); + if (unlikely(f < (double)INT64_MIN || f > (double)INT64_MAX)) + sledge_abi__wasm_trap_raise(WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION); return (int64_t)f; } diff --git a/libsledge/src/table_instructions.c b/libsledge/src/table_instructions.c index df2938c..d858e56 100644 --- a/libsledge/src/table_instructions.c +++ b/libsledge/src/table_instructions.c @@ -11,12 +11,14 @@ static INLINE void * wasm_table_get(struct sledge_abi__wasm_table *wasm_table, uint32_t idx, uint32_t type_id) { assert(wasm_table != NULL); - assert(idx < wasm_table->capacity); + + if (unlikely(idx >= wasm_table->capacity)) { sledge_abi__wasm_trap_raise(WASM_TRAP_INVALID_INDEX); } struct sledge_abi__wasm_table_entry f = wasm_table->buffer[idx]; - assert(f.type_id == type_id); - assert(f.func_pointer != NULL); + if (unlikely(f.type_id != type_id)) { sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); } + + if (unlikely(f.func_pointer == NULL)) { sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); } return f.func_pointer; } @@ -44,12 +46,15 @@ get_function_from_table(uint32_t idx, uint32_t type_id) { assert(sledge_abi__current_wasm_module_instance.table != NULL); - assert(idx < sledge_abi__current_wasm_module_instance.table->capacity); + if (unlikely(idx >= sledge_abi__current_wasm_module_instance.table->capacity)) { + sledge_abi__wasm_trap_raise(WASM_TRAP_INVALID_INDEX); + } struct sledge_abi__wasm_table_entry f = sledge_abi__current_wasm_module_instance.table->buffer[idx]; - assert(f.type_id == type_id); - assert(f.func_pointer != NULL); + if (unlikely(f.type_id != type_id)) { sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); } + + if (unlikely(f.func_pointer == NULL)) { sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); } return f.func_pointer; } diff --git a/libsledge/src/variable_instructions.c b/libsledge/src/variable_instructions.c index a895997..58a399e 100644 --- a/libsledge/src/variable_instructions.c +++ b/libsledge/src/variable_instructions.c @@ -35,8 +35,7 @@ set_global_i32(uint32_t idx, int32_t value) } else { /* aWsm does not currently pass the is_mutable flag, so all runtime globals are assumed to be mutable. This is true if aWsm uses the flags to inline constant globals */ - int rc = sledge_abi__wasm_globals_set_i32(idx, value, true); - assert(rc == 0); + sledge_abi__wasm_globals_set_i32(idx, value, true); } } @@ -48,7 +47,6 @@ set_global_i64(uint32_t idx, int64_t value) } else { /* aWsm does not currently pass the is_mutable flag, so all runtime globals are assumed to be mutable. This is true if aWsm uses the flags to inline constant globals */ - int rc = sledge_abi__wasm_globals_set_i64(idx, value, true); - assert(rc == 0); + sledge_abi__wasm_globals_set_i64(idx, value, true); } } diff --git a/runtime/include/arch/arch_context_t.h b/runtime/include/arch/arch_context_t.h index bb8483b..e1c1bac 100644 --- a/runtime/include/arch/arch_context_t.h +++ b/runtime/include/arch/arch_context_t.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "arch/reg_t.h" @@ -10,4 +11,5 @@ struct arch_context { arch_context_variant_t variant; reg_t regs[UREG_COUNT]; mcontext_t mctx; + jmp_buf start_buf; }; diff --git a/runtime/include/current_sandbox.h b/runtime/include/current_sandbox.h index 2f1f2c4..ea42c48 100644 --- a/runtime/include/current_sandbox.h +++ b/runtime/include/current_sandbox.h @@ -96,3 +96,16 @@ current_sandbox_memory_writeback(void) memcpy(¤t_sandbox->memory->abi, &sledge_abi__current_wasm_module_instance.abi.memory, sizeof(struct sledge_abi__wasm_memory)); } + +static inline void +current_sandbox_trap(enum sledge_abi__wasm_trap trapno) +{ + assert(trapno != 0); + assert(trapno < WASM_TRAP_COUNT); + + struct sandbox *sandbox = current_sandbox_get(); + assert(sandbox != NULL); + assert(sandbox->state == SANDBOX_RUNNING_USER || sandbox->state == SANDBOX_RUNNING_SYS); + + longjmp(sandbox->ctxt.start_buf, trapno); +} diff --git a/runtime/include/sandbox_set_as_error.h b/runtime/include/sandbox_set_as_error.h index e918e6d..aa5b628 100644 --- a/runtime/include/sandbox_set_as_error.h +++ b/runtime/include/sandbox_set_as_error.h @@ -19,8 +19,6 @@ * Unmaps linear memory, removes from the runqueue (if on it), and adds to the completion queue * Because the stack is still in use, freeing the stack is deferred until later * - * TODO: Is the sandbox adding itself to the completion queue here? Is this a problem? Issue #94 - * * @param sandbox the sandbox erroring out * @param last_state the state the sandbox is transitioning from. This is expressed as a constant to * enable the compiler to perform constant propagation optimizations. diff --git a/runtime/include/wasm_globals.h b/runtime/include/wasm_globals.h index 9b049bc..6a5493b 100644 --- a/runtime/include/wasm_globals.h +++ b/runtime/include/wasm_globals.h @@ -54,33 +54,36 @@ wasm_globals_update_if_used(struct vec_wasm_global_t *globals, uint32_t idx, uin if (likely(global->type != WASM_GLOBAL_TYPE_UNUSED)) *dest = (uint64_t)global->value.i64; } -static inline int32_t -wasm_globals_get_i32(struct vec_wasm_global_t *globals, uint32_t idx) +static inline int +wasm_globals_get_i32(struct vec_wasm_global_t *globals, uint32_t idx, int32_t *return_val) { wasm_global_t *global = vec_wasm_global_t_get(globals, idx); - assert(global != NULL); - assert(global->type == WASM_GLOBAL_TYPE_I32); + if (unlikely(global == NULL)) return -1; + if (unlikely(global->type != WASM_GLOBAL_TYPE_I32)) return -2; - return global->value.i32; + *return_val = global->value.i32; + return 0; } -static inline int64_t -wasm_globals_get_i64(struct vec_wasm_global_t *globals, uint32_t idx) +static inline int +wasm_globals_get_i64(struct vec_wasm_global_t *globals, uint32_t idx, int64_t *return_val) { wasm_global_t *global = vec_wasm_global_t_get(globals, idx); - assert(global != NULL); - assert(global->type == WASM_GLOBAL_TYPE_I64); + if (unlikely(global == NULL)) return -1; + if (unlikely(global->type != WASM_GLOBAL_TYPE_I64)) return -2; - return global->value.i64; + *return_val = global->value.i64; + return 0; } -static inline int32_t +// 0 on success, -1 on out of bounds, -2 on mismatched type +static inline int wasm_globals_set_i32(struct vec_wasm_global_t *globals, uint32_t idx, int32_t value, bool is_mutable) { wasm_global_t *current = vec_wasm_global_t_get(globals, idx); - assert(current->type == WASM_GLOBAL_TYPE_UNUSED || current->mut == true); + if (unlikely(current->type != WASM_GLOBAL_TYPE_UNUSED && current->mut == false)) return -2; int rc = vec_wasm_global_t_insert(globals, idx, (wasm_global_t){ @@ -88,12 +91,14 @@ wasm_globals_set_i32(struct vec_wasm_global_t *globals, uint32_t idx, int32_t va return rc; } -static inline int32_t +// 0 on success, -1 on out of bounds, -2 on mismatched type +static inline int wasm_globals_set_i64(struct vec_wasm_global_t *globals, uint32_t idx, int64_t value, bool is_mutable) { wasm_global_t *current = vec_wasm_global_t_get(globals, idx); - assert(current->type == WASM_GLOBAL_TYPE_UNUSED || current->mut == true); + if (unlikely(current->type != WASM_GLOBAL_TYPE_UNUSED && current->mut == false)) return -2; + // Returns -1 if idx > capacity int rc = vec_wasm_global_t_insert(globals, idx, (wasm_global_t){ .mut = is_mutable, .type = WASM_GLOBAL_TYPE_I64, .value = value }); diff --git a/runtime/include/wasm_table.h b/runtime/include/wasm_table.h index 281b93c..d9be90b 100644 --- a/runtime/include/wasm_table.h +++ b/runtime/include/wasm_table.h @@ -7,6 +7,10 @@ #include "sledge_abi.h" #include "wasm_types.h" +/* Redeclared due to circular header dependency */ +extern void sledge_abi__current_wasm_module_instance_trap(enum sledge_abi__wasm_trap trapno); + + /* memory also provides the table access functions */ #define INDIRECT_TABLE_SIZE (1 << 10) @@ -76,7 +80,11 @@ static INLINE void * wasm_table_get(struct sledge_abi__wasm_table *wasm_table, uint32_t idx, uint32_t type_id) { assert(wasm_table != NULL); - assert(idx < wasm_table->capacity); + + if (unlikely(idx >= wasm_table->capacity)) { + fprintf(stderr, "idx: %u, Table size: %u\n", idx, INDIRECT_TABLE_SIZE); + sledge_abi__current_wasm_module_instance_trap(WASM_TRAP_INVALID_INDEX); + } struct sledge_abi__wasm_table_entry f = wasm_table->buffer[idx]; assert(f.type_id == type_id); @@ -90,7 +98,12 @@ static INLINE void wasm_table_set(struct sledge_abi__wasm_table *wasm_table, uint32_t idx, uint32_t type_id, char *pointer) { assert(wasm_table != NULL); - assert(idx < wasm_table->capacity); + + if (unlikely(idx >= wasm_table->capacity)) { + fprintf(stderr, "idx: %u, Table size: %u\n", idx, INDIRECT_TABLE_SIZE); + sledge_abi__current_wasm_module_instance_trap(WASM_TRAP_INVALID_INDEX); + } + assert(pointer != NULL); /* TODO: atomic for multiple concurrent invocations? Issue #97 */ diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index 5fe08c4..23e0281 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -1,4 +1,6 @@ #include +#include +#include #include "current_sandbox.h" #include "current_sandbox_send_response.h" @@ -77,6 +79,51 @@ current_sandbox_exit() assert(0); } +void +current_sandbox_wasm_trap_handler(int trapno) +{ + char *error_message = NULL; + struct sandbox *sandbox = current_sandbox_get(); + sandbox_syscall(sandbox); + + switch (trapno) { + case WASM_TRAP_EXIT: + break; + case WASM_TRAP_INVALID_INDEX: + error_message = "WebAssembly Trap: Invalid Index\n"; + client_socket_send(sandbox->client_socket_descriptor, http_header_build(500), http_header_len(500), + current_sandbox_sleep); + break; + case WASM_TRAP_MISMATCHED_TYPE: + error_message = "WebAssembly Trap: Mismatched Type\n"; + client_socket_send(sandbox->client_socket_descriptor, http_header_build(500), http_header_len(500), + current_sandbox_sleep); + break; + case WASM_TRAP_PROTECTED_CALL_STACK_OVERFLOW: + error_message = "WebAssembly Trap: Protected Call Stack Overflow\n"; + client_socket_send(sandbox->client_socket_descriptor, http_header_build(500), http_header_len(500), + current_sandbox_sleep); + break; + case WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY: + error_message = "WebAssembly Trap: Out of Bounds Linear Memory Access\n"; + client_socket_send(sandbox->client_socket_descriptor, http_header_build(500), http_header_len(500), + current_sandbox_sleep); + break; + case WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION: + error_message = "WebAssembly Trap: Illegal Arithmetic Operation\n"; + client_socket_send(sandbox->client_socket_descriptor, http_header_build(500), http_header_len(500), + current_sandbox_sleep); + break; + } + + debuglog("%s", error_message); + sandbox_close_http(sandbox); + generic_thread_dump_lock_overhead(); + current_sandbox_exit(); + assert(0); +} + + static inline struct sandbox * current_sandbox_init() { @@ -127,8 +174,6 @@ err: sandbox_close_http(sandbox); generic_thread_dump_lock_overhead(); current_sandbox_exit(); - assert(0); - return NULL; } @@ -178,8 +223,15 @@ err: void current_sandbox_start(void) { - struct sandbox *sandbox = current_sandbox_init(); - struct module *current_module = sandbox_get_module(sandbox); - sandbox->return_value = module_entrypoint(current_module); + struct sandbox *sandbox = current_sandbox_init(); + + int rc = setjmp(sandbox->ctxt.start_buf); + if (rc == 0) { + struct module *current_module = sandbox_get_module(sandbox); + sandbox->return_value = module_entrypoint(current_module); + } else { + current_sandbox_wasm_trap_handler(rc); + } + current_sandbox_fini(); } diff --git a/runtime/src/current_wasm_module_instance.c b/runtime/src/current_wasm_module_instance.c index 5984ff2..fe1fff6 100644 --- a/runtime/src/current_wasm_module_instance.c +++ b/runtime/src/current_wasm_module_instance.c @@ -16,3 +16,16 @@ thread_local struct wasm_module_instance sledge_abi__current_wasm_module_instanc .abi.wasmg_0 = 0xDEADBEEF, .wasi_context = NULL, }; + +void +sledge_abi__current_wasm_module_instance_trap(enum sledge_abi__wasm_trap trapno) +{ + assert(trapno != 0); + assert(trapno < WASM_TRAP_COUNT); + + struct sandbox *sandbox = current_sandbox_get(); + assert(sandbox != NULL); + assert(sandbox->state == SANDBOX_RUNNING_USER || sandbox->state == SANDBOX_RUNNING_SYS); + + longjmp(sandbox->ctxt.start_buf, trapno); +} diff --git a/runtime/src/libc/wasi_impl_serverless.c b/runtime/src/libc/wasi_impl_serverless.c index 33fb83f..2ab6c51 100644 --- a/runtime/src/libc/wasi_impl_serverless.c +++ b/runtime/src/libc/wasi_impl_serverless.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -933,9 +934,6 @@ wasi_snapshot_preview1_backing_path_link(wasi_context_t *context, __wasi_fd_t ol * @param path_baseptr path of the file or directory to open relative to the fd directory. * @param path_len * @param oflags The method by which to open the file. - * - - * * @param fs_rights_base * @param fs_rights_inheriting * @param fdflags @@ -945,7 +943,6 @@ wasi_snapshot_preview1_backing_path_link(wasi_context_t *context, __wasi_fd_t ol __wasi_errno_t wasi_snapshot_preview1_backing_path_open(wasi_context_t *context, __wasi_fd_t dirfd, __wasi_lookupflags_t dirflags, const char *path, __wasi_size_t path_len, __wasi_oflags_t oflags, - __wasi_rights_t fs_rights_base, __wasi_rights_t fs_rights_inheriting, __wasi_fdflags_t fdflags, __wasi_fd_t *fd) { @@ -1067,7 +1064,9 @@ wasi_snapshot_preview1_backing_poll_oneoff(wasi_context_t *context, const __wasi noreturn void wasi_snapshot_preview1_backing_proc_exit(wasi_context_t *context, __wasi_exitcode_t exitcode) { - assert(0); + struct sandbox *s = current_sandbox_get(); + s->return_value = exitcode; + longjmp(s->ctxt.start_buf, WASM_TRAP_EXIT); } /** diff --git a/runtime/src/sledge_abi.c b/runtime/src/sledge_abi.c index 51154b1..da82e0c 100644 --- a/runtime/src/sledge_abi.c +++ b/runtime/src/sledge_abi.c @@ -6,6 +6,12 @@ #include "wasi.h" #include "wasi_serdes.h" +EXPORT void +sledge_abi__wasm_trap_raise(enum sledge_abi__wasm_trap trapno) +{ + return current_sandbox_trap(trapno); +} + /** * @brief Get the memory ptr for runtime object * @@ -84,10 +90,14 @@ sledge_abi__wasm_globals_get_i32(uint32_t idx) struct sandbox *sandbox = current_sandbox_get(); sandbox_syscall(sandbox); - int32_t value = wasm_globals_get_i32(&sandbox->globals, idx); + int32_t result; + int rc = wasm_globals_get_i32(&sandbox->globals, idx, &result); sandbox_return(sandbox); - return value; + if (rc == -1) sledge_abi__wasm_trap_raise(WASM_TRAP_INVALID_INDEX); + if (rc == -2) sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); + + return result; } EXPORT int64_t @@ -96,13 +106,17 @@ sledge_abi__wasm_globals_get_i64(uint32_t idx) struct sandbox *sandbox = current_sandbox_get(); sandbox_syscall(sandbox); - int64_t value = wasm_globals_get_i64(&sandbox->globals, idx); + int64_t result; + int rc = wasm_globals_get_i64(&sandbox->globals, idx, &result); sandbox_return(sandbox); - return value; + if (rc == -1) sledge_abi__wasm_trap_raise(WASM_TRAP_INVALID_INDEX); + if (rc == -2) sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); + + return result; } -EXPORT int32_t +EXPORT void sledge_abi__wasm_globals_set_i32(uint32_t idx, int32_t value, bool is_mutable) { struct sandbox *sandbox = current_sandbox_get(); @@ -111,10 +125,12 @@ sledge_abi__wasm_globals_set_i32(uint32_t idx, int32_t value, bool is_mutable) int32_t rc = wasm_globals_set_i32(&sandbox->globals, idx, value, true); sandbox_return(sandbox); - return rc; + if (rc == -1) sledge_abi__wasm_trap_raise(WASM_TRAP_INVALID_INDEX); + if (rc == -2) sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); } -EXPORT int32_t +// 0 on success, -1 on out of bounds, -2 on mismatched type +EXPORT void sledge_abi__wasm_globals_set_i64(uint32_t idx, int64_t value, bool is_mutable) { struct sandbox *sandbox = current_sandbox_get(); @@ -123,7 +139,8 @@ sledge_abi__wasm_globals_set_i64(uint32_t idx, int64_t value, bool is_mutable) int32_t rc = wasm_globals_set_i64(&sandbox->globals, idx, value, true); sandbox_return(sandbox); - return rc; + if (rc == -1) sledge_abi__wasm_trap_raise(WASM_TRAP_INVALID_INDEX); + if (rc == -2) sledge_abi__wasm_trap_raise(WASM_TRAP_MISMATCHED_TYPE); } /** diff --git a/test.mk b/test.mk index ed98db3..bc237d4 100755 --- a/test.mk +++ b/test.mk @@ -115,6 +115,13 @@ empty__concurrency: ./runtime/bin/empty.wasm.so # cd ./tests/empty/concurrency/ && ./install.sh cd ./tests/empty/concurrency/ && ./run.sh +./runtime/bin/trap_divzero.wasm.so: + make trap_divzero.install -C ./applications + +PHONY: trap_divzero +trap_divzero: ./runtime/bin/trap_divzero.wasm.so + cd ./tests/traps/ && ./run.sh + all: \ gocr__all \ ekf__all \ diff --git a/tests/traps/.gitignore b/tests/traps/.gitignore new file mode 100644 index 0000000..723e9af --- /dev/null +++ b/tests/traps/.gitignore @@ -0,0 +1,4 @@ +*res.dat +rt.log +log.csv +res/* diff --git a/tests/traps/Makefile b/tests/traps/Makefile new file mode 100644 index 0000000..6fc8c82 --- /dev/null +++ b/tests/traps/Makefile @@ -0,0 +1,61 @@ +RUNTIME_DIR=../../runtime/ +SLEDGE_BINARY_DIR=${RUNTIME_DIR}/bin +SLEDGE_TESTS_DIR=${RUNTIME_DIR}/tests +HOSTNAME=localhost +DURATION_SEC=15 + +all: run + +clean: + make -C ${RUNTIME_DIR} clean + make -C ${SLEDGE_TESTS_DIR} clean + rm -f ${SLEDGE_BINARY_DIR}/trap_divzero.wasm.so + +${SLEDGE_BINARY_DIR}/sledgert: + make -C ${RUNTIME_DIR} runtime + +.PHONY: sledgert +sledgert: ${SLEDGE_BINARY_DIR}/sledgert + +${SLEDGE_BINARY_DIR}/trap_divzero.wasm.so: + make -C ../../../applications trap_divzero.install + +.PHONY: trap_divzero +trap_divzero: ${SLEDGE_BINARY_DIR}/trap_divzero.wasm.so + +run: sledgert trap_divzero + LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} ${SLEDGE_BINARY_DIR}/sledgert spec.json + +debug: sledgert trap_divzero + SLEDGE_DISABLE_PREEMPTION=true SLEDGE_NWORKERS=1 \ + LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} gdb ${SLEDGE_BINARY_DIR}/sledgert \ + --eval-command="handle SIGUSR1 noprint nostop" \ + --eval-command="handle SIGPIPE noprint nostop" \ + --eval-command="set pagination off" \ + --eval-command="run spec.json" + +client-ok: + echo "1" | http :10000 + +client-trap: + echo "0" | http :10000 + +test: + echo "4" | http :10000 + echo "0" | http :10000 + echo "3" | http :10000 + echo "0" | http :10000 + echo "2" | http :10000 + echo "0" | http :10000 + echo "1" | http :10000 + echo "0" | http :10000 + echo "1" | http :10000 + echo "0" | http :10000 + echo "2" | http :10000 + echo "0" | http :10000 + echo "3" | http :10000 + echo "0" | http :10000 + echo "4" | http :10000 + +client-fib10-multi: + hey -z ${DURATION_SEC}s -cpus 4 -c 100 -t 0 -o csv -m GET -d "10\n" "http://${HOSTNAME}:10010" diff --git a/tests/traps/edf_nopreemption.env b/tests/traps/edf_nopreemption.env new file mode 100644 index 0000000..eeba531 --- /dev/null +++ b/tests/traps/edf_nopreemption.env @@ -0,0 +1,2 @@ +SLEDGE_SCHEDULER=EDF +SLEDGE_DISABLE_PREEMPTION=true diff --git a/tests/traps/edf_preemption.env b/tests/traps/edf_preemption.env new file mode 100644 index 0000000..302a324 --- /dev/null +++ b/tests/traps/edf_preemption.env @@ -0,0 +1,3 @@ +SLEDGE_SCHEDULER=EDF +SLEDGE_DISABLE_PREEMPTION=false +SLEDGE_SIGALRM_HANDLER=TRIAGED diff --git a/tests/traps/fifo_nopreemption.env b/tests/traps/fifo_nopreemption.env new file mode 100644 index 0000000..a572a70 --- /dev/null +++ b/tests/traps/fifo_nopreemption.env @@ -0,0 +1,2 @@ +SLEDGE_SCHEDULER=FIFO +SLEDGE_DISABLE_PREEMPTION=true diff --git a/tests/traps/fifo_preemption.env b/tests/traps/fifo_preemption.env new file mode 100644 index 0000000..eb1298f --- /dev/null +++ b/tests/traps/fifo_preemption.env @@ -0,0 +1,2 @@ +SLEDGE_SCHEDULER=FIFO +SLEDGE_DISABLE_PREEMPTION=false diff --git a/tests/traps/run.sh b/tests/traps/run.sh new file mode 100755 index 0000000..fd3f9ef --- /dev/null +++ b/tests/traps/run.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +__run_sh__base_path="$(dirname "$(realpath --logical "${BASH_SOURCE[0]}")")" +__run_sh__bash_libraries_relative_path="../bash_libraries" +__run_sh__bash_libraries_absolute_path=$(cd "$__run_sh__base_path" && cd "$__run_sh__bash_libraries_relative_path" && pwd) +export PATH="$__run_sh__bash_libraries_absolute_path:$PATH" + +source framework.sh || exit 1 +source validate_dependencies.sh || exit 1 + +experiment_client() { + local -r hostname="$1" + + for ((i = 1; i <= 10; i++)); do + echo "$i" | http -p h "${hostname}:10000" | grep 200 || { + echo "FAIL" + return 1 + } + echo "0" | http -p h "${hostname}:10000" | grep 500 || { + echo "FAIL" + return 1 + } + done + + echo "SUCCESS" + return 0 + +} + +validate_dependencies http + +framework_init "$@" diff --git a/tests/traps/spec.json b/tests/traps/spec.json new file mode 100644 index 0000000..19262ce --- /dev/null +++ b/tests/traps/spec.json @@ -0,0 +1,13 @@ +[ + { + "name": "fibonacci_40", + "path": "trap_divzero.wasm.so", + "port": 10000, + "expected-execution-us": 10000000, + "admissions-percentile": 70, + "relative-deadline-us": 20000000, + "http-req-size": 1024, + "http-resp-size": 1024, + "http-resp-content-type": "text/plain" + } +]