diff --git a/.gitignore b/.gitignore index c6127b3..a193344 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,7 @@ modules.order Module.symvers Mkfile.old dkms.conf + +runtime/tags +runtime/bin +runtime/tests/tmp/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..055e5f7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "silverfish"] + path = silverfish + url = https://github.com/gwsystems/silverfish.git + ignore = dirty +[submodule "runtime/jsmn"] + path = runtime/jsmn + url = https://github.com/zserge/jsmn.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bf42090 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,74 @@ +# Inspired by lucet's Dockerfile. + +# using ubuntu 18 docker image +FROM ubuntu:bionic + +# install some basic packages +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + git \ + python3-dev \ + python3-pip \ + cmake \ + ca-certificates \ + libssl-dev \ + pkg-config \ + gcc \ + g++ \ + clang-6.0 \ + clang-tools-6.0 \ + llvm-6.0 \ + llvm-6.0-dev \ + libc++-dev \ + libc++abi-dev \ + lld-6.0 \ + lldb-6.0 \ + libclang-6.0-dev \ + libclang-common-6.0-dev \ + vim \ + apache2 \ + subversion \ + libapache2-mod-svn \ + libsvn-dev \ + binutils-dev \ + build-essential \ + automake \ + libtool \ + strace \ + less \ + libuv1-dev \ + && rm -rf /var/lib/apt/lists/* + +# Enable apache2 for svn +RUN a2enmod dav +RUN a2enmod dav_svn +RUN service apache2 restart +RUN pip3 install numpy + +# set to use our installed clang version +RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-6.0 100 +RUN update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-6.0 100 + +# set LD_LIBRARY_PATH +ENV LD_LIBRARY_PATH=/usr/local/lib + +# install rust - commands copied straight from lucet's dockerfile. +# so we have exactly the same rust version as lucet! +RUN curl -sS -L -O https://static.rust-lang.org/dist/rust-1.35.0-x86_64-unknown-linux-gnu.tar.gz \ + && tar xzf rust-1.35.0-x86_64-unknown-linux-gnu.tar.gz \ + && cd rust-1.35.0-x86_64-unknown-linux-gnu \ + && ./install.sh \ + && cd .. \ + && rm -rf rust-1.35.0-x86_64-unknown-linux-gnu rust-1.35.0-x86_64-unknown-linux-gnu.tar.gz +ENV PATH=/usr/local/bin:$PATH +RUN cargo install --root /usr/local cargo-audit cargo-watch + +## copied again from lucet for when we want to use wasi-sdk +#RUN curl -sS -L -O https://github.com/CraneStation/wasi-sdk/releases/download/wasi-sdk-5/wasi-sdk_5.0_amd64.deb \ +# && dpkg -i wasi-sdk_5.0_amd64.deb && rm -f wasi-sdk_5.0_amd64.deb +# +#ENV WASI_SDK=/opt/wasi-sdk +ENV PATH=/opt/awsm/bin:$PATH + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2d5ad3a --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +SFCC='silverfish' +ROOT=${ROOT:-$(cd "$(dirname ${BASH_SOURCE:-$0})" && pwd)} + +.PHONY: build +build: + @echo "Building wasmception toolchain, takes a while." + @cd ${SFCC} && make -C wasmception && cd ${ROOT} + @echo "Building silverfish compiler (release)" + @cd ${SFCC} && cargo build --release && cd ${ROOT} + +.PHONY: build-dev +build-dev: + @echo "Building wasmception toolchain, takes a while." + @cd ${SFCC} && make -C wasmception && cd ${ROOT} + @echo "Building silverfish compiler (default==debug)" + @cd ${SFCC} && cargo build && cd ${ROOT} + +.PHONY: clean +clean: + @echo "Cleaning silverfish compiler" + @cd ${SFCC} && cargo clean && cd ${ROOT} + +# wasmception is too slow to recompile, +# so lets not make that part of the "silverfish" cleanup +.PHONY: wclean +wclean: + @echo "Cleaning wasmception toolchain" + @cd ${SFCC} && make -C wasmception clean && cd ${ROOT} + +.PHONY: runtime +runtime: + @echo "Building runtime!" + make -C runtime + +.PHONY: install +install: build + @./install.sh wasmception + diff --git a/devenv.sh b/devenv.sh new file mode 100755 index 0000000..891e604 --- /dev/null +++ b/devenv.sh @@ -0,0 +1,144 @@ +#!/bin/sh + +# +# This environment file and dockerfile are +# inspired by lucet's devenv_xxx.sh scripts +# +HOST_ROOT=${HOST_ROOT:-$(cd "$(dirname ${BASH_SOURCE:-$0})" && pwd)} +SYS_NAME='awsm' +HOST_SYS_MOUNT=${HOST_SYS_MOUNT:-"/${SYS_NAME}"} +SYS_WASMCEPTION='silverfish/wasmception' +SYS_DOC_NAME=${SYS_NAME} +SYS_DOC_DEVNAME=${SYS_DOC_NAME}'-dev' +SYS_DOC_TAG='latest' +SYS_DOC_NAMETAG=${SYS_DOC_NAME}:${SYS_DOC_TAG} +SYS_DOC_DEVNAMETAG=${SYS_DOC_DEVNAME}:${SYS_DOC_TAG} +SYS_BUILD_TIMEOUT=10 + +usage() +{ + echo "usage $0 " +} + +countdown() +{ + tmp_cnt=$1 + while [ ${tmp_cnt} -gt 0 ]; do + printf "${tmp_cnt}." +# sleep 1 + tmp_cnt=$((tmp_cnt - 1)) + done + echo +} + +envsetup() +{ + if docker image inspect ${SYS_DOC_NAMETAG} > /dev/null; then + echo "${SYS_DOC_NAMETAG} image exists, remove it first!" + exit 1 + fi + + echo "Setting up ${SYS_NAME}" + git submodule update --init --recursive 2>/dev/null ||: + + # Perhaps change in the wasmception (forked) repo, + # Gregor already forked every mainline repo to modify something or the other! + # + # That said, you may comment this if you're not behind a firewall! + # http:// doesn't work for me at my current work place. + echo "Changing http:// to https:// in ${SYS_WASMCEPTION}" + sed -i 's/http:\/\//https:\/\//' ${SYS_WASMCEPTION}/Makefile + + if docker image inspect ${SYS_DOC_DEVNAMETAG} >> /dev/null; then + echo "${SYS_DOC_DEVNAME} image exists, rebuilding it" + echo "(you have ${SYS_BUILD_TIMEOUT}secs to stop the rebuild)" + countdown ${SYS_BUILD_TIMEOUT} + fi + + echo "Building ${SYS_DOC_DEVNAMETAG}" + docker build -t ${SYS_DOC_DEVNAMETAG} . + + echo "Creating ${SYS_DOC_NAMETAG} on top of ${SYS_DOC_DEVNAMETAG}" + docker run --privileged --name=${SYS_DOC_DEVNAME} --detach --mount type=bind,src="$(cd $(dirname ${0}); pwd -P),target=/${SYS_NAME}" \ + ${SYS_DOC_DEVNAMETAG} /bin/sleep 99999999 > /dev/null + + echo "Building ${SYS_NAME}" + docker exec -t -w ${HOST_SYS_MOUNT} ${SYS_DOC_DEVNAME} make install + + echo "Tagging the new image" + docker container commit ${SYS_DOC_DEVNAME} ${SYS_DOC_NAMETAG} + + echo "Cleaning up ${SYS_DOC_DEVNAME}" + docker kill ${SYS_DOC_DEVNAME} + docker rm ${SYS_DOC_DEVNAME} + + echo "Done!" +} + +envrun() +{ + if ! docker image inspect ${SYS_DOC_NAMETAG} > /dev/null; then + envbuild + fi + + if docker ps -f name=${SYS_DOC_NAME} --format '{{.Names}}' | grep -q "^${SYS_DOC_NAME}" ; then + echo "Container is running" >&2 + else + echo "Starting ${SYS_DOC_NAME}" + docker run --privileged --security-opt seccomp:unconfined --name=${SYS_DOC_NAME} --detach --mount type=bind,src="$(cd $(dirname ${0}); pwd -P),target=/${SYS_NAME}" \ + ${SYS_DOC_NAMETAG} /bin/sleep 99999999 > /dev/null + fi + + echo "Running shell" + docker exec -t -i -w "${HOST_SYS_MOUNT}" ${SYS_DOC_NAME} /bin/bash +} + +envstop() +{ + echo "Stopping container" + docker stop ${SYS_DOC_NAME} + echo "Removing container" + docker rm ${SYS_DOC_NAME} +} + +envrm() +{ + envstop + docker rmi ${SYS_DOC_NAME} +} + +envrma() +{ + envrm + docker rmi ${SYS_DOC_DEVNAME} +} + +if [ $# -ne 1 ]; then + usage $0 + exit 1 +fi + +case $1 in + run) + envrun + ;; + stop) + envstop + ;; + setup) + envsetup + ;; + rm) + envrm + ;; + rma) + envrma + ;; + *) + echo "invalid option: $1" + usage $0 + exit 1 + ;; +esac +echo +echo "done!" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..48f3b2e --- /dev/null +++ b/install.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +# Inspired by Lucet's install.sh + +echo "Setting up toolchain environment" +SYS_SRC_PREFIX=${SYS_SRC_PREFIX:-"$( + cd $(dirname $(dirname ${0})) + pwd -P +)"} + +if [ ! -x "${SYS_SRC_PREFIX}/install.sh" ]; then + echo "Unable to find the install script" >&2 + exit 1 +fi + +SYS_NAME='awsm' +SILVERFISH='silverfish' +SYS_PREFIX=${SYS_PREFIX:-"/opt/${SYS_NAME}"} +SYS_SRC_PREFIX=${SYS_SRC_PREFIX:-"/${SYS_NAME}"} +SYS_SF_REL_DIR=${SYS_SF_REL_DIR:-"${SYS_SRC_PREFIX}/${SILVERFISH}/target/release"} +SYS_BIN_DIR=${SYS_BIN_DIR:-"${SYS_PREFIX}/bin"} +SYS_LIB_DIR=${SYS_LIB_DIR:-"${SYS_PREFIX}/lib"} + +#use wasmception +if [ $# -eq 0 ] || [ "$1" = "wasmception" ]; then +echo "Setting up for wasmception" +WASM_PREFIX=${WASM_PREFIX:-"${SYS_SRC_PREFIX}/${SILVERFISH}/wasmception"} +WASM_BIN=${WASM_BIN:-"${WASM_PREFIX}/dist/bin"} +WASM_SYSROOT=${WASM_SYSROOT:-"${WASM_PREFIX}/sysroot"} +WASM_TARGET=${WASM_TARGET:-"wasm32-unknown-unknown-wasm"} +WASM_BIN_PREFIX=${WASM_BIN_PREFIX:-"$WASM_TARGET"} +WASM_TOOLS=ar +elif [ "$1" = "wasi" ]; then +echo "Setting up for wasi-sdk" +#use wasi-sdk +WASM_PREFIX=${WASM_PREFIX:-${WASM_SDK:-"/opt/wasi-sdk"}} +WASM_BIN=${WASM_BIN:-"${WASM_PREFIX}/bin"} +WASM_SYSROOT=${WASM_SYSROOT:-"${WASM_PREFIX}/share/sysroot"} +WASM_TARGET=${WASM_TARGET:-"wasm32-wasi"} +WASM_BIN_PREFIX=${WASM_BIN_PREFIX:-"$WASM_TARGET"} +WASM_TOOLS=ar dwarfdump nm ranlib size +fi + +# silverfish compiler binary! +BINS=${SILVERFISH} +DEVSRC=${SYS_BIN_DIR}/'devenv_src.sh' + +rm -f ${SYS_BIN_DIR}/* +install -d -v "$SYS_BIN_DIR" || exit 1 +for bin in $BINS; do + ln -sfv "${SYS_SF_REL_DIR}/${bin}" "${SYS_BIN_DIR}/${bin}" +done + +for file in clang clang++; do + wrapper_file="$(mktemp)" + cat >"$wrapper_file" < +#include + +// TODO: Throughout here we use `assert` for error conditions, which isn't optimal +// Instead we should use `unlikely` branches to a single trapping function (which should optimize better) +// The below functions are for implementing WASM instructions + +// ROTL and ROTR helper functions +INLINE u32 +rotl_u32(u32 n, u32 c_u32) +{ + // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) + unsigned int c = c_u32 % (CHAR_BIT * sizeof(n)); + const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. + + c &= mask; + return (n << c) | (n >> ((-c) & mask)); +} + +INLINE u32 +rotr_u32(u32 n, u32 c_u32) +{ + // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) + unsigned int c = c_u32 % (CHAR_BIT * sizeof(n)); + const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); + + c &= mask; + return (n>>c) | (n << ((-c) & mask)); +} + +INLINE u64 +rotl_u64(u64 n, u64 c_u64) +{ + // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) + unsigned int c = c_u64 % (CHAR_BIT * sizeof(n)); + const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. + + c &= mask; + return (n << c) | (n >> ((-c) & mask)); +} + +INLINE u64 +rotr_u64(u64 n, u64 c_u64) +{ + // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) + unsigned int c = c_u64 % (CHAR_BIT * sizeof(n)); + const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); + + c &= mask; + return (n >> c) | (n << ((-c) & mask)); +} + +// Now safe division and remainder +INLINE u32 +u32_div(u32 a, u32 b) +{ + assert(b); + return a / b; +} + +INLINE u32 +u32_rem(u32 a, u32 b) +{ + assert(b); + return a % b; +} + +INLINE i32 +i32_div(i32 a, i32 b) +{ + assert(b && (a != INT32_MIN || b != -1)); + return a / b; +} + +INLINE i32 +i32_rem(i32 a, i32 b) +{ + assert(b && (a != INT32_MIN || b != -1)); + return a % b; +} + +INLINE u64 +u64_div(u64 a, u64 b) +{ + assert(b); + return a / b; +} + +INLINE u64 +u64_rem(u64 a, u64 b) +{ + assert(b); + return a % b; +} + +INLINE i64 +i64_div(i64 a, i64 b) +{ + assert(b && (a != INT64_MIN || b != -1)); + return a / b; +} + +INLINE i64 +i64_rem(i64 a, i64 b) +{ + assert(b && (a != INT64_MIN || b != -1)); + return a % b; +} + +// float to integer conversion methods +// In C, float => int conversions always truncate +// If a int2float(int::min_value) <= float <= int2float(int::max_value), it must always be safe to truncate it +u32 +u32_trunc_f32(float f) +{ + assert(0 <= f && f <= UINT32_MAX); + return (u32) f; +} + +i32 +i32_trunc_f32(float f) +{ + assert(INT32_MIN <= f && f <= INT32_MAX ); + return (i32) f; +} + +u32 +u32_trunc_f64(double f) +{ + assert(0 <= f && f <= UINT32_MAX); + return (u32) f; +} + +i32 +i32_trunc_f64(double f) +{ + assert(INT32_MIN <= f && f <= INT32_MAX ); + return (i32) f; +} + +u64 +u64_trunc_f32(float f) +{ + assert(0 <= f && f <= UINT64_MAX); + return (u64) f; +} + +i64 +i64_trunc_f32(float f) +{ + assert(INT64_MIN <= f && f <= INT64_MAX); + return (i64) f; +} + +u64 +u64_trunc_f64(double f) +{ + assert(0 <= f && f <= UINT64_MAX); + return (u64) f; +} + +i64 +i64_trunc_f64(double f) +{ + assert(INT64_MIN <= f && f <= INT64_MAX); + return (i64) f; +} + +// Float => Float truncation functions +INLINE float +f32_trunc_f32(float f) +{ + return trunc(f); +} + +INLINE float +f32_min(float a, float b) +{ + return a < b ? a : b; +} + +INLINE float +f32_max(float a, float b) +{ + return a > b ? a : b; +} + +INLINE double +f64_min(double a, double b) +{ + return a < b ? a : b; +} + +INLINE double +f64_max(double a, double b) +{ + return a > b ? a : b; +} diff --git a/runtime/compiletime/memory/64bit_nix.c b/runtime/compiletime/memory/64bit_nix.c new file mode 100644 index 0000000..b0eaaf1 --- /dev/null +++ b/runtime/compiletime/memory/64bit_nix.c @@ -0,0 +1,130 @@ +/* https://github.com/gwsystems/silverfish/blob/master/runtime/memory/64bit_nix.c */ +#include + +#ifdef USE_MEM_VM + +// All of these are pretty generic +INLINE float +get_f32(i32 offset) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + return *(float *)address; +} + +INLINE double +get_f64(i32 offset) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + return *(double *)address; +} + +INLINE i8 +get_i8(i32 offset) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + return *(i8 *)address; +} + +INLINE i16 +get_i16(i32 offset) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + return *(i16 *)address; +} + +INLINE i32 +get_i32(i32 offset) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + return *(i32 *)address; +} + +INLINE i64 +get_i64(i32 offset) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + return *(i64 *)address; +} + +// Now setting routines +INLINE void +set_f32(i32 offset, float v) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + *(float *)address = v; +} + +INLINE void +set_f64(i32 offset, double v) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + *(double *)address = v; +} + +INLINE void +set_i8(i32 offset, i8 v) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + *(i8 *)address = v; +} + +INLINE void +set_i16(i32 offset, i16 v) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + *(i16 *)address = v; +} + +INLINE void +set_i32(i32 offset, i32 v) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + *(i32 *)address = v; +} + +INLINE void +set_i64(i32 offset, i64 v) +{ + char *mem_as_chars = (char *)sandbox_lmbase; + void *address = &mem_as_chars[offset]; + + *(i64 *)address = v; +} + +// Table handling functionality +INLINE char * +get_function_from_table(u32 idx, u32 type_id) +{ + assert(idx < INDIRECT_TABLE_SIZE); + + struct indirect_table_entry f = module_indirect_table[idx]; + + assert(f.type_id == type_id && f.func_pointer); + + return f.func_pointer; +} +#else +#error "Incorrect memory module!" +#endif diff --git a/runtime/include/arch/aarch64/context.h b/runtime/include/arch/aarch64/context.h new file mode 100644 index 0000000..3994f31 --- /dev/null +++ b/runtime/include/arch/aarch64/context.h @@ -0,0 +1,37 @@ +#ifndef ARCH_AARCH64_CONTEXT_H +#define ARCH_AARCH64_CONTEXT_H + +#include +#include + +typedef uint64_t reg_t; +#define ARCH_NREGS 31 // TODO: aarch64 context-switch assembly and registers! + +/* + * This is the slowpath switch to a preempted sandbox! + * SIGUSR1 on the current thread and restore mcontext there! + */ +extern void __attribute__((noreturn)) sandbox_switch_preempt(void); + +struct arch_context { + reg_t regs[ARCH_NREGS]; + mcontext_t mctx; +}; + +typedef struct arch_context arch_context_t; +extern __thread arch_context_t base_context; + +static inline void +arch_context_init(arch_context_t *actx, reg_t ip, reg_t sp) +{ + memset(&actx->mctx, 0, sizeof(mcontext_t)); + memset((void *)actx->regs, 0, sizeof(reg_t) * ARCH_NREGS); +} + +static inline int +arch_context_switch(arch_context_t *ca, arch_context_t *na) +{ + return 0; +} + +#endif /* ARCH_AARCH64_CONTEXT_H */ diff --git a/runtime/include/arch/context.h b/runtime/include/arch/context.h new file mode 100644 index 0000000..e8ef2ec --- /dev/null +++ b/runtime/include/arch/context.h @@ -0,0 +1,14 @@ +#ifndef ARCH_CONTEXT_H +#define ARCH_CONTEXT_H + +#if defined(AARCH64) +#include "aarch64/context.h" +#elif defined(X86_64) +#include "x86_64/context.h" +#else +#warning "Architecture not set. Using x86_64" +#define X86_64 +#include "x86_64/context.h" +#endif + +#endif /* ARCH_CONTEXT_H */ diff --git a/runtime/include/arch/x86_64/context.h b/runtime/include/arch/x86_64/context.h new file mode 100644 index 0000000..73ff4ad --- /dev/null +++ b/runtime/include/arch/x86_64/context.h @@ -0,0 +1,155 @@ +#ifndef ARCH_X86_64_CONTEXT_H +#define ARCH_X86_64_CONTEXT_H + +#include +#include +#include + +/* + * Register strict ordering> + * rax = (regs + 0) = 0(%%reg) + * rbx = (regs + 1) = 8(%%reg) + * rcx = (regs + 2) = 16(%%reg) + * rdx = (regs + 3) = 24(%%reg) + * rbp = (regs + 4) = 32(%%reg) + * rsp = (regs + 5) = 40(%%reg) + * rsi = (regs + 6) = 48(%%reg) + * rdi = (regs + 7) = 56(%%reg) + * r8 = (regs + 8) = 64(%%reg) + * r9 = (regs + 9) = 72(%%reg) + * r10 = (regs + 10) = 80(%%reg) + * r11 = (regs + 11) = 88(%%reg) + * r12 = (regs + 12) = 96(%%reg) + * r13 = (regs + 13) = 104(%%reg) + * r14 = (regs + 14) = 112(%%reg) + * r15 = (regs + 15) = 120(%%reg) + * rip = (regs + 16) = 128(%%reg) + */ + +typedef uint64_t reg_t; +#define ARCH_NREGS (16 /* GP registers */ + 1 /* for IP */) + +/* + * This is the slowpath switch to a preempted sandbox! + * SIGUSR1 on the current thread and restore mcontext there! + */ +extern void __attribute__((noreturn)) sandbox_switch_preempt(void); + +struct arch_context { + reg_t regs[ARCH_NREGS]; + mcontext_t mctx; +}; + +typedef struct arch_context arch_context_t; +extern __thread arch_context_t base_context; + +static void +arch_mcontext_save(arch_context_t *ctx, mcontext_t *mc) +{ + assert(ctx != &base_context); + + ctx->regs[5] = 0; + memset(ctx->regs, 0, ARCH_NREGS * sizeof(reg_t)); + memcpy(&ctx->mctx, mc, sizeof(mcontext_t)); +} + +static void +arch_mcontext_restore(mcontext_t *mc, arch_context_t *ctx) +{ + assert(ctx != &base_context); + + // if ctx->regs[5] is set, this was last in a user-level context switch state! + // else restore mcontext.. + if (ctx->regs[5]) { + mc->gregs[REG_RSP] = ctx->regs[5]; + mc->gregs[REG_RIP] = ctx->regs[16]; + ctx->regs[5] = 0; + } else { + memcpy(mc, &ctx->mctx, sizeof(mcontext_t)); + memset(&ctx->mctx, 0, sizeof(mcontext_t)); + } +} + +static void __attribute__((noinline)) +arch_context_init(arch_context_t *actx, reg_t ip, reg_t sp) +{ + + memset(&actx->mctx, 0, sizeof(mcontext_t)); + memset((void *)actx->regs, 0, sizeof(reg_t) * ARCH_NREGS); + + if (sp) { + /* + * context_switch conventions: bp is expected to be on top of the stack + * when co-op context switching.. + * + * so push sp on this new stack and use + * that new sp as sp for switching to sandbox! + */ + asm volatile ( + "movq %%rsp, %%rbx\n\t" \ + "movq %%rax, %%rsp\n\t" \ + "pushq %%rax\n\t" \ + "movq %%rsp, %%rax\n\t" \ + "movq %%rbx, %%rsp\n\t" \ + : "=a" (sp) + : "a" (sp) + : "memory", "cc", "rbx" + ); + } + + *(actx->regs + 5) = sp; + *(actx->regs + 16) = ip; +} + +static inline int +arch_context_switch(arch_context_t *ca, arch_context_t *na) +{ + if (!ca) { + assert(na); + // switching from "no sandbox to execute" state to "executing a sandbox" + ca = &base_context; + } else if (!na) { + assert(ca); + + // switching from "executing a sandbox" to "no execution" state. + na = &base_context; + } else { + assert(na && ca); + + // switching between sandboxes. + } + + reg_t *cr = ca->regs, *nr = na->regs; + assert(cr && nr); + + /* TODO: slowpath: signal the current pthread if resuming a preempted sandbox! */ + + asm volatile ( \ + "pushq %%rbp\n\t" \ + "movq %%rsp, %%rbp\n\t" \ + "movq $2f, 128(%%rax)\n\t" \ + "movq %%rsp, 40(%%rax)\n\t" \ + "cmpq $0, 40(%%rbx)\n\t" \ + "je 1f\n\t" \ + "movq 40(%%rbx), %%rsp\n\t" \ + "jmpq *128(%%rbx)\n\t" \ + "1:\n\t" \ + "call sandbox_switch_preempt\n\t" \ + ".align 8\n\t" \ + "2:\n\t" \ + "movq $0, 40(%%rbx)\n\t" \ + ".align 8\n\t" \ + "3:\n\t" \ + "popq %%rbp\n\t" \ + : + : "a" (cr), "b" (nr) + : "memory", "cc", "rcx", "rdx", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" + ); + + return 0; +} + +#endif /* ARCH_X86_64_CONTEXT_H */ diff --git a/runtime/include/module.h b/runtime/include/module.h new file mode 100644 index 0000000..9e46651 --- /dev/null +++ b/runtime/include/module.h @@ -0,0 +1,91 @@ +#ifndef SFRT_MODULE_H +#define SFRT_MODULE_H + +#include +#include "types.h" + +struct module { + char name[MOD_NAME_MAX]; //not sure if i care for now. + char path[MOD_PATH_MAX]; //to dlopen if it has not been opened already. + + void *dl_handle; + + mod_main_fn_t entry_fn; + mod_glb_fn_t glb_init_fn; + mod_mem_fn_t mem_init_fn; + mod_tbl_fn_t tbl_init_fn; + + i32 nargs; //as per the specification somewhere. + /* i32 nrets; */ + + u32 stack_size; // a specification? + u64 max_memory; //perhaps a specification of the module. + u32 timeout; //again part of the module specification. + + u32 refcnt; //ref count how many instances exist here. + + u32 udpport; + uv_udp_t udpsrv; // udp server to listen to requests. + struct sockaddr_in srvaddr; + + // FIXME: for now, per-sandbox. no reason to have it be per-sandbox. + struct indirect_table_entry indirect_table[INDIRECT_TABLE_SIZE]; + // TODO: what else? +}; + +// a runtime resource, perhaps use "malloc" on this? +struct module *module_alloc(char *mod_name, char *mod_path, u32 udp_port, i32 nargs, i32 nrets, u32 stack_sz, u32 max_heap, u32 timeout/*, ...*/); +// frees only if refcnt == 0 +void module_free(struct module *mod); +struct module *module_find(char *name); + +static inline int +module_is_valid(struct module *mod) +{ + if (mod && mod->dl_handle && mod->entry_fn) return 1; + + return 0; +} + +static inline void +module_table_init(struct module *mod) +{ + // called in a sandbox. + mod->tbl_init_fn(); +} + +static inline void +module_memory_init(struct module *mod) +{ + // called in a sandbox. + mod->mem_init_fn(); +} + +static inline i32 +module_entry(struct module *mod, i32 argc, i32 argv) +{ + return mod->entry_fn(argc, argv); +} + +// instantiate this module. +static inline void +module_acquire(struct module *mod) +{ + // FIXME: atomic. + mod->refcnt++; +} + +static inline void +module_release(struct module *mod) +{ + // FIXME: atomic. + mod->refcnt--; +} + +static inline i32 +module_nargs(struct module *mod) +{ + return mod->nargs; +} + +#endif /* SFRT_MODULE_H */ diff --git a/runtime/include/ps_list.h b/runtime/include/ps_list.h new file mode 100644 index 0000000..8357782 --- /dev/null +++ b/runtime/include/ps_list.h @@ -0,0 +1,189 @@ +/*** + * Copyright 2009-2017 by Gabriel Parmer. All rights reserved. + * Redistribution of this file is permitted under the BSD 2 clause license. + * Author: Gabriel Parmer, gparmer@gwu.edu, 2017 + * + * History: + * - Initial implementation, ~2009 + * - Adapted for parsec and relicensed, 2016 + */ + +/* + * API Conventions: + * - obj, new, and tmp are pointers of type T where T is the struct containing the linked list + * - head is a pointer to struct ps_list_head + * - l is the list field name within T + * - type is T without any () + * - all ps_list_head_* functions should be applied to struct ps_list_head pointers + * - all ps_list_* functions should be passed items of type T, not struct ps_list_head + * - as with most macro-based APIs, please avoid passing in functions that cannot be multiply evaluated; + * generally passing only variables is a good move + * + * Example Usage: + * + * struct ps_list_head h; + * struct foo { + * struct ps_list l; + * void *d; + * } node, *i, *tmp; + * + * ps_list_head_init(&h); + * ps_list_init(&node); + * ps_list_head_add(&h, &node, l); + * ... + * for (i = ps_list_head_first(&h, struct foo, l) ; + * i != ps_list_head(&h, struct foo, l) ; + * i = ps_list_next(i, l)) { ... } + * + * for (ps_list_iter_init(&h, i, l) ; !ps_list_iter_term(&h, i, l) ; i = ps_list_next(i, l)) { ... } + * + * ps_list_foreach(&h, i, l) { ... } + * + * ps_list_foreach_del(&h, i, tmp, l) { + * ps_list_rem(i, l); + * ps_free(i); + * } + * + */ + +#ifndef PS_LIST_H +#define PS_LIST_H + +struct ps_list { + struct ps_list *n, *p; +}; + +/* + * This is a separate type to 1) provide guidance on how to use the + * API, and 2) to prevent developers from comparing pointers that + * should not be compared. + */ +struct ps_list_head { + struct ps_list l; +}; + +#define PS_LIST_DEF_NAME list + +static inline void +ps_list_ll_init(struct ps_list *l) +{ l->n = l->p = l; } + +static inline void +ps_list_head_init(struct ps_list_head *lh) +{ ps_list_ll_init(&lh->l); } + +static inline int +ps_list_ll_empty(struct ps_list *l) +{ return l->n == l; } + +static inline int +ps_list_head_empty(struct ps_list_head *lh) +{ return ps_list_ll_empty(&lh->l); } + +static inline void +ps_list_ll_add(struct ps_list *l, struct ps_list *new) +{ + new->n = l->n; + new->p = l; + l->n = new; + new->n->p = new; +} + +static inline void +ps_list_ll_rem(struct ps_list *l) +{ + l->n->p = l->p; + l->p->n = l->n; + l->p = l->n = l; +} + +#define ps_offsetof(s, field) __builtin_offsetof(s, field) +//#define ps_offsetof(s, field) ((unsigned long)&(((s *)0)->field)) + +#define ps_container(intern, type, field) \ + ((type *)((char *)(intern) - ps_offsetof(type, field))) + +/* + * Get a pointer to the object containing *l, of a type shared with + * *o. Importantly, "o" is not accessed here, and is _only_ used for + * its type. It will typically be the iterator/cursor working through + * a list. Do _not_ use this function. It is a utility used by the + * following functions. + */ +#define ps_list_obj_get(l, o, lname) \ + ps_container(l, __typeof__(*(o)), lname) + +//(typeof (*(o)) *)(((char*)(l)) - ps_offsetof(typeof(*(o)), lname)) + +/*** + * The object API. These functions are called with pointers to your + * own (typed) structures. + */ + +#define ps_list_is_head(lh, o, lname) (ps_list_obj_get((lh), (o), lname) == (o)) + +/* functions for if we don't use the default name for the list field */ +#define ps_list_singleton(o, lname) ps_list_ll_empty(&(o)->lname) +#define ps_list_init(o, lname) ps_list_ll_init(&(o)->lname) +#define ps_list_next(o, lname) ps_list_obj_get((o)->lname.n, (o), lname) +#define ps_list_prev(o, lname) ps_list_obj_get((o)->lname.p, (o), lname) +#define ps_list_add(o, n, lname) ps_list_ll_add(&(o)->lname, &(n)->lname) +#define ps_list_append(o, n, lname) ps_list_add(ps_list_prev((o), lname), n, lname) +#define ps_list_rem(o, lname) ps_list_ll_rem(&(o)->lname) +#define ps_list_head_add(lh, o, lname) ps_list_ll_add((&(lh)->l), &(o)->lname) +#define ps_list_head_append(lh, o, lname) ps_list_ll_add(((&(lh)->l)->p), &(o)->lname) + +/** + * Explicit type API: Pass in the types of the nodes in the list, and + * the name of the ps_list field in that type. + */ + +#define ps_list_head_first(lh, type, lname) \ + ps_container(((lh)->l.n), type, lname) +#define ps_list_head_last(lh, type, lname) \ + ps_container(((lh)->l.p), type, lname) + +/* If your struct named the list field "list" (as defined by PS_LIST_DEF_NAME */ +#define ps_list_is_head_d(lh, o) ps_list_is_head(lh, o, PS_LIST_DEF_NAME) +#define ps_list_singleton_d(o) ps_list_singleton(o, PS_LIST_DEF_NAME) +#define ps_list_init_d(o) ps_list_init(o, PS_LIST_DEF_NAME) +#define ps_list_next_d(o) ps_list_next(o, PS_LIST_DEF_NAME) +#define ps_list_prev_d(o) ps_list_prev(o, PS_LIST_DEF_NAME) +#define ps_list_add_d(o, n) ps_list_add(o, n, PS_LIST_DEF_NAME) +#define ps_list_append_d(o, n) ps_list_append(o, n, PS_LIST_DEF_NAME) +#define ps_list_rem_d(o) ps_list_rem(o, PS_LIST_DEF_NAME) + +#define ps_list_head_last_d(lh, o) ps_list_head_last(lh, o, PS_LIST_DEF_NAME) +#define ps_list_head_first_d(lh, type) ps_list_head_first(lh, type, PS_LIST_DEF_NAME) +#define ps_list_head_add_d(lh, o) ps_list_head_add(lh, o, PS_LIST_DEF_NAME) +#define ps_list_head_append_d(lh, o) ps_list_head_append(lh, o, PS_LIST_DEF_NAME) + +/** + * Iteration API + */ + +/* Iteration without mutating the list */ +#define ps_list_foreach(head, iter, lname) \ + for (iter = ps_list_head_first((head), __typeof__(*iter), lname) ; \ + !ps_list_is_head((head), iter, lname) ; \ + (iter) = ps_list_next(iter, lname)) + +#define ps_list_foreach_d(head, iter) ps_list_foreach(head, iter, PS_LIST_DEF_NAME) + +/* + * Iteration where the current node can be ps_list_rem'ed. + * Notes: + * - typeof(iter) == typeof(tmp) + * - ps_list_add can be used on iter, but the added node will not be iterated over + * + * TODO: Add SMR/parallel version of this macro + */ +#define ps_list_foreach_del(head, iter, tmp, lname) \ + for (iter = ps_list_head_first((head), __typeof__(*iter), lname), \ + (tmp) = ps_list_next((iter), lname) ; \ + !ps_list_is_head((head), iter, lname) ; \ + (iter) = (tmp), (tmp) = ps_list_next((tmp), lname)) + +#define ps_list_foreach_del_d(head, iter, tmp) ps_list_foreach_del(head, iter, tmp, PS_LIST_DEF_NAME) + +#endif /* PS_LIST_H */ diff --git a/runtime/include/runtime.h b/runtime/include/runtime.h new file mode 100644 index 0000000..1f8308a --- /dev/null +++ b/runtime/include/runtime.h @@ -0,0 +1,55 @@ +#ifndef SFRT_RUNTIME_H +#define SFRT_RUNTIME_H +#include "types.h" + +#include +#include "sandbox.h" + +void alloc_linear_memory(void); +void expand_memory(void); +void free_linear_memory(void *base, u32 bound, u32 max); +// Assumption: bounds_check < WASM_PAGE_SIZE +INLINE char *get_memory_ptr_for_runtime(u32 offset, u32 bounds_check); + +static inline void * +get_memory_ptr_void(u32 offset, u32 bounds_check) +{ + return (void*) get_memory_ptr_for_runtime(offset, bounds_check); +} + +static inline char * +get_memory_string(u32 offset) +{ + char *naive_ptr = get_memory_ptr_for_runtime(offset, 1); + int i = 0; + + while (1) { + // Keep bounds checking the waters over and over until we know it's safe (we find a terminating character) + char ith_element = get_memory_ptr_for_runtime(offset, i + 1)[i]; + + if (ith_element == '\0') return naive_ptr; + i++; + } +} + +INLINE char *get_function_from_table(u32 idx, u32 type_id); + +// libc/* might need to do some setup for the libc setup +void stub_init(char *modulename, i32 offset, mod_init_libc_fn_t fn); + +void runtime_init(void); + +static inline void +runtime_on_alloc(uv_handle_t *h, size_t suggested, uv_buf_t *buf) +{ + buf->base = malloc(suggested); + memset(buf->base, 0, suggested); + buf->len = suggested; +} + +extern __thread uv_loop_t uvio; +static inline uv_loop_t * +runtime_uvio(void) +{ return &uvio; } + +#endif /* SFRT_RUNTIME_H */ diff --git a/runtime/include/sandbox.h b/runtime/include/sandbox.h new file mode 100644 index 0000000..382c810 --- /dev/null +++ b/runtime/include/sandbox.h @@ -0,0 +1,211 @@ +#ifndef SFRT_SANDBOX_H +#define SFRT_SANDBOX_H + +#include "ps_list.h" +#include "module.h" +#include "arch/context.h" +#include "softint.h" +#include +#include + +struct io_handle { + int fd; + struct stat s_cache; + union uv_any_handle uvh; +}; + +typedef enum { + SANDBOX_FREE, + SANDBOX_RUNNABLE, + SANDBOX_BLOCKED, + SANDBOX_WOKEN, //for race in block()/wakeup() + SANDBOX_RETURNED, //waiting for parent to read status? +} sandbox_state_t; + +/* + * This is the slowpath switch to a preempted sandbox! + * SIGUSR1 on the current thread and restore mcontext there! + */ +extern void __attribute__((noreturn)) sandbox_switch_preempt(void); + +struct sandbox { + sandbox_state_t state; + + void *linear_start; + u32 linear_size; + u32 linear_max_size; + + void *stack_start; // guess we need a mechanism for stack allocation. + u32 stack_size; // and to set the size of it. + + arch_context_t ctxt; //register context for context switch. + + // TODO: are all these necessary? + u64 actual_deadline; + u64 expected_deadline; + u64 total_time; + u64 remaining_time; + u64 start_time; + + struct module *mod; //which module is this an instance of? + //struct indirect_table_entry indirect_table[INDIRECT_TABLE_SIZE]; + + i32 args_offset; //actual placement of args in the sandbox. + /* i32 ret_offset; //placement of return value(s) in the sandbox. */ + void *args; // args from request, must be of module->nargs size. + i32 retval; + + struct io_handle handles[SBOX_MAX_OPEN]; + + char *read_buf; + ssize_t read_len, read_size; + + struct ps_list list; + + // track I/O handles? +}; + +// a runtime resource, malloc on this! +struct sandbox *sandbox_alloc(struct module *mod, char *args); +// should free stack and heap resources.. also any I/O handles. +void sandbox_free(struct sandbox *sbox); + +// next_sandbox only used in SIGUSR1 +extern __thread struct sandbox *current_sandbox; +extern __thread arch_context_t *next_context; + +typedef struct sandbox sandbox_t; + +static inline struct sandbox * +sandbox_current(void) +{ return current_sandbox; } + +static inline void +sandbox_current_set(struct sandbox *sbox) +{ + int dis = 0; + + if (softint_enabled()) { + dis = 1; + softint_disable(); + } + + // FIXME: critical-section. + current_sandbox = sbox; + if (sbox == NULL) return; + + sandbox_lmbase = sbox->linear_start; + sandbox_lmbound = sbox->linear_size; + // TODO: module table or sandbox table? + module_indirect_table = sbox->mod->indirect_table; + + if (dis) softint_enable(); +} + +static inline struct module * +sandbox_module(struct sandbox *s) +{ + if (!s) return NULL; + + return s->mod; +} + +static inline void +sandbox_switch(struct sandbox *next) +{ + arch_context_t *n = next == NULL ? NULL : &next->ctxt; + + // disable interrupts (signals) + softint_disable(); + + // switch sandbox (register context & base/bound/table) + struct sandbox *curr = sandbox_current(); + arch_context_t *c = curr == NULL ? NULL : &curr->ctxt; + sandbox_current_set(next); + // save current's registers and restore next's registers. + next_context = n; + arch_context_switch(c, n); + next_context = NULL; + + // enable interrupts (signals) + softint_enable(); +} + +static inline char * +sandbox_args(void) +{ + struct sandbox *c = sandbox_current(); + + return (char *)c->args; +} + +void sandbox_run(struct sandbox *s); +void *sandbox_run_func(void *data); +struct sandbox *sandbox_schedule(void); +void sandbox_block(void); +void sandbox_wakeup(sandbox_t *sb); + +// should be the entry-point for each sandbox so it can do per-sandbox mem/etc init. +// should have been called with stack allocated and sandbox_current() set! +void sandbox_entry(void); +void sandbox_exit(void); + +static inline int +io_handle_preopen(void) +{ + struct sandbox *s = sandbox_current(); + int i; + for (i = 0; i < SBOX_MAX_OPEN; i++) { + if (s->handles[i].fd < 0) break; + } + if (i == SBOX_MAX_OPEN) return -1; + s->handles[i].fd = SBOX_PREOPEN_MAGIC; + memset(&s->handles[i].uvh, 0, sizeof(union uv_any_handle)); + return i; +} + +static inline int +io_handle_open(int fd) +{ + struct sandbox *s = sandbox_current(); + if (fd < 0) return fd; + int i = io_handle_preopen(); + s->handles[i].fd = fd; //well, per sandbox.. so synchronization necessary! + return i; +} + +static inline int +io_handle_preopen_set(int idx, int fd) +{ + struct sandbox *s = sandbox_current(); + if (idx >= SBOX_MAX_OPEN || idx < 0) return -1; + if (fd < 0 || s->handles[idx].fd != SBOX_PREOPEN_MAGIC) return -1; + s->handles[idx].fd = fd; + return idx; +} + +static inline int +io_handle_fd(int idx) +{ + struct sandbox *s = sandbox_current(); + if (idx >= SBOX_MAX_OPEN || idx < 0) return -1; + return s->handles[idx].fd; +} + +static inline void +io_handle_close(int idx) +{ + struct sandbox *s = sandbox_current(); + if (idx >= SBOX_MAX_OPEN || idx < 0) return; + s->handles[idx].fd = -1; +} + +static inline union uv_any_handle * +io_handle_uv_get(int idx) +{ + struct sandbox *s = sandbox_current(); + if (idx >= SBOX_MAX_OPEN || idx < 0) return NULL; + return &s->handles[idx].uvh; +} + +#endif /* SFRT_SANDBOX_H */ diff --git a/runtime/include/softint.h b/runtime/include/softint.h new file mode 100644 index 0000000..664e78b --- /dev/null +++ b/runtime/include/softint.h @@ -0,0 +1,73 @@ +#ifndef SFRT_SOFTINT_H +#define SFRT_SOFTINT_H + +#include +#include +#include + +static inline int +softint_mask(int sig) +{ + sigset_t set; + int ret; + + assert(sig == SIGALRM || sig == SIGUSR1); + /* all threads created by the calling thread will have sig blocked */ + sigemptyset(&set); + sigaddset(&set, sig); + ret = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (ret != 0) { + errno = ret; + perror("pthread_sigmask"); + exit(-1); + } + + return 0; +} + +static inline int +softint_unmask(int sig) +{ + sigset_t set; + int ret; + + assert(sig == SIGALRM || sig == SIGUSR1); + /* all threads created by the calling thread will have sig unblocked */ + sigemptyset(&set); + sigaddset(&set, sig); + ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL); + if (ret != 0) { + errno = ret; + perror("pthread_sigmask"); + exit(-1); + } + + return 0; +} + +extern __thread volatile sig_atomic_t softint_off; + +static inline void +softint_disable(void) +{ + while (__sync_bool_compare_and_swap(&softint_off, 0, 1) == false) ; +} + +static inline void +softint_enable(void) +{ + if (__sync_bool_compare_and_swap(&softint_off, 1, 0) == false) assert(0); +} + +static inline int +softint_enabled(void) +{ + return (softint_off == 0); +} + +void softint_init(void); + +void softint_timer_arm(void); +void softint_timer_disarm(void); + +#endif /* SFRT_SOFTINT_H */ diff --git a/runtime/include/types.h b/runtime/include/types.h new file mode 100644 index 0000000..f80f69b --- /dev/null +++ b/runtime/include/types.h @@ -0,0 +1,129 @@ +#ifndef SFRT_TYPES_H +#define SFRT_TYPES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define EXPORT __attribute__ ((visibility ("default"))) +#define IMPORT __attribute__ ((visibility ("default"))) + +#define INLINE __attribute__((always_inline)) +#define WEAK __attribute__((weak)) + +// Type alias's so I don't have to write uint32_t a million times +typedef signed char i8; +typedef unsigned char u8; +typedef int16_t i16; +typedef uint16_t u16; +typedef int32_t i32; +typedef uint32_t u32; +typedef int64_t i64; +typedef uint64_t u64; + +// FIXME: per-module configuration? +#define WASM_PAGE_SIZE (1024 * 64) //64KB +#define WASM_START_PAGES (1<<8) //16MB +#define WASM_MAX_PAGES (1<<15) //4GB + +#define WASM_STACK_SIZE (1<<15) // FIXME: fixed size for now. + +// These are per module symbols and I'd need to dlsym for each module. instead just use global constants, see above macros. +// The code generator compiles in the starting number of wasm pages, and the maximum number of pages +// If we try and allocate more than max_pages, we should fault +//extern u32 starting_pages; +//extern u32 max_pages; + +// The code generator also compiles in stubs that populate the linear memory and function table +void populate_memory(void); +void populate_table(void); + +// memory/* also provides the table access functions +// TODO: Change this to use a compiled in size +#define INDIRECT_TABLE_SIZE 1024 + +struct indirect_table_entry { + u32 type_id; + void *func_pointer; +}; + +extern __thread struct indirect_table_entry *module_indirect_table; + +// for sandbox linear memory isolation +extern __thread void *sandbox_lmbase; +extern __thread u32 sandbox_lmbound; +extern i32 logfd; + +// functions in the module to lookup and call per sandbox. +typedef i32 (*mod_main_fn_t)(i32 a, i32 b); +typedef void (*mod_glb_fn_t)(void); +typedef void (*mod_mem_fn_t)(void); +typedef void (*mod_tbl_fn_t)(void); +typedef void (*mod_init_libc_fn_t)(i32, i32); + +typedef enum { + MOD_ARG_MODPATH = 0, + MOD_ARG_MODPORT, + MOD_ARG_MODNAME, + MOD_ARG_MODNARGS, + MOD_ARG_MAX, +} mod_argindex_t; + +#define MOD_MAIN_FN "wasmf_main" +#define MOD_GLB_FN "populate_globals" +#define MOD_MEM_FN "populate_memory" +#define MOD_TBL_FN "populate_table" +#define MOD_INIT_LIBC_FN "wasmf___init_libc" + +#define MOD_MAX_ARGS 16 +#define MOD_ARG_MAX_SZ 64 +#define MOD_MAX 1024 + +#define MOD_NAME_MAX 32 +#define MOD_PATH_MAX 256 + +#define JSON_ELE_MAX 16 + +// FIXME: some naive work-stealing here.. +#define SBOX_PULL_MAX 16 + +#define SBOX_MAX_OPEN 32 +#define SBOX_PREOPEN_MAGIC (707707707) // reads lol lol lol upside down + +#define SOFTINT_TIMER_START_USEC (10*1000) //start timers 10 us from now. +#define SOFTINT_TIMER_PERIOD_USEC (1000*100) // 100ms timer.. + +#ifdef DEBUG +#ifdef NOSTDIO +#define debuglog(fmt,...) dprintf(logfd, "(%d,%lu) %s: " fmt, sched_getcpu(), pthread_self(), __func__, ## __VA_ARGS__) +#else +#define debuglog(fmt,...) printf("(%d,%lu) %s: " fmt, sched_getcpu(), pthread_self(), __func__, ## __VA_ARGS__) +#endif +#else +#define debuglog(fmt,...) +#endif + +#define GLB_STDOUT "/dev/null" +#define GLB_STDERR "/dev/null" +#define GLB_STDIN "/dev/zero" + +#define LOGFILE "awesome.log" + +#define RDWR_VEC_MAX 16 + +#define MOD_REQ_CORE 0 // core dedicated to check module requests.. +#define SBOX_NCORES 2 // number of sandboxing threads/cores.. + +#endif /* SFRT_TYPES_H */ diff --git a/runtime/include/util.h b/runtime/include/util.h new file mode 100644 index 0000000..fd60138 --- /dev/null +++ b/runtime/include/util.h @@ -0,0 +1,13 @@ +#ifndef SFRT_UTIL_H +#define SFRT_UTIL_H + +#include +#include + +/* perhaps move it to module.h or sandbox.h? */ +struct sandbox *util_parse_sandbox_string_custom(struct module *m, char *str); +struct sandbox *util_parse_sandbox_string_json(struct module *m, char *str); +int util_parse_modules_file_json(char *filename); +int util_parse_modules_file_custom(char *filename); + +#endif /* SFRT_UTIL_H */ diff --git a/runtime/jsmn b/runtime/jsmn new file mode 160000 index 0000000..85695f3 --- /dev/null +++ b/runtime/jsmn @@ -0,0 +1 @@ +Subproject commit 85695f3d5903b1cd5b4030efe50db3b4f5f3c928 diff --git a/runtime/src/env.c b/runtime/src/env.c new file mode 100644 index 0000000..0dd3d03 --- /dev/null +++ b/runtime/src/env.c @@ -0,0 +1,193 @@ +/* https://github.com/gwsystems/silverfish/blob/master/runtime/libc/libc_backing.c */ +#include + +extern i32 inner_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f); + +i32 +env_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f) +{ + i32 i = inner_syscall_handler(n, a, b, c, d, e, f); + + return i; +} + +i32 +env___syscall(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f) +{ + return env_syscall_handler(n, a, b, c, d, e, f); +} + +// Atomic functions, with definitions stolen from musl + +i32 +env_a_ctz_64(u64 x) +{ + __asm__( "bsf %1,%0" : "=r"(x) : "r"(x) ); + + return x; +} + +//static inline int a_ctz_l(unsigned long x) +//{ +// __asm__( "bsf %1,%0" : "=r"(x) : "r"(x) ); +// return x; +//} + +INLINE void +env_a_and_64(i32 p_off, u64 v) +{ + uint64_t* p = get_memory_ptr_void(p_off, sizeof(uint64_t)); + *p &= v; + + // __asm__( "lock ; and %1, %0" + // : "=m"(*p) : "r"(v) : "memory" ); +} + +INLINE void +env_a_or_64(i32 p_off, i64 v) +{ + assert(sizeof(i64) == sizeof(uint64_t)); + uint64_t* p = get_memory_ptr_void(p_off, sizeof(i64)); + *p |= v; + + // __asm__( "lock ; or %1, %0" + // : "=m"(*p) : "r"(v) : "memory" ); +} + +//static inline void a_or_l(volatile void *p, long v) +//{ +// __asm__( "lock ; or %1, %0" +// : "=m"(*(long *)p) : "r"(v) : "memory" ); +//} +// +//static inline void *a_cas_p(volatile void *p, void *t, void *s) +//{ +// __asm__( "lock ; cmpxchg %3, %1" +// : "=a"(t), "=m"(*(long *)p) : "a"(t), "r"(s) : "memory" ); +// return t; +//} +// +i32 +env_a_cas(i32 p_off, i32 t, i32 s) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* p = get_memory_ptr_void(p_off, sizeof(i32)); + + __asm__( "lock ; cmpxchg %3, %1" + : "=a"(t), "=m"(*p) : "a"(t), "r"(s) : "memory" ); + + return t; +} + +void +env_a_or(i32 p_off, i32 v) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* p = get_memory_ptr_void(p_off, sizeof(i32)); + __asm__( "lock ; or %1, %0" + : "=m"(*p) : "r"(v) : "memory" ); +} + +//static inline void a_and(volatile int *p, int v) +//{ +// __asm__( "lock ; and %1, %0" +// : "=m"(*p) : "r"(v) : "memory" ); +//} + +i32 +env_a_swap(i32 x_off, i32 v) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* x = get_memory_ptr_void(x_off, sizeof(i32)); + + __asm__( "xchg %0, %1" : "=r"(v), "=m"(*x) : "0"(v) : "memory" ); + + return v; +} + +i32 +env_a_fetch_add(i32 x_off, i32 v) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* x = get_memory_ptr_void(x_off, sizeof(i32)); + + __asm__( "lock ; xadd %0, %1" : "=r"(v), "=m"(*x) : "0"(v) : "memory" ); + + return v; +} + +void +env_a_inc(i32 x_off) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* x = get_memory_ptr_void(x_off, sizeof(i32)); + + __asm__( "lock ; incl %0" : "=m"(*x) : "m"(*x) : "memory" ); +} + +void +env_a_dec(i32 x_off) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* x = get_memory_ptr_void(x_off, sizeof(i32)); + + __asm__( "lock ; decl %0" : "=m"(*x) : "m"(*x) : "memory" ); +} + +void +env_a_store(i32 p_off, i32 x) +{ + assert(sizeof(i32) == sizeof(volatile int)); + volatile int* p = get_memory_ptr_void(p_off, sizeof(i32)); + __asm__ __volatile__( + "mov %1, %0 ; lock ; orl $0,(%%esp)" : "=m"(*p) : "r"(x) : "memory" ); +} + +void +env_do_spin(i32 i) +{ + __asm__ __volatile__( "pause" : : : "memory" ); +} + +void +env_do_crash(i32 i) +{ + printf("crashing: %d\n", i); + assert(0); +} + +int +env_a_ctz_32(i32 x) +{ + __asm__( "bsf %1,%0" : "=r"(x) : "r"(x) ); + + return x; +} + +void +env_do_barrier(i32 x) +{ + __asm__ __volatile__( "" : : : "memory" ); +} + + +void +env___unmapself(u32 base, u32 size) +{ + // Just do some no op +} + + +// Floating point routines +// TODO: Do a fair comparison between musl and wasm-musl +INLINE double +env_sin(double d) +{ + return sin(d); +} + +INLINE double +env_cos(double d) +{ + return cos(d); +} diff --git a/runtime/src/libc/syscall.c b/runtime/src/libc/syscall.c new file mode 100644 index 0000000..0010fc7 --- /dev/null +++ b/runtime/src/libc/syscall.c @@ -0,0 +1,666 @@ +#ifdef USE_SYSCALL + +/* code from https://github.com/gwsystems/silverfish/blob/master/runtime/libc/libc_backing.c */ +#include +#include +#include + +// What should we tell the child program its UID and GID are? +#define UID 0xFF +#define GID 0xFE + +// Elf auxilary vector values (see google for what those are) +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 +#define AT_SECURE 23 +#define AT_BASE_PLATFORM 24 +#define AT_RANDOM 25 + +// offset = a WASM ptr to memory the runtime can use +void +stub_init(char *program_name, i32 offset, mod_init_libc_fn_t libcfn) +{ + printf("Don't think we should reinit libc! so ignore for now!\n"); +} + +// Emulated syscall implementations + +// We define our own syscall numbers, because WASM uses x86_64 values even on systems that are not x86_64 +#define SYS_READ 0 +u32 +wasm_read(i32 filedes, i32 buf_offset, i32 nbyte) +{ + char* buf = get_memory_ptr_void(buf_offset, nbyte); + i32 res = (i32) read(filedes, buf, nbyte); + + if (res == -1) return -errno; + + return res; +} + +#define SYS_WRITE 1 +i32 +wasm_write(i32 fd, i32 buf_offset, i32 buf_size) +{ + char* buf = get_memory_ptr_void(buf_offset, buf_size); + i32 res = (i32) write(fd, buf, buf_size); + + if (res == -1) return -errno; + + return res; +} + +#define WO_RDONLY 00 +#define WO_WRONLY 01 +#define WO_RDWR 02 +#define WO_CREAT 0100 +#define WO_EXCL 0200 +#define WO_NOCTTY 0400 +#define WO_TRUNC 01000 +#define WO_APPEND 02000 +#define WO_NONBLOCK 04000 +#define WO_DSYNC 010000 +#define WO_SYNC 04010000 +#define WO_RSYNC 04010000 +#define WO_DIRECTORY 0200000 +#define WO_NOFOLLOW 0400000 +#define WO_CLOEXEC 02000000 + + +#define SYS_OPEN 2 +i32 +wasm_open(i32 path_off, i32 flags, i32 mode) +{ + char* path = get_memory_string(path_off); + + i32 modified_flags = 0; + + if (flags & WO_RDONLY) { + modified_flags |= O_RDONLY; + flags ^= WO_RDONLY; + } + + if (flags & WO_WRONLY) { + modified_flags |= O_WRONLY; + flags ^= WO_WRONLY; + } + + if (flags & WO_RDWR) { + modified_flags |= O_RDWR; + flags ^= WO_RDWR; + } + + if (flags & WO_APPEND) { + modified_flags |= O_APPEND; + flags ^= WO_APPEND; + } + + if (flags & WO_CREAT) { + modified_flags |= O_CREAT; + flags ^= WO_CREAT; + } + + if (flags & WO_EXCL) { + modified_flags |= O_EXCL; + flags ^= WO_EXCL; + } + + i32 res = (i32) open(path, modified_flags, mode); + + if (res == -1) return -errno; + + return res; + +} + +#define SYS_CLOSE 3 +i32 +wasm_close(i32 fd) +{ + i32 res = (i32) close(fd); + + if (res == -1) return -errno; + + return res; +} + +// What the wasm stat structure looks like +struct wasm_stat { + i64 st_dev; + u64 st_ino; + u32 st_nlink; + + u32 st_mode; + u32 st_uid; + u32 st_gid; + u32 __pad0; + u64 st_rdev; + u64 st_size; + i32 st_blksize; + i64 st_blocks; + + struct { i32 tv_sec; i32 tv_nsec; } st_atim; + struct { i32 tv_sec; i32 tv_nsec; } st_mtim; + struct { i32 tv_sec; i32 tv_nsec; } st_ctim; + i32 __pad1[3]; +}; + +#define SYS_STAT 4 +// What the OSX stat structure looks like: +// struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */ +// dev_t st_dev; /* device inode resides on */ +// ino_t st_ino; /* inode's number */ +// mode_t st_mode; /* inode protection mode */ +// nlink_t st_nlink; /* number of hard links to the file */ +// uid_t st_uid; /* user-id of owner */ +// gid_t st_gid; /* group-id of owner */ +// dev_t st_rdev; /* device type, for special file inode */ +// struct timespec st_atimespec; /* time of last access */ +// struct timespec st_mtimespec; /* time of last data modification */ +// struct timespec st_ctimespec; /* time of last file status change */ +// off_t st_size; /* file size, in bytes */ +// quad_t st_blocks; /* blocks allocated for file */ +// u_long st_blksize;/* optimal file sys I/O ops blocksize */ +// u_long st_flags; /* user defined flags for file */ +// u_long st_gen; /* file generation number */ +// }; + +i32 +wasm_stat(u32 path_str_offset, i32 stat_offset) +{ + char *path = get_memory_string(path_str_offset); + struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); + + struct stat stat; + i32 res = lstat(path, &stat); + if (res == -1) return -errno; + + *stat_ptr = (struct wasm_stat) { + .st_dev = stat.st_dev, + .st_ino = stat.st_ino, + .st_nlink = stat.st_nlink, + .st_mode = stat.st_mode, + .st_uid = stat.st_uid, + .st_gid = stat.st_gid, + .st_rdev = stat.st_rdev, + .st_size = stat.st_size, + .st_blksize = stat.st_blksize, + .st_blocks = stat.st_blocks, + }; +#ifdef __APPLE__ + stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; +#else + stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; +#endif + + return res; +} + +#define SYS_FSTAT 5 +i32 +wasm_fstat(i32 filedes, i32 stat_offset) +{ + struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); + + struct stat stat; + i32 res = fstat(filedes, &stat); + if (res == -1) return -errno; + + *stat_ptr = (struct wasm_stat) { + .st_dev = stat.st_dev, + .st_ino = stat.st_ino, + .st_nlink = stat.st_nlink, + .st_mode = stat.st_mode, + .st_uid = stat.st_uid, + .st_gid = stat.st_gid, + .st_rdev = stat.st_rdev, + .st_size = stat.st_size, + .st_blksize = stat.st_blksize, + .st_blocks = stat.st_blocks, + }; +#ifdef __APPLE__ + stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; +#else + stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; +#endif + + return res; +} + +#define SYS_LSTAT 6 +i32 +wasm_lstat(i32 path_str_offset, i32 stat_offset) +{ + char *path = get_memory_string(path_str_offset); + struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); + + struct stat stat; + i32 res = lstat(path, &stat); + if (res == -1) return -errno; + + *stat_ptr = (struct wasm_stat) { + .st_dev = stat.st_dev, + .st_ino = stat.st_ino, + .st_nlink = stat.st_nlink, + .st_mode = stat.st_mode, + .st_uid = stat.st_uid, + .st_gid = stat.st_gid, + .st_rdev = stat.st_rdev, + .st_size = stat.st_size, + .st_blksize = stat.st_blksize, + .st_blocks = stat.st_blocks, + }; +#ifdef __APPLE__ + stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; +#else + stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; +#endif + + return res; +} + + +#define SYS_LSEEK 8 +i32 +wasm_lseek(i32 filedes, i32 file_offset, i32 whence) +{ + i32 res = (i32) lseek(filedes, file_offset, whence); + + if (res == -1) return -errno; + + return res; +} + +#define SYS_MMAP 9 +u32 +wasm_mmap(i32 addr, i32 len, i32 prot, i32 flags, i32 fd, i32 offset) +{ + if (addr != 0) { + printf("parameter void *addr is not supported!\n"); + assert(0); + } + + if (fd != -1) { + printf("file mapping is not supported!\n"); + assert(0); + } + + assert(len % WASM_PAGE_SIZE == 0); + + i32 result = sandbox_lmbound; + for (int i = 0; i < len / WASM_PAGE_SIZE; i++) { + expand_memory(); + } + + return result; +} + +#define SYS_MUNMAP 11 + +#define SYS_BRK 12 + +#define SYS_RT_SIGACTION 13 + +#define SYS_RT_SIGPROGMASK 14 + +#define SYS_IOCTL 16 +i32 +wasm_ioctl(i32 fd, i32 request, i32 data_offet) +{ + // musl libc does some ioctls to stdout, so just allow these to silently go through + // FIXME: The above is idiotic + return 0; +} + +#define SYS_READV 19 +struct wasm_iovec { + i32 base_offset; + i32 len; +}; + +i32 +wasm_readv(i32 fd, i32 iov_offset, i32 iovcnt) +{ + i32 read = 0; + struct wasm_iovec *iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); + for (int i = 0; i < iovcnt; i++) { + read += wasm_read(fd, iov[i].base_offset, iov[i].len); + } + + return read; +} + +#define SYS_WRITEV 20 +i32 +wasm_writev(i32 fd, i32 iov_offset, i32 iovcnt) +{ + struct wasm_iovec *iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); + + // If we aren't on MUSL, pass writev to printf if possible +#if defined(__APPLE__) || defined(__GLIBC__) + if (fd == 1) { + int sum = 0; + for (int i = 0; i < iovcnt; i++) { + i32 len = iov[i].len; + void* ptr = get_memory_ptr_void(iov[i].base_offset, len); + + printf("%.*s", len, ptr); + sum += len; + } + return sum; + } +#endif + + struct iovec vecs[iovcnt]; + for (int i = 0; i < iovcnt; i++) { + i32 len = iov[i].len; + void* ptr = get_memory_ptr_void(iov[i].base_offset, len); + vecs[i] = (struct iovec) {ptr, len}; + } + + i32 res = (i32) writev(fd, vecs, iovcnt); + if (res == -1) return -errno; + + return res; +} + +#define SYS_MADVISE 28 + +#define SYS_GETPID 39 +u32 +wasm_getpid() +{ + return (u32) getpid(); +} + + +#define WF_DUPFD 0 +#define WF_GETFD 1 +#define WF_SETFD 2 +#define WF_GETFL 3 +#define WF_SETFL 4 + +#define WF_SETOWN 8 +#define WF_GETOWN 9 +#define WF_SETSIG 10 +#define WF_GETSIG 11 + +#define WF_GETLK 5 +#define WF_SETLK 6 +#define WF_SETLKW 7 + +#define SYS_FCNTL 72 +u32 +wasm_fcntl(u32 fd, u32 cmd, u32 arg_or_lock_ptr) +{ + switch (cmd) { + case WF_SETFD: + // return fcntl(fd, F_SETFD, arg_or_lock_ptr); + return 0; + case WF_SETLK: + return 0; + default: + assert(0); + } + +} + +#define SYS_FSYNC 74 +u32 +wasm_fsync(u32 filedes) +{ + u32 res = fsync(filedes); + if (res == -1) return -errno; + + return 0; +} + +#define SYS_GETCWD 79 +u32 +wasm_getcwd(u32 buf_offset, u32 buf_size) +{ + char* buf = get_memory_ptr_void(buf_offset, buf_size); + char* res = getcwd(buf, buf_size); + + if (!res) return 0; + return buf_offset; +} + +#define SYS_UNLINK 87 +u32 +wasm_unlink(u32 path_str_offset) +{ + char* str = get_memory_string(path_str_offset); + u32 res = unlink(str); + if (res == -1) return -errno; + + return 0; +} + +#define SYS_GETEUID 107 +u32 +wasm_geteuid() +{ + return (u32) geteuid(); +} + +#define SYS_SET_THREAD_AREA 205 + +#define SYS_SET_TID_ADDRESS 218 + +#define SYS_GET_TIME 228 +struct wasm_time_spec { + u32 sec; + u32 nanosec; +}; + +i32 +wasm_get_time(i32 clock_id, i32 timespec_off) +{ + clockid_t real_clock; + switch (clock_id) { + case 0: + real_clock = CLOCK_REALTIME; + break; + case 1: + real_clock = CLOCK_MONOTONIC; + break; + case 2: + real_clock = CLOCK_PROCESS_CPUTIME_ID; + break; + default: + assert(0); + } + + struct wasm_time_spec* timespec = get_memory_ptr_void(timespec_off, sizeof(struct wasm_time_spec)); + + struct timespec native_timespec = { 0, 0 }; + int res = clock_gettime(real_clock, &native_timespec); + if (res == -1) return -errno; + + timespec->sec = native_timespec.tv_sec; + timespec->nanosec = native_timespec.tv_nsec; + + return res; +} + +#define SYS_EXIT_GROUP 231 +i32 +wasm_exit_group(i32 status) +{ + exit(status); + return 0; +} + +#define SYS_FCHOWN 93 +i32 +wasm_fchown(i32 fd, u32 owner, u32 group) +{ + return fchown(fd, owner, group); +} + +// networking syscalls +#define SYS_SOCKET 41 +#define SYS_CONNECT 42 +#define SYS_ACCEPT 43 +#define SYS_BIND 49 +#define SYS_LISTEN 50 +i32 +wasm_socket(i32 domain, i32 type, i32 protocol) +{ + return socket(domain, type, protocol); +} + +i32 +wasm_connect(i32 sockfd, i32 sockaddr_offset, i32 addrlen) +{ + return connect(sockfd, get_memory_ptr_void(sockaddr_offset, addrlen), addrlen); +} + +i32 +wasm_accept(i32 sockfd, i32 sockaddr_offset, i32 addrlen_offset) +{ + socklen_t *addrlen = get_memory_ptr_void(addrlen_offset, sizeof(socklen_t)); + + return accept(sockfd, get_memory_ptr_void(sockaddr_offset, *addrlen), addrlen); +} + +i32 +wasm_bind(i32 sockfd, i32 sockaddr_offset, i32 addrlen) +{ + return bind(sockfd, get_memory_ptr_void(sockaddr_offset, addrlen), addrlen); +} + +i32 +wasm_listen(i32 sockfd, i32 backlog) +{ + return listen(sockfd, backlog); +} + +#define SYS_SENDTO 44 +#define SYS_RECVFROM 45 + +i32 +wasm_sendto(i32 fd, i32 buff_offset, i32 len, i32 flags, i32 sockaddr_offset, i32 sockaddr_len) +{ + char *buf = get_memory_ptr_void(buff_offset, len); + struct sockaddr *addr = sockaddr_len ? get_memory_ptr_void(sockaddr_offset, sockaddr_len) : NULL; + + return sendto(fd, buf, len, flags, addr, sockaddr_len); +} + +i32 +wasm_recvfrom(i32 fd, i32 buff_offset, i32 size, i32 flags, i32 sockaddr_offset, i32 socklen_offset) +{ + char *buf = get_memory_ptr_void(buff_offset, size); + socklen_t *len = get_memory_ptr_void(socklen_offset, sizeof(socklen_t)); + struct sockaddr *addr = *len ? get_memory_ptr_void(sockaddr_offset, *len) : NULL; + + return recvfrom(fd, buf, size, flags, addr, addr ? len : NULL); +} + +i32 +inner_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f) +{ + i32 res; + switch(n) { + case SYS_READ: return wasm_read(a, b, c); + case SYS_WRITE: return wasm_write(a, b, c); + case SYS_OPEN: return wasm_open(a, b, c); + case SYS_CLOSE: return wasm_close(a); + case SYS_STAT: return wasm_stat(a, b); + case SYS_FSTAT: return wasm_fstat(a, b); + case SYS_LSTAT: return wasm_lstat(a, b); + case SYS_LSEEK: return wasm_lseek(a, b, c); + case SYS_MMAP: return wasm_mmap(a, b, c, d, e, f); + case SYS_MUNMAP: return 0; + case SYS_BRK: return 0; + case SYS_RT_SIGACTION: return 0; + case SYS_RT_SIGPROGMASK: return 0; + case SYS_IOCTL: return wasm_ioctl(a, b, c); + case SYS_READV: return wasm_readv(a, b, c); + case SYS_WRITEV: return wasm_writev(a, b, c); + case SYS_MADVISE: return 0; + case SYS_GETPID: return wasm_getpid(); + case SYS_FCNTL: return wasm_fcntl(a, b, c); + case SYS_FSYNC: return wasm_fsync(a); + case SYS_UNLINK: return wasm_unlink(a); + case SYS_GETCWD: return wasm_getcwd(a, b); + case SYS_GETEUID: return wasm_geteuid(); + case SYS_SET_THREAD_AREA: return 0; + case SYS_SET_TID_ADDRESS: return 0; + case SYS_GET_TIME: return wasm_get_time(a, b); + case SYS_EXIT_GROUP: return wasm_exit_group(a); + case SYS_FCHOWN: return wasm_fchown(a, b, c); + + case SYS_SOCKET: return wasm_socket(a, b, c); + case SYS_CONNECT: return wasm_connect(a, b, c); + case SYS_ACCEPT: return wasm_accept(a, b, c); + case SYS_BIND: return wasm_bind(a, b, c); + case SYS_LISTEN: return wasm_listen(a, b); + case SYS_SENDTO: return wasm_sendto(a, b, c, d, e, f); + case SYS_RECVFROM: return wasm_recvfrom(a, b, c, d, e, f); + } + printf("syscall %d (%d, %d, %d, %d, %d, %d)\n", n, a, b, c, d, e, f); + assert(0); + + return 0; +} + +#endif diff --git a/runtime/src/libc/uvio.c b/runtime/src/libc/uvio.c new file mode 100644 index 0000000..e69d644 --- /dev/null +++ b/runtime/src/libc/uvio.c @@ -0,0 +1,1019 @@ +#ifdef USE_UVIO +#include +#include + +// What should we tell the child program its UID and GID are? +#define UID 0xFF +#define GID 0xFE + +// Elf auxilary vector values (see google for what those are) +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 +#define AT_SECURE 23 +#define AT_BASE_PLATFORM 24 +#define AT_RANDOM 25 + +// offset = a WASM ptr to memory the runtime can use +void +stub_init(char *program_name, i32 offset, mod_init_libc_fn_t libcfn) +{ + printf("Don't think we should reinit libc! so ignore for now!\n"); +} + +// Emulated syscall implementations +static inline void * +uv_fs_get_data(uv_fs_t *req) +{ return req->data; } + +static inline ssize_t +uv_fs_get_result(uv_fs_t *req) +{ return req->result; } + +static inline uv_fs_type +uv_fs_get_type(uv_fs_t *req) +{ return req->fs_type; } + +#define UV_FS_REQ_INIT() { .data = sandbox_current(), .result = 0 } + +static void +wasm_fs_callback(uv_fs_t *req) +{ + debuglog("[%p]\n", req->data); + sandbox_wakeup((sandbox_t *)req->data); +} + +// We define our own syscall numbers, because WASM uses x86_64 values even on systems that are not x86_64 +#define SYS_READ 0 +u32 +wasm_read(i32 filedes, i32 buf_offset, i32 nbyte) +{ + int f = io_handle_fd(filedes); + // TODO: read on other file types + uv_fs_t req = UV_FS_REQ_INIT(); + char* buf = get_memory_ptr_void(buf_offset, nbyte); + + debuglog("[%p] start[%d:%d, n%d]\n", uv_fs_get_data(&req), filedes, f, nbyte); + uv_buf_t bufv = uv_buf_init(buf, nbyte); + uv_fs_read(runtime_uvio(), &req, f, &bufv, 1, -1, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + + return ret; +} + +#define SYS_WRITE 1 +i32 +wasm_write(i32 fd, i32 buf_offset, i32 buf_size) +{ + int f = io_handle_fd(fd); + // TODO: read on other file types + uv_fs_t req = UV_FS_REQ_INIT(); + char* buf = get_memory_ptr_void(buf_offset, buf_size); + + debuglog("[%p] start[%d:%d, n%d]\n", uv_fs_get_data(&req), fd, f, buf_size); + uv_buf_t bufv = uv_buf_init(buf, buf_size); + uv_fs_write(runtime_uvio(), &req, f, &bufv, 1, -1, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + + return ret; +} + +#define WO_RDONLY 00 +#define WO_WRONLY 01 +#define WO_RDWR 02 +#define WO_CREAT 0100 +#define WO_EXCL 0200 +#define WO_NOCTTY 0400 +#define WO_TRUNC 01000 +#define WO_APPEND 02000 +#define WO_NONBLOCK 04000 +#define WO_DSYNC 010000 +#define WO_SYNC 04010000 +#define WO_RSYNC 04010000 +#define WO_DIRECTORY 0200000 +#define WO_NOFOLLOW 0400000 +#define WO_CLOEXEC 02000000 + +#define SYS_OPEN 2 +i32 +wasm_open(i32 path_off, i32 flags, i32 mode) +{ + uv_fs_t req = UV_FS_REQ_INIT(); + char* path = get_memory_string(path_off); + + int iofd = io_handle_preopen(); + if (iofd < 0) return -1; + i32 modified_flags = 0; + if (flags & WO_RDONLY) modified_flags |= O_RDONLY; + if (flags & WO_WRONLY) modified_flags |= O_WRONLY; + if (flags & WO_RDWR) modified_flags |= O_RDWR; + if (flags & WO_APPEND) modified_flags |= O_APPEND; + if (flags & WO_CREAT) modified_flags |= O_CREAT; + if (flags & WO_EXCL) modified_flags |= O_EXCL; + debuglog("[%p] start[%s:%d:%d]\n", uv_fs_get_data(&req), path, flags, modified_flags); + + uv_fs_open(runtime_uvio(), &req, path, modified_flags, mode, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + if (ret < 0) io_handle_close(iofd); + else io_handle_preopen_set(iofd, ret); + + return iofd; +} + +#define SYS_CLOSE 3 +i32 +wasm_close(i32 fd) +{ + struct sandbox *c = sandbox_current(); + int d = io_handle_fd(fd); + union uv_any_handle *h = io_handle_uv_get(fd); + uv_handle_type type = ((uv_handle_t *)h)->type; + debuglog("[%p] [%d,%d]\n", c, fd, d); + + if (type == UV_TCP) { + debuglog("[%p] close tcp\n", c); + // TODO: close! + return 0; + } else if (type == UV_UDP) { + debuglog("[%p] close udp\n", c); + // TODO: close! + return 0; + } + + uv_fs_t req = UV_FS_REQ_INIT(); + debuglog("[%p] file[%d,%d]\n", uv_fs_get_data(&req), fd, d); + uv_fs_close(runtime_uvio(), &req, d, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + if (ret == 0) io_handle_close(fd); + return ret; +} + +// What the wasm stat structure looks like +struct wasm_stat { + i64 st_dev; + u64 st_ino; + u32 st_nlink; + + u32 st_mode; + u32 st_uid; + u32 st_gid; + u32 __pad0; + u64 st_rdev; + u64 st_size; + i32 st_blksize; + i64 st_blocks; + + struct { i32 tv_sec; i32 tv_nsec; } st_atim; + struct { i32 tv_sec; i32 tv_nsec; } st_mtim; + struct { i32 tv_sec; i32 tv_nsec; } st_ctim; + i32 __pad1[3]; +}; + +#define SYS_STAT 4 +// What the OSX stat structure looks like: +// struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */ +// dev_t st_dev; /* device inode resides on */ +// ino_t st_ino; /* inode's number */ +// mode_t st_mode; /* inode protection mode */ +// nlink_t st_nlink; /* number of hard links to the file */ +// uid_t st_uid; /* user-id of owner */ +// gid_t st_gid; /* group-id of owner */ +// dev_t st_rdev; /* device type, for special file inode */ +// struct timespec st_atimespec; /* time of last access */ +// struct timespec st_mtimespec; /* time of last data modification */ +// struct timespec st_ctimespec; /* time of last file status change */ +// off_t st_size; /* file size, in bytes */ +// quad_t st_blocks; /* blocks allocated for file */ +// u_long st_blksize;/* optimal file sys I/O ops blocksize */ +// u_long st_flags; /* user defined flags for file */ +// u_long st_gen; /* file generation number */ +// }; + +i32 +wasm_stat(u32 path_str_offset, i32 stat_offset) +{ + char *path = get_memory_string(path_str_offset); + struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); + + struct stat stat; + i32 res = lstat(path, &stat); + if (res == -1) return -errno; + + *stat_ptr = (struct wasm_stat) { + .st_dev = stat.st_dev, + .st_ino = stat.st_ino, + .st_nlink = stat.st_nlink, + .st_mode = stat.st_mode, + .st_uid = stat.st_uid, + .st_gid = stat.st_gid, + .st_rdev = stat.st_rdev, + .st_size = stat.st_size, + .st_blksize = stat.st_blksize, + .st_blocks = stat.st_blocks, + }; +#ifdef __APPLE__ + stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; +#else + stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; +#endif + + return res; +} + +#define SYS_FSTAT 5 +i32 +wasm_fstat(i32 filedes, i32 stat_offset) +{ + struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); + + struct stat stat; + int d = io_handle_fd(filedes); + i32 res = fstat(d, &stat); + if (res == -1) return -errno; + + *stat_ptr = (struct wasm_stat) { + .st_dev = stat.st_dev, + .st_ino = stat.st_ino, + .st_nlink = stat.st_nlink, + .st_mode = stat.st_mode, + .st_uid = stat.st_uid, + .st_gid = stat.st_gid, + .st_rdev = stat.st_rdev, + .st_size = stat.st_size, + .st_blksize = stat.st_blksize, + .st_blocks = stat.st_blocks, + }; +#ifdef __APPLE__ + stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; +#else + stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; +#endif + + return res; +} + +#define SYS_LSTAT 6 +i32 +wasm_lstat(i32 path_str_offset, i32 stat_offset) +{ + char *path = get_memory_string(path_str_offset); + struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); + + struct stat stat; + i32 res = lstat(path, &stat); + if (res == -1) return -errno; + + *stat_ptr = (struct wasm_stat) { + .st_dev = stat.st_dev, + .st_ino = stat.st_ino, + .st_nlink = stat.st_nlink, + .st_mode = stat.st_mode, + .st_uid = stat.st_uid, + .st_gid = stat.st_gid, + .st_rdev = stat.st_rdev, + .st_size = stat.st_size, + .st_blksize = stat.st_blksize, + .st_blocks = stat.st_blocks, + }; +#ifdef __APPLE__ + stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; +#else + stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; + stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; + + stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; + stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + + stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; + stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; +#endif + + return res; +} + + +#define SYS_LSEEK 8 +i32 +wasm_lseek(i32 filedes, i32 file_offset, i32 whence) +{ + int d = io_handle_fd(filedes); + i32 res = (i32) lseek(d, file_offset, whence); + + if (res == -1) return -errno; + + return res; +} + +#define SYS_MMAP 9 +u32 +wasm_mmap(i32 addr, i32 len, i32 prot, i32 flags, i32 fd, i32 offset) +{ + int d = io_handle_fd(fd); + if (fd >= 0) assert(d >= 0); + if (addr != 0) { + printf("parameter void *addr is not supported!\n"); + assert(0); + } + + if (d != -1) { + printf("file mapping is not supported!\n"); + assert(0); + } + + assert(len % WASM_PAGE_SIZE == 0); + + i32 result = sandbox_lmbound; + for (int i = 0; i < len / WASM_PAGE_SIZE; i++) { + expand_memory(); + } + + return result; +} + +#define SYS_MUNMAP 11 + +#define SYS_BRK 12 + +#define SYS_RT_SIGACTION 13 + +#define SYS_RT_SIGPROGMASK 14 + +#define SYS_IOCTL 16 +i32 +wasm_ioctl(i32 fd, i32 request, i32 data_offet) +{ + //int d = io_handle_fd(fd); + // musl libc does some ioctls to stdout, so just allow these to silently go through + // FIXME: The above is idiotic + //assert(d == 1); + return 0; +} + +#define SYS_READV 19 +struct wasm_iovec { + i32 base_offset; + i32 len; +}; + +i32 +wasm_readv(i32 fd, i32 iov_offset, i32 iovcnt) +{ + // TODO: read on other file types + int gret = 0; + int d = io_handle_fd(fd); + struct wasm_iovec *iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); + + for (int i = 0; i < iovcnt; i += RDWR_VEC_MAX) { + uv_fs_t req = UV_FS_REQ_INIT(); + uv_buf_t bufs[RDWR_VEC_MAX] = { 0 }; //avoid mallocs here! + int j = 0; + + for (j = 0; j < RDWR_VEC_MAX && i + j < iovcnt; j++) { + bufs[j] = uv_buf_init(get_memory_ptr_void(iov[i + j].base_offset, iov[i + j].len), iov[i + j].len); + } + debuglog("[%p] start[%d,%d, n%d:%d]\n", uv_fs_get_data(&req), fd, d, i, j); + uv_fs_read(runtime_uvio(), &req, d, bufs, j, -1, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + if (ret < 0) return ret; + gret += ret; + } + debuglog(" gend[%d]\n", gret); + + return gret; +} + +#define SYS_WRITEV 20 +i32 +wasm_writev(i32 fd, i32 iov_offset, i32 iovcnt) +{ + // TODO: read on other file types + int d = io_handle_fd(fd); + int gret = 0; + struct wasm_iovec *iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); + + for (int i = 0; i < iovcnt; i += RDWR_VEC_MAX) { + uv_fs_t req = UV_FS_REQ_INIT(); + uv_buf_t bufs[RDWR_VEC_MAX] = { 0 }; //avoid mallocs here! + int j = 0; + + for (j = 0; j < RDWR_VEC_MAX && i + j < iovcnt; j++) { + bufs[j] = uv_buf_init(get_memory_ptr_void(iov[i + j].base_offset, iov[i + j].len), iov[i + j].len); + } + debuglog("[%p] start[%d,%d, n%d:%d]\n", uv_fs_get_data(&req), fd, d, i, j); + uv_fs_write(runtime_uvio(), &req, d, bufs, j, -1, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + if (ret < 0) return ret; + gret += ret; + } + debuglog(" gend[%d]\n", gret); + + return gret; +} + +#define SYS_MADVISE 28 + +#define SYS_GETPID 39 +u32 +wasm_getpid() +{ + return (u32) getpid(); +} + + +#define WF_DUPFD 0 +#define WF_GETFD 1 +#define WF_SETFD 2 +#define WF_GETFL 3 +#define WF_SETFL 4 + +#define WF_SETOWN 8 +#define WF_GETOWN 9 +#define WF_SETSIG 10 +#define WF_GETSIG 11 + +#define WF_GETLK 5 +#define WF_SETLK 6 +#define WF_SETLKW 7 + +#define SYS_FCNTL 72 +u32 +wasm_fcntl(u32 fd, u32 cmd, u32 arg_or_lock_ptr) +{ + int d = io_handle_fd(fd); + switch (cmd) { + case WF_SETFD: + // return fcntl(d, F_SETFD, arg_or_lock_ptr); + return 0; + case WF_SETLK: + return 0; + default: + assert(0); + } + +} + +#define SYS_FSYNC 74 +u32 +wasm_fsync(u32 fd) +{ + int d = io_handle_fd(fd); + uv_fs_t req = UV_FS_REQ_INIT(); + debuglog("[%p] start[%d,%d]\n", uv_fs_get_data(&req), fd, d); + uv_fs_fsync(runtime_uvio(), &req, d, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + + return ret; +} + +#define SYS_GETCWD 79 +u32 +wasm_getcwd(u32 buf_offset, u32 buf_size) +{ + char* buf = get_memory_ptr_void(buf_offset, buf_size); + char* res = getcwd(buf, buf_size); + + if (!res) return 0; + return buf_offset; +} + +#define SYS_UNLINK 87 +u32 +wasm_unlink(u32 path_str_offset) +{ + char *str = get_memory_string(path_str_offset); + uv_fs_t req = UV_FS_REQ_INIT(); + debuglog("[%p] start[%s]\n", uv_fs_get_data(&req), str); + uv_fs_unlink(runtime_uvio(), &req, str, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + + return ret; +} + +#define SYS_GETEUID 107 +u32 +wasm_geteuid() +{ + return (u32) geteuid(); +} + +#define SYS_SET_THREAD_AREA 205 + +#define SYS_SET_TID_ADDRESS 218 + +#define SYS_GET_TIME 228 +struct wasm_time_spec { + u32 sec; + u32 nanosec; +}; + +i32 +wasm_get_time(i32 clock_id, i32 timespec_off) +{ + clockid_t real_clock; + switch (clock_id) { + case 0: + real_clock = CLOCK_REALTIME; + break; + case 1: + real_clock = CLOCK_MONOTONIC; + break; + case 2: + real_clock = CLOCK_PROCESS_CPUTIME_ID; + break; + default: + assert(0); + } + + struct wasm_time_spec* timespec = get_memory_ptr_void(timespec_off, sizeof(struct wasm_time_spec)); + + struct timespec native_timespec = { 0, 0 }; + int res = clock_gettime(real_clock, &native_timespec); + if (res == -1) return -errno; + + timespec->sec = native_timespec.tv_sec; + timespec->nanosec = native_timespec.tv_nsec; + + return res; +} + +#define SYS_EXIT_GROUP 231 +i32 +wasm_exit_group(i32 status) +{ + exit(status); + return 0; +} + +#define SYS_FCHOWN 93 +i32 +wasm_fchown(i32 fd, u32 owner, u32 group) +{ + int d = io_handle_fd(fd); + uv_fs_t req = UV_FS_REQ_INIT(); + debuglog("[%p] start[%d,%d]\n", uv_fs_get_data(&req), fd, d); + uv_fs_fchown(runtime_uvio(), &req, d, owner, group, wasm_fs_callback); + sandbox_block(); + + int ret = uv_fs_get_result(&req); + debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); + uv_fs_req_cleanup(&req); + + return ret; +} + +// networking syscalls +#define SYS_SOCKET 41 +#define SYS_CONNECT 42 +#define SYS_ACCEPT 43 +#define SYS_BIND 49 +#define SYS_LISTEN 50 + +static void +wasm_connection_callback(uv_stream_t *srv, int status) +{ + sandbox_t *s = srv->data; + debuglog(" [%p]\n", s); + s->retval = status; + sandbox_wakeup(s); +} + +static void +wasm_connect_callback(uv_connect_t *req, int status) +{ + // TODO: how do we use the handle in uv_connect_t ?? + sandbox_t *s = req->data; + debuglog(" [%p]\n", s); + s->retval = status; + sandbox_wakeup(s); +} + +i32 +wasm_socket(i32 domain, i32 type, i32 protocol) +{ + struct sandbox *c = sandbox_current(); + int pfd = io_handle_preopen(), fd = -1; + // TODO: use domain, type and protocol in libuv? + debuglog("[%p] => %d %d %d\n", c, domain, type, protocol); + if (pfd < 0) return pfd; + union uv_any_handle *h = io_handle_uv_get(pfd); + + if (type & SOCK_DGRAM) { + uv_udp_t *uh = (uv_udp_t *)h; + uh->data = c; + uv_udp_init(runtime_uvio(), uh); + debuglog(" udp init done!\n"); + } else if (type & SOCK_STREAM) { + uv_tcp_t *uh = (uv_tcp_t *)h; + uh->data = c; + uv_tcp_init(runtime_uvio(), uh); + debuglog(" tcp init done!\n"); + } else { + assert(0); // not supported yet! + } + + return pfd; +} + +i32 +wasm_connect(i32 sockfd, i32 sockaddr_offset, i32 addrlen) +{ + struct sandbox *c = sandbox_current(); + int fd = io_handle_fd(sockfd); + debuglog("[%p] [%d, %d]\n", c, sockfd, fd); + union uv_any_handle *h = io_handle_uv_get(sockfd); + uv_handle_type t = ((uv_handle_t *)h)->type; + + if (t == UV_TCP) { + uv_connect_t req = { .data = c }; + debuglog("[%p] connect\n", c); + int r = uv_tcp_connect(&req, (uv_tcp_t *)h, get_memory_ptr_void(sockaddr_offset, addrlen), wasm_connect_callback); + sandbox_block(); + + debuglog("[%p] %d\n", c, c->retval); + return c->retval; + } else if (t == UV_UDP) { + debuglog(" UDP connect not implemented!\n"); + // TODO: this api is in the doc online but not in the libuv installed.. perhaps update?? + // return uv_udp_connect((uv_udp_t *)h, get_memory_ptr_void(sockaddr_offset, addrlen)); + } + debuglog(" unsupported\n"); + assert(0); + return -1; +} + +i32 +wasm_accept(i32 sockfd, i32 sockaddr_offset, i32 addrlen_offset) +{ + // what do we do with the sockaddr ???? + socklen_t *addrlen = get_memory_ptr_void(addrlen_offset, sizeof(socklen_t)); + struct sockaddr *addr = get_memory_ptr_void(sockaddr_offset, *addrlen); + union uv_any_handle *s = io_handle_uv_get(sockfd); + int cfd = io_handle_preopen(); + if (cfd < 0) return -1; + struct sandbox *c = sandbox_current(); + debuglog("[%p] [%d, %d]\n", c, sockfd, io_handle_fd(sockfd)); + + // assert so we can look into whether we need to implement UDP or others.. + assert(((uv_handle_t *)s)->type == UV_TCP); + union uv_any_handle *h = io_handle_uv_get(cfd); + uv_tcp_init(runtime_uvio(), (uv_tcp_t *)h); + debuglog("[%p] tcp init %d\n", c, cfd); + int r = uv_accept((uv_stream_t *)s, (uv_stream_t *)h); + if (r < 0) return r; + // TODO: if accept fails, what do we do with the preopened handle? + // if (r < 0) io_handle_close(cfd); + // we've to also remove it from the runtime loop?? + + int r2 = -1, f = -1; + r2 = uv_fileno((uv_handle_t *)h, &f); + if (r2 < 0 || f < 0) assert(0); + io_handle_preopen_set(cfd, f); + debuglog("[%p] done[%d,%d]\n", c, cfd, f); + + return cfd; +} + +i32 +wasm_bind(i32 sockfd, i32 sockaddr_offset, i32 addrlen) +{ + struct sandbox *c = sandbox_current(); + int fd = io_handle_fd(sockfd); + debuglog("[%p] [%d,%d]\n", c, sockfd, fd); + union uv_any_handle *h = io_handle_uv_get(sockfd); + uv_handle_type t = ((uv_handle_t *)h)->type; + + if (t == UV_TCP) { + debuglog("[%p] tcp\n", c); + int r1 = uv_tcp_bind((uv_tcp_t *)h, get_memory_ptr_void(sockaddr_offset, addrlen), 0 /* TODO: flags */); + if (fd == SBOX_PREOPEN_MAGIC) { + int r2 = -1, f = -1; + r2 = uv_fileno((uv_handle_t *)h, &f); + debuglog("[%p] [%d,%d]\n", c, f, fd); + io_handle_preopen_set(sockfd, f); + } + return r1; + } else if (t == UV_UDP) { + debuglog("[%p] udp\n", c); + int r1 = uv_udp_bind((uv_udp_t *)h, get_memory_ptr_void(sockaddr_offset, addrlen), 0 /* TODO: flags */); + if (fd == SBOX_PREOPEN_MAGIC) { + int r2 = -1, f = -1; + r2 = uv_fileno((uv_handle_t *)h, &f); + debuglog("[%p] [%d,%d]\n", c, f, fd); + io_handle_preopen_set(sockfd, f); + } + return r1; + } + debuglog("[%p] unimplemented\n", c); + assert(0); + return -1; +} + +i32 +wasm_listen(i32 sockfd, i32 backlog) +{ + struct sandbox *c = sandbox_current(); + union uv_any_handle *h = io_handle_uv_get(sockfd); + assert(c == (struct sandbox *)(((uv_tcp_t *)h)->data)); + debuglog("[%p] [%d,%d]\n", c, sockfd, io_handle_fd(sockfd)); + uv_handle_type t = ((uv_handle_t *)h)->type; + + // assert so we can look into whether we need to implement UDP or others.. + assert(t == UV_TCP); + + int r = uv_listen((uv_stream_t *)h, backlog, wasm_connection_callback); + sandbox_block(); + + debuglog("[%p] %d\n", c, c->retval); + return c->retval; +} + +#define SYS_SENDTO 44 +#define SYS_RECVFROM 45 + +void +wasm_read_callback(uv_stream_t *s, ssize_t nread, const uv_buf_t *buf) +{ + struct sandbox *c = s->data; + + debuglog("[%p] %ld %p\n", c, nread, buf); + if (nread > 0) { + assert(c->read_buf); + if (c->read_len + nread > c->read_size) { + debuglog("[%p] oops!\n", c); + c->read_buf = NULL; // TODO: stream read is crazy! + c->retval = -EIO; // TODO: well!!! cannot do anything here.. + // FIXME: very complex.. what if read read more than what is requested in recv system call?? !!!! + // for now, I don't care! + } + memcpy((c->read_buf + c->read_len), buf->base, nread); + c->read_len += nread; + } + debuglog("[%p] %ld\n", c, c->read_len); + free(buf->base); + uv_read_stop(s); + sandbox_wakeup(c); +} + +void +wasm_write_callback(uv_write_t *req, int status) +{ + struct sandbox *c = req->data; + c->retval = status; + debuglog("[%p] %d\n", c, status); + + sandbox_wakeup(c); +} + +void +wasm_udp_recv_callback(uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) +{ + struct sandbox *c = h->data; + + debuglog("[%p] %ld %p\n", c, nread, buf); + if (nread > 0) { + assert(c->read_buf); + if (c->read_len + nread > c->read_size) { + debuglog("[%p] oops!\n", c); + c->read_buf = NULL; // TODO: stream read is crazy! + c->retval = -EIO; // TODO: well!!! cannot do anything here.. + // FIXME: very complex.. what if read read more than what is requested in recv system call?? !!!! + // for now, I don't care! + } + memcpy((c->read_buf + c->read_len), buf->base, nread); + c->read_len += nread; + } + debuglog("[%p] %ld\n", c, c->read_len); + free(buf->base); + uv_udp_recv_stop(h); + sandbox_wakeup(c); +} + +void +wasm_udp_send_callback(uv_udp_send_t *req, int status) +{ + struct sandbox *c = req->data; + c->retval = status; + debuglog("[%p] %d\n", c, status); + + sandbox_wakeup(c); +} + +i32 +wasm_sendto(i32 fd, i32 buff_offset, i32 len, i32 flags, i32 sockaddr_offset, i32 sockaddr_len) +{ + char *buf = get_memory_ptr_void(buff_offset, len); + // TODO: only support "send" api for now + assert(sockaddr_len == 0); + struct sandbox *c = sandbox_current(); + union uv_any_handle *h = io_handle_uv_get(fd); + uv_handle_type t = ((uv_handle_t *)h)->type; + debuglog("[%p] [%d,%d]\n", c, fd, io_handle_fd(fd)); + + if (t == UV_TCP) { + uv_write_t req = { .data = c, }; + uv_buf_t b = uv_buf_init(buf, len); + debuglog("[%p] tcp\n", c); + int ret = uv_write(&req, (uv_stream_t *)h, &b, 1, wasm_write_callback); + sandbox_block(); + + debuglog("[%p] %d\n", c, c->retval); + return c->retval; + } else if (t == UV_UDP) { + uv_udp_send_t req = { .data = c, }; + uv_buf_t b = uv_buf_init(buf, len); + debuglog("[%p] udp\n", c); + // TODO: sockaddr! + int r = uv_udp_send(&req, (uv_udp_t *)h, &b, 1, NULL, wasm_udp_send_callback); + sandbox_block(); + + debuglog("[%p] %d\n", c, c->retval); + return c->retval; + } + debuglog("[%p] unimplemented\n", c); + assert(0); + return 0; +} + +i32 +wasm_recvfrom(i32 fd, i32 buff_offset, i32 size, i32 flags, i32 sockaddr_offset, i32 socklen_offset) +{ + char *buf = get_memory_ptr_void(buff_offset, size); + socklen_t *len = get_memory_ptr_void(socklen_offset, sizeof(socklen_t)); + // TODO: only support "recv" api for now + assert(*len == 0); + struct sandbox *c = sandbox_current(); + union uv_any_handle *h = io_handle_uv_get(fd); + uv_handle_type t = ((uv_handle_t *)h)->type; + debuglog("[%p] [%d,%d]\n", c, fd, io_handle_fd(fd)); + + // uv stream API are not simple wrappers on read/write.. + // and there will only be one system call pending.. + // so we keep the read buffer pointers in sandbox structure.. + // for use in the callbacks.. + c->read_buf = buf; + c->read_size = size; + c->read_len = 0; + c->retval = 0; + // TODO: what if stream read more than what "size" is here?? + + if (t == UV_TCP) { + ((uv_stream_t *)h)->data = c; + debuglog("[%p] tcp\n", c); + int r = uv_read_start((uv_stream_t *)h, runtime_on_alloc, wasm_read_callback); + sandbox_block(); + debuglog("[%p] %d\n", c, c->retval); + if (c->retval == -EIO) { + // TODO: buffer errors?? + } + if (r >= 0 && c->retval == 0) { + return c->read_len; + } + return -EIO; + } else if (t == UV_UDP) { + ((uv_udp_t *)h)->data = c; + debuglog("[%p] udp\n", c); + int r = uv_udp_recv_start((uv_udp_t *)h, runtime_on_alloc, wasm_udp_recv_callback); + sandbox_block(); + debuglog("[%p] %d\n", c, c->retval); + if (c->retval == -EIO) { + // TODO: buffer errors?? + } + if (r >= 0 && c->retval == 0) { + return c->read_len; + } + return -EIO; + } + debuglog("[%p] unimplemented\n", c); + assert(0); + return 0; +} + +i32 +inner_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f) +{ + i32 res; + switch(n) { + case SYS_READ: return wasm_read(a, b, c); + case SYS_WRITE: return wasm_write(a, b, c); + case SYS_OPEN: return wasm_open(a, b, c); + case SYS_CLOSE: return wasm_close(a); + case SYS_STAT: return wasm_stat(a, b); + case SYS_FSTAT: return wasm_fstat(a, b); + case SYS_LSTAT: return wasm_lstat(a, b); + case SYS_LSEEK: return wasm_lseek(a, b, c); + case SYS_MMAP: return wasm_mmap(a, b, c, d, e, f); + case SYS_MUNMAP: return 0; + case SYS_BRK: return 0; + case SYS_RT_SIGACTION: return 0; + case SYS_RT_SIGPROGMASK: return 0; + case SYS_IOCTL: return wasm_ioctl(a, b, c); + case SYS_READV: return wasm_readv(a, b, c); + case SYS_WRITEV: return wasm_writev(a, b, c); + case SYS_MADVISE: return 0; + case SYS_GETPID: return wasm_getpid(); + case SYS_FCNTL: return wasm_fcntl(a, b, c); + case SYS_FSYNC: return wasm_fsync(a); + case SYS_UNLINK: return wasm_unlink(a); + case SYS_GETCWD: return wasm_getcwd(a, b); + case SYS_GETEUID: return wasm_geteuid(); + case SYS_SET_THREAD_AREA: return 0; + case SYS_SET_TID_ADDRESS: return 0; + case SYS_GET_TIME: return wasm_get_time(a, b); + case SYS_EXIT_GROUP: return wasm_exit_group(a); + case SYS_FCHOWN: return wasm_fchown(a, b, c); + + case SYS_SOCKET: return wasm_socket(a, b, c); + case SYS_CONNECT: return wasm_connect(a, b, c); + case SYS_ACCEPT: return wasm_accept(a, b, c); + case SYS_BIND: return wasm_bind(a, b, c); + case SYS_LISTEN: return wasm_listen(a, b); + case SYS_SENDTO: return wasm_sendto(a, b, c, d, e, f); + case SYS_RECVFROM: return wasm_recvfrom(a, b, c, d, e, f); + } + printf("syscall %d (%d, %d, %d, %d, %d, %d)\n", n, a, b, c, d, e, f); + assert(0); + + return 0; +} + +#endif diff --git a/runtime/src/main.c b/runtime/src/main.c new file mode 100644 index 0000000..5acb519 --- /dev/null +++ b/runtime/src/main.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_LINE_MAX 1024 + +i32 logfd = -1; + +u32 ncores = 0, sbox_ncores = 0, sbox_core_st = 0; + +static void +usage(char *cmd) +{ + printf("%s \n", cmd); + debuglog("%s \n", cmd); +} + +int +main(int argc, char* argv[]) +{ + int i = 0, rtthd_ret[SBOX_NCORES] = { 0 }; + pthread_t rtthd[SBOX_NCORES]; + + if (argc != 2) { + usage(argv[0]); + + exit(-1); + } + + ncores = sysconf(_SC_NPROCESSORS_ONLN); + + if (ncores > 1) { + u32 x = ncores - 1; + sbox_ncores = SBOX_NCORES; + if (x < SBOX_NCORES) sbox_ncores = x; + sbox_core_st = 1; + } else { + sbox_ncores = 1; + } + debuglog("Number of cores %u, sandboxing cores %u (start: %u) and module reqs %u\n", ncores, sbox_ncores, sbox_core_st, MOD_REQ_CORE); + +#ifdef NOSTDIO + fclose(stdout); + fclose(stderr); + fclose(stdin); + logfd = open(LOGFILE, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU | S_IRWXG); + if (logfd < 0) { + perror("open"); + exit(-1); + } +#else + logfd = 1; +#endif + + runtime_init(); + debuglog("Parsing modules file [%s]\n", argv[1]); + if (util_parse_modules_file_json(argv[1])) { +// if (util_parse_modules_file_custom(argv[1])) { + printf("failed to parse modules file[%s]\n", argv[1]); + + exit(-1); + } + + for (i = 0; i < sbox_ncores; i++) { + int ret = pthread_create(&rtthd[i], NULL, sandbox_run_func, (void *)&rtthd_ret[i]); + if (ret) { + errno = ret; + perror("pthread_create"); + exit(-1); + } + + cpu_set_t cs; + CPU_ZERO(&cs); + CPU_SET(sbox_core_st + i, &cs); + ret = pthread_setaffinity_np(rtthd[i], sizeof(cs), &cs); + assert(ret == 0); + } + debuglog("Sandboxing environment ready!\n"); + + for (i = 0; i < sbox_ncores; i++) { + int ret = pthread_join(rtthd[i], NULL); + if (ret) { + errno = ret; + perror("pthread_join"); + exit(-1); + } + } + + // runtime threads run forever!! so join should not return!! + printf("\nOh no..!! This can't be happening..!!\n"); + exit(-1); +} diff --git a/runtime/src/memory/64bit_nix.c b/runtime/src/memory/64bit_nix.c new file mode 100644 index 0000000..ba28790 --- /dev/null +++ b/runtime/src/memory/64bit_nix.c @@ -0,0 +1,86 @@ +/* Code from https://github.com/gwsystems/silverfish/blob/master/runtime/memory/64bit_nix.c */ +#include +#include + +#ifdef USE_MEM_VM + +#include + +#define MAX_LINEAR_MEM ((1LL << 32) + WASM_PAGE_SIZE) + +void +alloc_linear_memory(void) +{ + struct sandbox *curr = sandbox_current(); + + // Map 4gb + PAGE_SIZE of memory that will fault when accessed + // We allocate the extra page so that reads off the end will also fail + sandbox_lmbase = mmap(NULL, MAX_LINEAR_MEM, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (sandbox_lmbase == MAP_FAILED) { + perror("Mapping of initial unusable region failed"); + exit(1); + } + + void *map_result = mmap(sandbox_lmbase, WASM_PAGE_SIZE * WASM_START_PAGES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (map_result == MAP_FAILED) { + perror("Mapping of initial memory failed"); + exit(1); + } + sandbox_lmbound = WASM_PAGE_SIZE * WASM_START_PAGES; + + curr->linear_start = sandbox_lmbase; + curr->linear_size = sandbox_lmbound; +} + +void +free_linear_memory(void *base, u32 bound, u32 max) +{ + struct sandbox *curr = sandbox_current(); + + assert(base && bound); + // cannot free currently executing sandbox's memory + assert(base != curr->linear_start || base != sandbox_lmbase); + + int ret = munmap(base, MAX_LINEAR_MEM); + if (ret) perror("munmap"); +} + +void +expand_memory(void) +{ + struct sandbox *curr = sandbox_current(); + + // max_pages = 0 => no limit: FIXME + assert(sandbox_lmbound / WASM_PAGE_SIZE < WASM_MAX_PAGES); + // Remap the relevant wasm page to readable + char *mem_as_chars = sandbox_lmbase; + char *page_address = &mem_as_chars[sandbox_lmbound]; + + void *map_result = mmap(page_address, WASM_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (map_result == MAP_FAILED) { + perror("Mapping of new memory failed"); + exit(1); + } + + // TODO: check curr->linear_max_size + sandbox_lmbound += WASM_PAGE_SIZE; + curr->linear_size = sandbox_lmbound; +} + +INLINE char * +get_memory_ptr_for_runtime(u32 offset, u32 bounds_check) +{ + // Due to how we setup memory for x86, the virtual memory mechanism will catch the error, if bounds < WASM_PAGE_SIZE + assert(bounds_check < WASM_PAGE_SIZE || + (sandbox_lmbound > bounds_check && + offset <= sandbox_lmbound - bounds_check)); + + char *mem_as_chars = (char *)sandbox_lmbase; + char *address = &mem_as_chars[offset]; + + return address; +} + +#else +#error "Incorrect runtime memory module!" +#endif diff --git a/runtime/src/memory/common.c b/runtime/src/memory/common.c new file mode 100644 index 0000000..d574d07 --- /dev/null +++ b/runtime/src/memory/common.c @@ -0,0 +1,35 @@ +#include + +__thread struct indirect_table_entry *module_indirect_table = NULL; +__thread void *sandbox_lmbase = NULL; +__thread u32 sandbox_lmbound = 0; + +// Region initialization helper function +EXPORT void +initialize_region(u32 offset, u32 data_count, char* data) +{ + assert(sandbox_lmbound >= data_count); + assert(offset < sandbox_lmbound - data_count); + + // FIXME: Hack around segmented and unsegmented access + memcpy(get_memory_ptr_for_runtime(offset, data_count), data, data_count); +} + +void +add_function_to_table(u32 idx, u32 type_id, char* pointer) +{ + assert(idx < INDIRECT_TABLE_SIZE); + + // TODO: atomic for multiple concurrent invocations? + if (module_indirect_table[idx].type_id == type_id && module_indirect_table[idx].func_pointer == pointer) return; + + module_indirect_table[idx] = (struct indirect_table_entry) { .type_id = type_id, .func_pointer = pointer }; +} + +// If we are using runtime globals, we need to populate them +WEAK void +populate_globals() +{ + assert(0); // FIXME: is this used in WASM as dynamic modules? +} + diff --git a/runtime/src/module.c b/runtime/src/module.c new file mode 100644 index 0000000..9b68862 --- /dev/null +++ b/runtime/src/module.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include + +static struct module *__mod_db[MOD_MAX] = { NULL }; +static int __mod_free_off = 0; + +// todo: optimize this.. do we care? plus not atomic!! +struct module * +module_find(char *name) +{ + int f = __mod_free_off; + for (int i = 0; i < f; i++) { + assert(__mod_db[i]); + if (strcmp(__mod_db[i]->name, name) == 0) return __mod_db[i]; + } + return NULL; +} + +static inline int +module_add(struct module *m) +{ + assert(module_find(m->name) == NULL); + + int f = __sync_fetch_and_add(&__mod_free_off, 1); + assert(f < MOD_MAX); + __mod_db[f] = m; + return 0; +} + +static void +module_on_recv(uv_udp_t *h, ssize_t nr, const uv_buf_t *rcvbuf, const struct sockaddr *addr, unsigned flags) +{ + if (nr <= 0) goto done; + + debuglog("MC:%s, %s\n", h->data, rcvbuf->base); + // invoke a function! + struct sandbox *s = util_parse_sandbox_string_json((struct module *)(h->data), rcvbuf->base); + //struct sandbox *s = util_parse_sandbox_string_custom((struct module *)(h->data), rcvbuf->base); + assert(s); + +done: + free(rcvbuf->base); +} + +static void +module_io_init(struct module *m) +{ + int status; + + status = uv_udp_init(uv_default_loop(), &m->udpsrv); + assert(status >= 0); + + debuglog("MIO:%s,%u\n", m->name, m->udpport); + uv_ip4_addr("127.0.0.1", m->udpport, &m->srvaddr); + status = uv_udp_bind(&m->udpsrv, (const struct sockaddr *)&m->srvaddr, 0); + assert(status >= 0); + m->udpsrv.data = (void *)m; + + status = uv_udp_recv_start(&m->udpsrv, runtime_on_alloc, module_on_recv); + assert(status >= 0); +} + +struct module * +module_alloc(char *modname, char *modpath, u32 udp_port, i32 nargs, i32 nrets, u32 stacksz, u32 maxheap, u32 timeout) +{ + // FIXME: cannot do this at runtime, we may be interfering with a sandbox's heap! + struct module *mod = (struct module *)malloc(sizeof(struct module)); + + if (!mod) return NULL; + + memset(mod, 0, sizeof(struct module)); + mod->dl_handle = dlopen(modpath, RTLD_LAZY | RTLD_DEEPBIND); + if (mod->dl_handle == NULL) goto dl_open_error; + + mod->entry_fn = (mod_main_fn_t)dlsym(mod->dl_handle, MOD_MAIN_FN); + if (mod->entry_fn == NULL) goto dl_error; + + // TODO: don't think this is necessary or implemented. + //mod->glb_init_fn = (mod_glb_fn_t)dlsym(mod->dl_handle, MOD_GLB_FN); + //if (mod->glb_init_fn == NULL) goto dl_error; + + mod->mem_init_fn = (mod_mem_fn_t)dlsym(mod->dl_handle, MOD_MEM_FN); + if (mod->mem_init_fn == NULL) goto dl_error; + + mod->tbl_init_fn = (mod_tbl_fn_t)dlsym(mod->dl_handle, MOD_TBL_FN); + if (mod->tbl_init_fn == NULL) goto dl_error; + + strncpy(mod->name, modname, MOD_NAME_MAX); + strncpy(mod->path, modpath, MOD_PATH_MAX); + + mod->nargs = nargs; + /* mod->nrets = nrets; */ + mod->stack_size = stacksz == 0 ? WASM_STACK_SIZE : stacksz; + mod->max_memory = maxheap == 0 ? ((u64)WASM_PAGE_SIZE * WASM_MAX_PAGES) : maxheap; + mod->timeout = timeout; + + struct indirect_table_entry *cache_tbl = module_indirect_table; + // assumption: modules are created before enabling preemption and before running runtime-sandboxing threads.. + // if this isn't a good assumption, just let the first invocation do table init..!! + assert(cache_tbl == NULL); + module_indirect_table = mod->indirect_table; + module_table_init(mod); + module_indirect_table = cache_tbl; + mod->udpport = udp_port; + module_add(mod); + module_io_init(mod); + + return mod; + +dl_error: + dlclose(mod->dl_handle); + +dl_open_error: + free(mod); + debuglog("%s\n", dlerror()); + + return NULL; +} + +void +module_free(struct module *mod) +{ + if (mod == NULL) return; + if (mod->dl_handle == NULL) return; + if (mod->refcnt) return; + + dlclose(mod->dl_handle); + memset(mod, 0, sizeof(struct module)); + + // FIXME: use global/static memory. cannot interfere with some sandbox's heap! + free(mod); +} diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c new file mode 100644 index 0000000..3c3ac85 --- /dev/null +++ b/runtime/src/runtime.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// global queue for stealing! TODO: work-stealing-deque +static struct ps_list_head glbq; +static pthread_mutex_t glbq_mtx = PTHREAD_MUTEX_INITIALIZER; + +// per-thread (per-core) run and wait queues.. (using doubly-linked-lists) +__thread static struct ps_list_head runq; +__thread static struct ps_list_head waitq; + +// current sandbox that is active.. +__thread sandbox_t *current_sandbox = NULL; + +// context pointer to switch to when this thread gets a SIGUSR1 +__thread arch_context_t *next_context = NULL; + +// context of the runtime thread before running sandboxes or to resume its "main". +__thread arch_context_t base_context; + +// libuv i/o loop handle per sandboxing thread! +__thread uv_loop_t uvio; + +static inline void +sandbox_local_run(struct sandbox *s) +{ + assert(ps_list_singleton_d(s)); + + ps_list_head_append_d(&runq, s); +} + +static inline int +sandbox_pull(void) +{ + int n = 0; + + while (n < SBOX_PULL_MAX) { + pthread_mutex_lock(&glbq_mtx); + if (ps_list_head_empty(&glbq)) { + pthread_mutex_unlock(&glbq_mtx); + break; + } + struct sandbox *g = ps_list_head_first_d(&glbq, struct sandbox); + + assert(g); + ps_list_rem_d(g); + pthread_mutex_unlock(&glbq_mtx); + debuglog("[%p: %s]\n", g, g->mod->name); + assert(g->state == SANDBOX_RUNNABLE); + sandbox_local_run(g); + n++; + } + + return n; +} + +static __thread unsigned int in_callback; + +void +sandbox_io_nowait(void) +{ +#ifdef USE_UVIO + // non-zero if more callbacks are expected + in_callback = 1; + int n = uv_run(runtime_uvio(), UV_RUN_NOWAIT), i = 0; + while (n > 0) { + n--; + uv_run(runtime_uvio(), UV_RUN_NOWAIT); + } + in_callback = 0; +#endif + // zero, so there is nothing (don't block!) +} + +struct sandbox * +sandbox_schedule(void) +{ + if (!in_callback) sandbox_io_nowait(); + + struct sandbox *s = NULL; + if (ps_list_head_empty(&runq)) { + if (sandbox_pull() == 0) { + //debuglog("[null: null]\n"); + return NULL; + } + } + + s = ps_list_head_first_d(&runq, struct sandbox); + + // round-robin + ps_list_rem_d(s); + ps_list_head_append_d(&runq, s); + debuglog("[%p: %s]\n", s, s->mod->name); + + return s; +} + +void +sandbox_wakeup(sandbox_t *s) +{ + debuglog("[%p: %s]\n", s, s->mod->name); + // perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist.. + assert(ps_list_singleton_d(s)); + s->state = SANDBOX_RUNNABLE; + ps_list_head_append_d(&runq, s); +} + +void +sandbox_block(void) +{ + // perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist.. + softint_disable(); + struct sandbox *c = sandbox_current(); + ps_list_rem_d(c); + softint_enable(); + debuglog("[%p: %s]\n", c, c->mod->name); + c->state = SANDBOX_BLOCKED; + struct sandbox *s = sandbox_schedule(); + sandbox_switch(s); +} + +void __attribute__((noinline)) __attribute__((noreturn)) +sandbox_switch_preempt(void) +{ + pthread_kill(pthread_self(), SIGUSR1); + + assert(0); // should not get here.. + while (1) ; +} +static inline void +sandbox_local_stop(struct sandbox *s) +{ + ps_list_rem_d(s); +} + +void * +sandbox_run_func(void *data) +{ + arch_context_init(&base_context, 0, 0); + + ps_list_head_init(&runq); + ps_list_head_init(&waitq); + softint_off = 0; + next_context = NULL; +#ifndef PREEMPT_DISABLE + softint_unmask(SIGALRM); + softint_unmask(SIGUSR1); +#endif + uv_loop_init(&uvio); + in_callback = 0; + + while (1) { + struct sandbox *s = sandbox_schedule(); + while (s) { + sandbox_switch(s); + s = sandbox_schedule(); + } + } + + *(int *)data = -1; + pthread_exit(data); +} + +void +sandbox_run(struct sandbox *s) +{ + // for now, a pull model... + // sandbox_run adds to the global ready queue.. + // each sandboxing thread pulls off of that global ready queue.. + debuglog("[%p: %s]\n", s, s->mod->name); + s->state = SANDBOX_RUNNABLE; + pthread_mutex_lock(&glbq_mtx); + ps_list_head_append_d(&glbq, s); + pthread_mutex_unlock(&glbq_mtx); +} + +// perhaps respond to request +void +sandbox_exit(void) +{ + struct sandbox *curr = sandbox_current(); + + assert(curr); + + debuglog("[%p: %s]\n", curr, curr->mod->name); + sandbox_local_stop(curr); + curr->state = SANDBOX_RETURNED; + // TODO: free resources here? or only from main? + sandbox_switch(sandbox_schedule()); +} + +void * +runtime_uvio_thdfn(void *d) +{ + assert(d == (void *)uv_default_loop()); + while (1) { + // runs until there are no events.. + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + pthread_yield(); + } + + assert(0); + + return NULL; +} + +void +runtime_init(void) +{ + ps_list_head_init(&glbq); + + softint_mask(SIGUSR1); + softint_mask(SIGALRM); + cpu_set_t cs; + + CPU_ZERO(&cs); + CPU_SET(MOD_REQ_CORE, &cs); + + pthread_t iothd; + int ret = pthread_create(&iothd, NULL, runtime_uvio_thdfn, (void *)uv_default_loop()); + assert(ret == 0); + ret = pthread_setaffinity_np(iothd, sizeof(cpu_set_t), &cs); + assert(ret == 0); + ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs); + assert(ret == 0); + + softint_init(); + softint_timer_arm(); +} diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c new file mode 100644 index 0000000..a254050 --- /dev/null +++ b/runtime/src/sandbox.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include + +static inline void +sandbox_args_setup(i32 argc) +{ + struct sandbox *curr = sandbox_current(); + char *args = sandbox_args(); + + // whatever gregor has, to be able to pass args to a module! + curr->args_offset = sandbox_lmbound; + assert(sandbox_lmbase == curr->linear_start); + expand_memory(); + + i32 *array_ptr = get_memory_ptr_void(curr->args_offset, argc * sizeof(i32)); + i32 string_off = curr->args_offset + (argc * sizeof(i32)); + + for (int i = 0; i < argc; i++) { + char *arg = args + (i * MOD_ARG_MAX_SZ); + size_t str_sz = strlen(arg) + 1; + + array_ptr[i] = string_off; + // why get_memory_ptr_for_runtime?? + strncpy(get_memory_ptr_for_runtime(string_off, strlen(arg) + 1), arg, strlen(arg)); + + string_off += str_sz; + } +} + +void +sandbox_entry(void) +{ + struct sandbox *curr = sandbox_current(); + // FIXME: is this right? this is the first time this sandbox is running.. so it wont + // return to sandbox_switch() api.. + // we'd potentially do what we'd in sandbox_switch() api here for cleanup.. + if (!softint_enabled()) { + arch_context_init(&curr->ctxt, 0, 0); + next_context = NULL; + softint_enable(); + } + struct module *curr_mod = sandbox_module(curr); + int argc = module_nargs(curr_mod); + + // for stdio + int f = io_handle_open(0); + assert(f == 0); + f = io_handle_open(1); + assert(f == 1); + f = io_handle_open(2); + assert(f == 2); + + alloc_linear_memory(); + + // perhaps only initialized for the first instance? or TODO! + //module_table_init(curr_mod); + module_memory_init(curr_mod); + + sandbox_args_setup(argc); + + curr->retval = module_entry(curr_mod, argc, curr->args_offset); + + sandbox_exit(); +} + +struct sandbox * +sandbox_alloc(struct module *mod, char *args) +{ + if (!module_is_valid(mod)) return NULL; + + // FIXME: don't use malloc. huge security problem! + // perhaps, main should be in its own sandbox, when it is not running any sandbox. + struct sandbox *sb = (struct sandbox *)malloc(sizeof(struct sandbox)); + + if (!sb) return NULL; + + memset(sb, 0, sizeof(struct sandbox)); + //actual module instantiation! + sb->mod = mod; + module_acquire(mod); + sb->args = (void *)args; + sb->stack_size = mod->stack_size; + sb->stack_start = mmap(NULL, sb->stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if (sb->stack_start == MAP_FAILED) { + perror("mmap"); + assert(0); + } + for (int i = 0; i < SBOX_MAX_OPEN; i++) sb->handles[i].fd = -1; + ps_list_init_d(sb); + + arch_context_init(&sb->ctxt, (reg_t)sandbox_entry, (reg_t)(sb->stack_start + sb->stack_size)); + sandbox_run(sb); + + return sb; +} + +void +sandbox_free(struct sandbox *sb) +{ + // you have to context switch away to free a sandbox. + if (!sb || sb == sandbox_current()) return; + + // again sandbox should be done and waiting for the parent. + // TODO: this needs to be enhanced. you may be killing a sandbox when its in any other execution states. + if (sb->state != SANDBOX_RETURNED) return; + module_release(sb->mod); + + free(sb->args); + // remove stack! and also heap! + int ret = munmap(sb->stack_start, sb->stack_size); + if (ret) perror("munmap"); + + // depending on the memory type + free_linear_memory(sb->linear_start, sb->linear_size, sb->linear_max_size); +} diff --git a/runtime/src/softint.c b/runtime/src/softint.c new file mode 100644 index 0000000..f087ac4 --- /dev/null +++ b/runtime/src/softint.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +__thread static volatile sig_atomic_t alarm_cnt = 0, usr1_cnt = 0; + +__thread volatile sig_atomic_t softint_off = 0; + +static const int softints[] = { + SIGALRM, + SIGUSR1 + }; + +void +softint_timer_arm(void) +{ +#ifndef PREEMPT_DISABLE + struct itimerval it; + + memset(&it, 0, sizeof(struct itimerval)); + it.it_value.tv_usec = SOFTINT_TIMER_START_USEC; + it.it_interval.tv_usec = SOFTINT_TIMER_PERIOD_USEC; + + int ret = setitimer(ITIMER_REAL, &it, NULL); + if (ret) { + perror("setitimer"); + exit(1); + } +#endif +} + +void +softint_timer_disarm(void) +{ + struct itimerval it; + + memset(&it, 0, sizeof(struct itimerval)); + it.it_value.tv_sec = 0; + it.it_interval.tv_usec = 0; + + int ret = setitimer(ITIMER_REAL, &it, NULL); + if (ret) { + perror("setitimer"); + exit(1); + } +} + +static inline void +softint_handler(int sig, siginfo_t *si, void *u) +{ +#ifdef PREEMPT_DISABLE + assert(0); +#else + struct sandbox *curr = sandbox_current(); + ucontext_t *uc = (ucontext_t *)u; + + switch(sig) { + case SIGALRM: + { + // if interrupts are disabled.. increment a per_thread counter and return + if (si->si_code == SI_KERNEL) { + // TODO: deliver signal to all other runtime threads.. + } else { + assert(si->si_code == SI_TKILL); + } + //debuglog("alrm:%d\n", alarm_cnt); + + alarm_cnt++; + // softints per-core.. + if (next_context) return; + if (!softint_enabled()) return; + + // no sandboxes running..so nothing to preempt..let the "main" scheduler run its course. + if (curr == NULL) return; + + // find a next sandbox to run.. + struct sandbox *next = sandbox_schedule(); + if (next == NULL) return; + if (next == curr) return; // only this sandbox to schedule.. return to it! + // save the current sandbox, state from uc! + arch_mcontext_save(&curr->ctxt, &uc->uc_mcontext); + + // sandbox_current_set on it. restore through *uc.. + sandbox_current_set(next); + + arch_mcontext_restore(&uc->uc_mcontext, &next->ctxt); + // reset if SIGALRM happens before SIGUSR1 and if don't preempt..OR + // perhaps switch here for SIGUSR1 and see if we can clear that signal + // so it doesn't get called on SIGALRM return.. + // next_context = NULL; + + break; + } + case SIGUSR1: + { + /* we set current before calling pthread_kill! */ + assert(next_context && (&curr->ctxt == next_context)); + assert(si->si_code == SI_TKILL); + //debuglog("usr1:%d\n", usr1_cnt); + + usr1_cnt++; + // sigalrm happened.. such a waste of time..!! + if (next_context == NULL) return; + // make sure sigalrm doesn't mess this up if nested.. + assert(!softint_enabled()); + + // do not save current sandbox.. it is in co-operative switch.. + // pick the next from "next_context".. + // assert its "sp" to be zero in regs.. + // memcpy from next context.. + arch_mcontext_restore(&uc->uc_mcontext, &curr->ctxt); + next_context = NULL; + softint_enable(); + break; + } + case SIGPIPE: + case SIGILL: + case SIGFPE: + case SIGSEGV: + { + // determine if the crash was in the sandbox.. + // if (pthread_self() == one_of_the_runtime_threads), a sandbox crashed.. kill it. + // another check there could be if it is in linear memory or outside, if outside it could repeat with other sandboxes.. so perhaps restart that thread or start a fresh thread??. + // else, shoot yourself in the head!.. + break; + } + default: break; + } +#endif +} + +void +softint_init(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction = softint_handler; + sa.sa_flags = SA_SIGINFO | SA_RESTART; + + for (int i = 0; i < (sizeof(softints) / sizeof(softints[0])); i++) { + int ret = sigaction(softints[i], &sa, NULL); + if (ret) { + perror("sigaction"); + exit(1); + } + } +} diff --git a/runtime/src/util.c b/runtime/src/util.c new file mode 100644 index 0000000..797272d --- /dev/null +++ b/runtime/src/util.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UTIL_MOD_LINE_MAX 1024 + +static char * +util_remove_spaces(char *str) +{ + int i = 0; + while (isspace(*str)) str++; + i = strlen(str); + while (isspace(str[i-1])) str[i-1]='\0'; + + return str; +} + + +int +util_parse_modules_file_json(char *filename) +{ + struct stat sb; + memset(&sb, 0, sizeof(struct stat)); + if (stat(filename, &sb) < 0) { + perror("stat"); + return -1; + } + + FILE *mf = fopen(filename, "r"); + if (!mf) { + perror("fopen"); + return -1; + } + + char *filebuf = malloc(sb.st_size); + memset(filebuf, 0, sb.st_size); + int ret = fread(filebuf, sizeof(char), sb.st_size, mf); + debuglog("size read: %d content: %s\n", ret, filebuf); + if (ret != sb.st_size) { + perror("fread"); + return -1; + } + fclose(mf); + + jsmn_parser modp; + jsmn_init(&modp); + jsmntok_t toks[MOD_MAX * JSON_ELE_MAX]; + + int r = jsmn_parse(&modp, filebuf, strlen(filebuf), toks, sizeof(toks) / sizeof(toks[0])); + if (r < 0) { + debuglog("jsmn_parse: invalid JSON?\n"); + return -1; + } + + int nmods = 0; + for (int i = 0; i < r; i++) { + assert(toks[i].type == JSMN_OBJECT); + + char mname[MOD_NAME_MAX] = { 0 }; + char mpath[MOD_PATH_MAX] = { 0 }; + i32 nargs = 0; + u32 udp_port = 0; + i32 isactive = 0; + for (int j = 1; j < (toks[i].size * 2); j+=2) { + char val[256] = { 0 }, key[32] = { 0 }; + + sprintf(val, "%.*s", toks[j + i + 1].end - toks[j + i + 1].start, filebuf + toks[j + i + 1].start); + sprintf(key, "%.*s", toks[j + i].end - toks[j + i].start, filebuf + toks[j + i].start); + if (strcmp(key, "name") == 0) { + strcpy(mname, val); + } else if (strcmp(key, "path") == 0) { + strcpy(mpath, val); + } else if (strcmp(key, "port") == 0) { + udp_port = atoi(val); + } else if (strcmp(key, "argsize") == 0) { + nargs = atoi(val); + } else if (strcmp(key, "active") == 0) { + isactive = (strcmp(val, "yes") == 0); + } else { + debuglog("Invalid (%s,%s)\n", key, val); + } + } + i += (toks[i].size * 2); + // do not load if it is not active + if (isactive == 0) continue; + + struct module *m = module_alloc(mname, mpath, udp_port, nargs, 0, 0, 0, 0); + assert(m); + nmods++; + } + + free(filebuf); + assert(nmods); + debuglog("Loaded %d module%s!\n", nmods, nmods > 1 ? "s" : ""); + + return 0; +} + +/* + * TEST data file should contain: + * module_name: + * and if your arg has to contain a ',', woops i can't deal with that for now! + * if the first character in a line is ";", then the line is ignored! + */ +int +parse_sandbox_file_custom(char *filename) +{ + FILE *mf = fopen(filename, "r"); + char buff[UTIL_MOD_LINE_MAX] = { 0 }; + int total_boxes = 0; + + if (!mf) { + perror("fopen"); + + return -1; + } + + while (fgets(buff, UTIL_MOD_LINE_MAX, mf) != NULL) { + char mname[MOD_NAME_MAX] = { 0 }; + char *tok = NULL, *src = buff; + struct module *mod = NULL; + struct sandbox *sb = NULL; + char *args = NULL; + + src = util_remove_spaces(src); + if (src[0] == ';') goto next; + + if ((tok = strtok_r(src, ":", &src))) { + int ntoks = 0; + strncpy(mname, tok, MOD_NAME_MAX); + + mod = module_find(mname); + assert(mod); + if (mod->nargs > 0) { + args = (char *)malloc(mod->nargs * MOD_ARG_MAX_SZ); + assert(args); + + while ((tok = strtok_r(src, ",", &src))) { + strncpy(args + (ntoks * MOD_ARG_MAX_SZ), tok, MOD_ARG_MAX_SZ); + ntoks++; + + assert(ntoks < MOD_MAX_ARGS); + } + } + } else { + assert(0); + } + + sb = sandbox_alloc(mod, args); + assert(sb); + total_boxes++; + +next: + memset(buff, 0, UTIL_MOD_LINE_MAX); + } + + assert(total_boxes); + debuglog("Instantiated %d sandbox%s!\n", total_boxes, total_boxes > 1 ? "es" : ""); + + return 0; +} + + +struct sandbox * +util_parse_sandbox_string_json(struct module *mod, char *str) +{ + jsmn_parser sp; + jsmntok_t tk[JSON_ELE_MAX]; + jsmn_init(&sp); + + int r = jsmn_parse(&sp, str, strlen(str), tk, sizeof(tk)/sizeof(tk[0])); + if (r < 1) { + debuglog("Failed to parse string:%s\n", str); + return NULL; + } + + if (tk[0].type != JSMN_OBJECT) return NULL; + + for (int j = 1; j < r; j++) { + char key[32] = { 0 }; + sprintf(key, "%.*s", tk[j].end - tk[j].start, str + tk[j].start); + if (strcmp(key, "module") == 0) { + char name[32] = { 0 }; + sprintf(name, "%.*s", tk[j + 1].end - tk[j + 1].start, str + tk[j + 1].start); + if (strcmp(name, mod->name) != 0) return NULL; + j++; + } else if (strcmp(key, "args") == 0) { + if (tk[j + 1].type != JSMN_ARRAY) return NULL; + + char *args = malloc(tk[j + 1].size * MOD_ARG_MAX_SZ); + assert(args); + + for (int k = 1; k <= tk[j + 1].size; k++) { + jsmntok_t *g = &tk[j + k + 1]; + strncpy(args + ((k - 1) * MOD_ARG_MAX_SZ), str + g->start, g->end - g->start); + *(args + ((k - 1) * MOD_ARG_MAX_SZ) + g->end - g->start) = '\0'; + } + + struct sandbox *sb = sandbox_alloc(mod, args); + assert(sb); + + return sb; + } else { + return NULL; + } + } + + return NULL; +} + +struct sandbox * +util_parse_sandbox_string_custom(struct module *mod, char *str) +{ + char *tok = NULL, *src = str; + + src = util_remove_spaces(src); + if (src[0] == ';') return NULL; + + if (!(tok = strtok_r(src, ":", &src))) return NULL; + + if (strcmp(mod->name, tok)) return NULL; + // struct module *mod = module_find(tok); + // if (!mod) return NULL; + assert(mod->nargs >= 0 && mod->nargs < MOD_MAX_ARGS); + + char *args = (char *)malloc(mod->nargs * MOD_ARG_MAX_SZ); + assert(args); + int ntoks = 0; + while ((tok = strtok_r(src, ",", &src))) { + strncpy(args + (ntoks * MOD_ARG_MAX_SZ), tok, MOD_ARG_MAX_SZ); + ntoks++; + assert(ntoks < MOD_MAX_ARGS); + } + + struct sandbox *sb = sandbox_alloc(mod, args); + assert(sb); + + return sb; +} + +/* + * Each line in the file should be like: + * + * module_path:module_name:module_nargs:module_stack_size:module_max_heap_size[:moreargs::argn\n] + * if the first character in a line is ";", then the line is ignored! + */ +int +util_parse_modules_file_custom(char *filename) +{ + FILE *mf = fopen(filename, "r"); + char buff[UTIL_MOD_LINE_MAX] = { 0 }; + int nmods = 0; + + if (!mf) { + perror("fopen"); + + return -1; + } + + while (fgets(buff, UTIL_MOD_LINE_MAX, mf) != NULL) { + char mname[MOD_NAME_MAX] = { 0 }; + char mpath[MOD_PATH_MAX] = { 0 }; + i32 nargs = 0; + u32 stack_sz = 0; + u32 max_heap = 0; + u32 timeout = 0; + char *tok = NULL, *src = buff; + u32 udp_port = 0; + i32 ntoks = 0; + + src = util_remove_spaces(src); + if (src[0] == ';') goto next; + while ((tok = strtok_r(src, ":", &src))) { + switch(ntoks) { + case MOD_ARG_MODPATH: strncpy(mpath, tok, MOD_PATH_MAX); break; + case MOD_ARG_MODPORT: udp_port = atoi(tok); + case MOD_ARG_MODNAME: strncpy(mname, tok, MOD_NAME_MAX); break; + case MOD_ARG_MODNARGS: nargs = atoi(tok); break; + default: break; + } + ntoks++; + } + assert(ntoks >= MOD_ARG_MAX); + + struct module *m = module_alloc(mname, mpath, udp_port, nargs, 0, 0, 0, 0); + assert(m); + nmods++; + +next: + memset(buff, 0, UTIL_MOD_LINE_MAX); + } + + assert(nmods); + debuglog("Loaded %d module%s!\n", nmods, nmods > 1 ? "s" : ""); + fclose(mf); + + return 0; +} + diff --git a/runtime/tests/Makefile b/runtime/tests/Makefile new file mode 100644 index 0000000..966fa4c --- /dev/null +++ b/runtime/tests/Makefile @@ -0,0 +1,51 @@ +include Makefile.inc + +BENCH_DIR=../../silverfish/code_benches/ + +TESTS=forever filesys sockserver sockclient +TESTSRT=$(TESTS:%=%_rt) +BENCHES=adpcm basic_math binarytrees bitcount blowfish crc dijkstra fft function_pointers \ + gsm libjpeg mandelbrot patricia pgp qsort rsynth sha sqlite stringsearch susan +BENCHESSF=$(BENCHES:%=%_sf) + +adpcm_CFLAGS=-Wno-implicit-int -Wno-implicit-function-declaration +crc_CFLAGS=-Wno-implicit-int -Wno-format +dijkstra_CFLAGS=-Wno-return-type +gsm_CFLAGS=-DSASR -Wno-everything +libjpeg_CFLAGS=-Wno-incompatible-library-redeclaration -Wno-implicit-function-declaration -Wno-shift-negative-value +pgp_CFLAGS=-DUNIX -D_BSD -DPORTABLE -DUSE_NBIO -DMPORTABLE -I${BENCH_DIR}/pgp/ -Wno-everything +rsynth_CFLAGS=-I${BENCH_DIR}/rsynth -Wno-everything -I/usr/local/include +sha_CLFAGS=-Wno-everything +susan_CFLAGS=-Wno-everything + +.PHONY: all clean rttests sftests + +sftests: $(BENCHESSF) + +rttests: $(TESTSRT) + +all: rttests sftests + @echo "Compilation done!" + +clean: + @rm -rf ${TMP_DIR} + @rm -f ${BIN_DIR}/*_wasm.so + +%_sf: + @mkdir -p ${TMP_DIR} + @echo "Compiling $(@:%_sf=%)" + ${WASMCC} ${$(@:%_sf=%)_CFLAGS} ${WASMCFLAGS} ${OPTFLAGS} ${BENCH_DIR}/$(@:%_sf=%)/*.c -o ${TMP_DIR}/$(@:%_sf=%).wasm + ${SFCC} ${TMP_DIR}/$(@:%_sf=%).wasm -o ${TMP_DIR}/$(@:%_sf=%).bc + ${CC} --shared -fPIC ${OPTFLAGS} -I${RT_INC} -D${USE_MEM} ${TMP_DIR}/$(@:%_sf=%).bc ${MEMC} ${WASMISA} -o ${TMP_DIR}/$(@:%_sf=%)_wasm.so + @cp ${TMP_DIR}/$(@:%_sf=%)_wasm.so ${BIN_DIR} + @rm -rf ${TMP_DIR} + +%_rt: + @mkdir -p ${TMP_DIR} + @echo "Compiling $(@:%_rt=%)" + ${WASMCC} ${$(@:%_rt=%)_CFLAGS} ${WASMCFLAGS} ${OPTFLAGS} $(@:%_rt=%)/*.c -o ${TMP_DIR}/$(@:%_rt=%).wasm + ${SFCC} ${TMP_DIR}/$(@:%_rt=%).wasm -o ${TMP_DIR}/$(@:%_rt=%).bc + ${CC} --shared -fPIC ${OPTFLAGS} -I${RT_INC} -D${USE_MEM} ${TMP_DIR}/$(@:%_rt=%).bc ${MEMC} ${WASMISA} -o ${TMP_DIR}/$(@:%_rt=%)_wasm.so + @cp ${TMP_DIR}/$(@:%_rt=%)_wasm.so ${BIN_DIR} + @rm -rf ${TMP_DIR} + diff --git a/runtime/tests/Makefile.inc b/runtime/tests/Makefile.inc new file mode 100644 index 0000000..13b161e --- /dev/null +++ b/runtime/tests/Makefile.inc @@ -0,0 +1,22 @@ +BASE=awsm +SFCC=silverfish +CC=clang +WASMCC=wasm32-unknown-unknown-wasm-clang + +OPTFLAGS=-O3 -flto +#same stack-size for all +WASMLINKFLAGS=-Wl,-z,stack-size=524288,--allow-undefined,--no-threads,--stack-first,--no-entry,--export-all,--export=main,--export=dummy +WASMCFLAGS=${WASMLINKFLAGS} -nostartfiles + +BASE_DIR=../../ +RT_DIR=${BASE_DIR}/runtime/ +RT_INC=${RT_DIR}/include/ + +USE_MEM=USE_MEM_VM + +ifeq ($(USE_MEM),USE_MEM_VM) +MEMC=${RT_DIR}/compiletime/memory/64bit_nix.c +endif + +BIN_DIR=${RT_DIR}/bin/ +TMP_DIR=tmp/ diff --git a/runtime/tests/filesys/main.c b/runtime/tests/filesys/main.c new file mode 100644 index 0000000..1d5850d --- /dev/null +++ b/runtime/tests/filesys/main.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUF_MAX 44 +#define RDWR_MAX 1 +#if RDWR_MAX > 1 +int main(int argc, char **argv) __attribute__ ((optnone)) +{ + char buf[RDWR_MAX][BUF_MAX] = { 0 }; + + printf("%s enter [in:%s, out:%s]\n", argv[0], argv[1], argv[2]); + int fdr = open(argv[1], O_RDONLY, S_IRUSR | S_IRGRP); + if (fdr < 0) { + perror("fopen"); + return -1; + } + int fdw = creat(argv[2], S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (fdw < 0) { + perror("creat"); + return -1; + } + + int n = 0; + struct iovec iov[RDWR_MAX] = { 0 }; + for (int i = 0; i < RDWR_MAX; i++) { + iov[i].iov_base = buf[i]; + iov[i].iov_len = BUF_MAX; + } + while ((n = readv(fdr, iov, RDWR_MAX)) > 0) { + int wvcnt = n / BUF_MAX; + if (n % BUF_MAX) { + iov[wvcnt].iov_len = n % BUF_MAX; + wvcnt++; + } + if (writev(fdw, iov, wvcnt) != n) perror("writev"); + + memset(buf, 0, RDWR_MAX * BUF_MAX); + for (int i = 0; i < RDWR_MAX; i++) { + iov[i].iov_base = buf[i]; + iov[i].iov_len = BUF_MAX; + } + n = 0; + } + + close(fdr); + close(fdw); + + printf("%s done\n", argv[0]); + + return 0; +} + +#else +int +main(int argc, char **argv) __attribute__ ((optnone)) +{ + char buf[BUF_MAX] = { 0 }; + + printf("%s enter [in:%s, out:%s]\n", argv[0], argv[1], argv[2]); +#ifdef USE_OPEN + int fdr = open(argv[1], O_RDONLY, S_IRUSR | S_IRGRP); + if (fdr < 0) { + perror("fopen"); + return -1; + } + int fdw = creat(argv[2], S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (fdw < 0) { + perror("creat"); + return -1; + } + + int n = 0; + while ((n = read(fdr, buf, BUF_MAX)) > 0) { + if (write(fdw, buf, n) != n) perror("write"); + memset(buf, 0, BUF_MAX); + n = 0; + } + if (n < 0) perror("read"); + + close(fdr); + close(fdw); + +#else + FILE *fpr = fopen(argv[1], "r"); + if (!fpr) { + perror("fopen"); + return -1; + } + FILE *fpw = fopen(argv[2], "w"); + if (!fpw) { + perror("fopen"); + return -1; + } + + while (!feof(fpr)) { + char *p = NULL; + if ((p = fgets(buf, BUF_MAX, fpr)) == NULL) perror("fgets"); + else { + if (fputs(p, fpw) < 0) perror("fputs"); + p = NULL; + } + memset(buf, 0, BUF_MAX); + } + + fclose(fpr); + fclose(fpw); +#endif + printf("%s done\n", argv[0]); + + return 0; +} +#endif diff --git a/runtime/tests/forever/main.c b/runtime/tests/forever/main.c new file mode 100644 index 0000000..6f50a20 --- /dev/null +++ b/runtime/tests/forever/main.c @@ -0,0 +1,27 @@ +#include +#include +#include + +#define ITERS 50000000 + +int +main(int argc, char **argv) __attribute__ ((optnone)) +{ + printf("%s enter\n", argv[0]); + int n = 0, e = 1; + if (argc == 2) { + n = atoi(argv[1]); + if (n > 0) e = 0; + } + + while (e || n > 0) { + int i = ITERS; + n--; + while (i-- > 0) { + int j = ITERS; + while (j-- > 0) __asm__ __volatile__("nop": : :"memory"); + } + printf("%s\n", argv[0]); + } + printf("%s done\n", argv[0]); +} diff --git a/runtime/tests/sockclient/client.c b/runtime/tests/sockclient/client.c new file mode 100644 index 0000000..0a87562 --- /dev/null +++ b/runtime/tests/sockclient/client.c @@ -0,0 +1,55 @@ +/* code from http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html */ +#include +#include +#include +#include +#include +#include + +void error(char *msg) +{ + perror(msg); + exit(0); +} + +int main(int argc, char *argv[]) +{ + int sockfd, portno, n; + + struct sockaddr_in serv_addr; + struct hostent *server; + + char buffer[256] = "The quick brown fox jumps over the lazy dog"; + if (argc < 3) { + fprintf(stderr,"usage %s hostname port\n", argv[0]); + exit(0); + } + portno = atoi(argv[2]); + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + error("ERROR opening socket"); + server = gethostbyname(argv[1]); + if (server == NULL) { + fprintf(stderr,"ERROR, no such host\n"); + exit(0); + } + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, + (char *)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(portno); + if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) + error("ERROR connecting"); + printf("Sending message %s\n", buffer); + n = send(sockfd,buffer,strlen(buffer), 0); + if (n < 0) + error("ERROR writing to socket"); + bzero(buffer,256); + n = recv(sockfd,buffer,255, 0); + if (n < 0) + error("ERROR reading from socket"); + printf("%s\n",buffer); + close(sockfd); + return 0; +} diff --git a/runtime/tests/sockserver/server.c b/runtime/tests/sockserver/server.c new file mode 100644 index 0000000..6f22264 --- /dev/null +++ b/runtime/tests/sockserver/server.c @@ -0,0 +1,51 @@ +/* A simple server in the internet domain using TCP + The port number is passed as an argument */ +/* code from: http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html */ +#include +#include +#include +#include + +void error(char *msg) +{ + perror(msg); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int sockfd, newsockfd, portno, clilen; + char buffer[256]; + struct sockaddr_in serv_addr, cli_addr; + int n; + if (argc < 2) { + fprintf(stderr,"ERROR, no port provided\n"); + exit(1); + } + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + error("ERROR opening socket"); + bzero((char *) &serv_addr, sizeof(serv_addr)); + portno = atoi(argv[1]); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(portno); + if (bind(sockfd, (struct sockaddr *) &serv_addr, + sizeof(serv_addr)) < 0) + error("ERROR on binding"); + listen(sockfd,5); + clilen = sizeof(cli_addr); + newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); + if (newsockfd < 0) + error("ERROR on accept"); + bzero(buffer,256); + n = recv(newsockfd,buffer,255, 0); + if (n < 0) error("ERROR reading from socket"); + printf("Here is the message: %s\n",buffer); + n = send(newsockfd,"I got your message",18, 0); + if (n < 0) error("ERROR writing to socket"); + close(newsockfd); + close(sockfd); + + return 0; +} diff --git a/runtime/tests/test_modules.json b/runtime/tests/test_modules.json new file mode 100644 index 0000000..928c0ca --- /dev/null +++ b/runtime/tests/test_modules.json @@ -0,0 +1,133 @@ +{ + "active" : "no", + "name" : "adpcm", + "path" : "adpcm_wasm.so", + "port" : 10000, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "bitcount", + "path" : "bitcount_wasm.so", + "port" : 10002, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "basic_math", + "path" : "basic_math_wasm.so", + "port" : 10004, + "argsize" : 1 +}, +{ + "active" : "no", + "name" : "binarytrees", + "path" : "binarytrees_wasm.so", + "port" : 10006, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "crc", + "path" : "crc_wasm.so", + "port" : 10008, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "dijkstra", + "path" : "dijkstra_wasm.so", + "port" : 10010, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "forever", + "path" : "forever_wasm.so", + "port" : 10012, + "argsize" : 1 +}, +{ + "active" : "no", + "name" : "fornever", + "path" : "forever_wasm.so", + "port" : 10014, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "fft", + "path" : "fft_wasm.so", + "port" : 10016, + "argsize" : 3 +}, +{ + "active" : "no", + "name" : "function_pointers", + "path" : "function_pointers_wasm.so", + "port" : 10018, + "argsize" : 1 +}, +{ + "active" : "no", + "name" : "gsm", + "path" : "gsm_wasm.so", + "port" : 10020, + "argsize" : 4 +}, +{ + "active" : "no", + "name" : "libjpeg", + "path" : "libjpeg_wasm.so", + "port" : 10022, + "argsize" : 1 +}, +{ + "active" : "no", + "name" : "mandelbrot", + "path" : "mandelbrot_wasm.so", + "port" : 10024, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "matrix_multiply", + "path" : "matrix_multiply_wasm.so", + "port" : 10026, + "argsize" : 1 +}, +{ + "active" : "no", + "name" : "particia", + "path" : "partricia_wasm.so", + "port" : 10028, + "argsize" : 2 +}, +{ + "active" : "no", + "name" : "sqlite", + "path" : "sqlite_wasm.so", + "port" : 10030, + "argsize" : 1 +}, +{ + "active" : "no", + "name" : "filesys", + "path" : "filesys_wasm.so", + "port" : 10032, + "argsize" : 3 +}, +{ + "active" : "yes", + "name" : "sockserver", + "path" : "sockserver_wasm.so", + "port" : 10034, + "argsize" : 2 +}, +{ + "active" : "yes", + "name" : "sockclient", + "path" : "sockclient_wasm.so", + "port" : 10036, + "argsize" : 3 +} diff --git a/runtime/tests/test_modules.txt b/runtime/tests/test_modules.txt new file mode 100644 index 0000000..8d5c0d4 --- /dev/null +++ b/runtime/tests/test_modules.txt @@ -0,0 +1,16 @@ +adpcm_wasm.so:10000:adpcm:2:0:0:0:0 +bitcount_wasm.so:10002:bitcount:2:0:0:0:0 +basic_math_wasm.so:10004:basic_math:1:0:0:0:0 +binarytrees_wasm.so:10006:binarytrees:2:0:0:0:0 +crc_wasm.so:10008:crc:2:0:0:0:0 +;dijkstra_wasm.so:10010:dijkstra:2:0:0:0:0 +forever_wasm.so:10012:forever:1:0:0:0:0 +forever_wasm.so:10014:fornever:2:0:0:0:0 +;fft_wasm.so:10016:fft:3:0:0:0:0 +function_pointers_wasm.so:10018:function_pointers:1:0:0:0:0 +gsm_wasm.so:10020:gsm:4:0:0:0:0 +;libjpeg_wasm.so:10022:libjpeg:1:0:0:0:0 +mandelbrot_wasm.so:10024:mandelbrot:2:0:0:0:0 +;matrix_multiply_wasm.so:10026:matrix_multiply:1:0:0:0:0 +patricia_wasm.so:10028:patricia:2:0:0:0:0 +sqlite_wasm.so:10030:sqlite:1:0:0:0:0 diff --git a/runtime/tests/test_sandboxes.jsondata b/runtime/tests/test_sandboxes.jsondata new file mode 100644 index 0000000..498e73c --- /dev/null +++ b/runtime/tests/test_sandboxes.jsondata @@ -0,0 +1,34 @@ +;127.0.0.1:10002${ "module" : "bitcount", "args" : [ "bitcount1" , "16777216" ] } +;127.0.0.1:10002${ "module" : "bitcount", "args" : [ "bitcount2" , "16777216" ] } +;127.0.0.1:10004${ "module" : "basic_math", "args" : [ "basic_math1" ] } +;127.0.0.1:10004${ "module" : "basic_math", "args" : [ "basic_math2" ] } +;127.0.0.1:10006${ "module" : "binarytrees", "args" : [ "binarytrees1", "16" ] } +;127.0.0.1:10006${ "module" : "binarytrees", "args" : [ "binarytrees2", "16" ] } +;127.0.0.1:10008${ "module" : "crc", "args" : [ "crc1", "crc/large.pcm" ] } +;127.0.0.1:10008${ "module" : "crc", "args" : [ "crc2", "crc/large.pcm" ] } +;127.0.0.1:10010${ "module" : "dijkstra", "args" : [ "dijkstra1", "dijkstra/input.dat" ] } +;127.0.0.1:10010${ "module" : "dijkstra", "args" : [ "dijkstra2", "dijkstra/input.dat" ] } +;127.0.0.1:10012${ "module" : "forever", "args" : [ "forever01" ] } +;127.0.0.1:10012${ "module" : "forever", "args" : [ "forever02" ] } +;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever01", "10" ] } +;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever02", "20" ] } +;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever03", "30" ] } +;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever04", "40" ] } +;127.0.0.1:10016${ "module" : "fft", "args" : [ "fft1" , "8", "32768" ] } +;127.0.0.1:10016${ "module" : "fft", "args" : [ "fft2" , "8", "32768" ] } +;127.0.0.1:10018${ "module" : "function_pointers", "args" : [ "function_pointers1" ] } +;127.0.0.1:10018${ "module" : "function_pointers", "args" : [ "function_pointers2" ] } +;127.0.0.1:10020${ "module" : "gsm", "args" : [ "gsm1" , "-fps" , "-c", "gsm/large.au" ] } +;127.0.0.1:10020${ "module" : "gsm", "args" : [ "gsm2" , "-fps" , "-c", "gsm/large.au" ] } +;127.0.0.1:10022${ "module" : "libjpeg", "args" : [ "libjpeg1" ] } +;127.0.0.1:10022${ "module" : "libjpeg", "args" : [ "libjpeg2" ] } +;127.0.0.1:10024${ "module" : "mandelbrot", "args" : [ "mandelbrot1", "5000" ] } +;127.0.0.1:10024${ "module" : "mandelbrot", "args" : [ "mandelbrot2", "5000" ] } +;127.0.0.1:10026${ "module" : "matrix_multiply", "args" : [ "matrix_multiply1" ] } +;127.0.0.1:10026${ "module" : "matrix_multiply", "args" : [ "matrix_multiply2" ] } +;127.0.0.1:10028${ "module" : "patricia", "args" : [ "patricia1" , "large.udp" ] } +;127.0.0.1:10030${ "module" : "sqlite", "args" : [ "sqlite1" ] } +;127.0.0.1:10030${ "module" : "sqlite", "args" : [ "sqlite2" ] } +;127.0.0.1:10032${ "module" : "filesys", "args" : [ "filesys1", "fs_in.txt", "fs_out.txt" ] } +127.0.0.1:10034${ "module" : "sockserver", "args" : [ "sockserv1", "20000" ] } +127.0.0.1:10036${ "module" : "sockclient", "args" : [ "sockcli1", "localhost", "20000" ] } diff --git a/runtime/tests/test_sandboxes.txt b/runtime/tests/test_sandboxes.txt new file mode 100644 index 0000000..98625ca --- /dev/null +++ b/runtime/tests/test_sandboxes.txt @@ -0,0 +1,33 @@ +10002$bitcount:bitcount1,16777216 +10002$bitcount:bitcount2,16777216 +10004$basic_math:basic_math1 +10004$basic_math:basic_math2 +10006$binarytrees:binarytrees1,16 +10006$binarytrees:binarytrees2,16 +10008$crc:crc1,crc/large.pcm +10008$crc:crc2,crc/large.pcm +;10010$dijkstra:dijkstra1,dijkstra/input.dat +;10010$dijkstra:dijkstra2,dijkstra/input.dat +;10012$forever:forever01 +;10012$forever:forever02 +10014$fornever:fornever01,10 +10014$fornever:fornever02,20 +;10012$forever:forever03 +;10012$forever:forever04 +10014$fornever:fornever03,30 +10014$fornever:fornever04,40 +;10016$fft:fft1,8,32768 +;10016$fft:fft2,8,32768 +;10018$function_pointers:function_pointers1 +;10018$function_pointers:function_pointers2 +;10020$gsm:gsm1,-fps,-c,gsm/large.au +;10020$gsm:gsm2,-fps,-c,gsm/large.au +;10022$libjpeg:libjpeg1 +;10022$libjpeg:libjpeg2 +;10024$mandelbrot:mandelbrot1,5000 +;10024$mandelbrot:mandelbrot2,5000 +;10026$matrix_multiply:matrix_multiply1 +;10026$matrix_multiply:matrix_multiply2 +;10028$patricia:patricia1,large.udp +;10030$sqlite:sqlite1 +;10030$sqlite:sqlite2 diff --git a/runtime/tools/Makefile b/runtime/tools/Makefile new file mode 100644 index 0000000..0d77917 --- /dev/null +++ b/runtime/tools/Makefile @@ -0,0 +1,13 @@ +SUBDIRS=$(wildcard */) + +dirs: + @for dir in $(SUBDIRS) ; do \ + $(MAKE) -C $$dir ; \ + done + +clean: + @for dir in $(SUBDIRS) ; do \ + $(MAKE) -C $$dir clean ; \ + done + +all: clean dirs diff --git a/runtime/tools/udpclient/Makefile b/runtime/tools/udpclient/Makefile new file mode 100644 index 0000000..ed447f5 --- /dev/null +++ b/runtime/tools/udpclient/Makefile @@ -0,0 +1,9 @@ +udp: udpclient.c + @echo "Compiling udpclient" + @gcc udpclient.c -o ../../bin/udpclient + +clean: + @echo "Cleaning up udpclient" + @rm -f ../../bin/udpclient + +all: clean udp diff --git a/runtime/tools/udpclient/udpclient.c b/runtime/tools/udpclient/udpclient.c new file mode 100644 index 0000000..9eb9cc8 --- /dev/null +++ b/runtime/tools/udpclient/udpclient.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MSG_MAX 1024 + +static char * +remove_spaces(char *str) +{ + int i = 0; + while (isspace(*str)) str++; + i = strlen(str); + while (isspace(str[i-1])) str[i-1]='\0'; + + return str; +} + +int main(int argc, char *argv[]) +{ + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + FILE *f = fopen(argv[1], "r"); + if (!f) { + perror("fopen"); + return -1; + } + + while (1) { + fseek(f, 0, SEEK_SET); + + char line[MSG_MAX] = { 0 }; + while (fgets(line, MSG_MAX, f) != NULL) { + int fd = -1; + struct sockaddr_in sa; + char *msg = NULL, *tok, *src = line; + char ip[32] = { 0 }, port[32] = { 0 }; + src = remove_spaces(src); + + if (src[0] == ';') goto next; + tok = strtok_r(src, ":", &src); + strcpy(ip, tok); + tok = strtok_r(src, "$", &src); + strcpy(port, tok); + msg = src; + + int i = 0; + printf("\nRequest [%s] to [%s:%d]\n (1:Proceed 2:Skip ANY:Exit) ", msg, ip, atoi(port)); + scanf("%d", &i); + if (i <= 0 || i > 2) { + printf("Exiting!\n"); + exit(0); + } else if (i == 1) { + printf("Proceeding!\n"); + } else { + printf("Skipping!\n"); + goto next; + } + + sa.sin_family = AF_INET; + sa.sin_port = htons(atoi(port)); + sa.sin_addr.s_addr = inet_addr(ip); + if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { + perror("Establishing socket"); + return -1; + } + + if (sendto(fd, msg, strlen(msg), 0, (struct sockaddr*)&sa, sizeof(sa)) < 0 && + errno != EINTR) { + perror("sendto"); + return -1; + } + printf("Done!\n"); +next: + memset(line, 0, MSG_MAX); + fflush(stdin); + fflush(stdout); + + if (fd >= 0) close(fd); + } + } + + fclose(f); + + return 0; +} diff --git a/silverfish b/silverfish new file mode 160000 index 0000000..015e2bb --- /dev/null +++ b/silverfish @@ -0,0 +1 @@ +Subproject commit 015e2bb638d47a793d0da96bb4b697be9d010209