Coder Social home page Coder Social logo

kanaka / wac Goto Github PK

View Code? Open in Web Editor NEW
464.0 23.0 44.0 202 KB

WebAssembly interpreter in C

License: Other

Makefile 1.57% Python 9.97% C 86.67% Dockerfile 0.71% WebAssembly 1.08%
wasm webassembly emscripten binaryen docker-image c native javascript mvp repl wasm-modules gcc

wac's Introduction

wac - WebAssembly in C

A Minimal WebAssembly interpreter written in C. Supports the WebAssembly MVP (minimum viable product) version of the WebAssembly specification.

There are three different builds of wac:

  • wac: (WebAssembly in C) Minimal client with an interactive REPL mode. Designed to run standalone wasm files compiled with wat2wasm or wasm-as. Passes most spec tests apart from some multi-module import/export tests.
  • wax: (WebAssembly in C with WASI) Client with WebAssembly System Interface APIs (WASI).
  • wace: (WebAssembly in C with Emscripten) Client with host library/memory integration. Designed to run wasm code that has been built with Emscripten (using -s SIDE_MODULE=1 -s LEGALIZE_JS_FFI=0).

Prerequisites

To build wac/wax/wace you need a 32-bit version of gcc and 32-bit versions of SDL2 and libedit. On 64-bit Ubuntu/Debian these can be installed like this:

dpkg --add-architecture i386
apt-get update
apt-get install lib32gcc-4.9-dev libSDL2-dev:i386 libedit-dev:i386

To compile wat source files to binary wasm modules you will need the wasm-as program from Binaryen. To compile C programs to wasm modules you will need Binaryen and emscripten.

As an alternative to downloading and building the above tools, the kanaka/wac docker image (1.7GB) has 32-bit gcc compiler/libraries, emscripten, and binaryen preinstalled. The docker image can be started with appropriate file mounts like this:

docker run -v `pwd`:/wac -w /wac -it kanaka/wac bash

All the build commands below can be run within the docker container.

wac usage

Build wac:

$ make wac

Use wasm-as to compile a simple wat program to a wasm:

$ make examples_wat/arith.wasm

Now load the compiled wasm file and invoke some functions:

$ ./wac examples_wat/arith.wasm add 2 3
0x5:i32
$ ./wac examples_wat/arith.wasm mul 7 8
0x38:i32

wac also supports a very simple REPL (read-eval-print-loop) mode that runs commands in the form of FUNC ARG...:

$ ./wac --repl examples_wat/arith.wasm
> sub 10 5
0x5:i32
> div 13 4
0x3:i32

wax usage

Build wax:

$ make wax

Use wasm-as to compile a wat program that uses the WASI interface:

$ make examples_wat/echo.wasm

Now run the compiled wasm file. The program reads text from stdin and echos it to stdout until an EOF (Ctrl-D) is sent.

$ ./wax examples_wat/echo.wasm
> foo
foo
> bar
bar
> <Ctrl-D>

wace usage

Build wace:

$ make wace

Use emscripten/binaryen to compile some simple C programs and run them using wace:

$ make examples_c/hello1.wasm
$ ./wace examples_c/hello1.wasm
hello world

$ make examples_c/hello2.wasm
$ ./wace examples_c/hello2.wasm
hello malloc people

Use emscripten/binaryen to compile some C SDL programs and run them using wace:

$ make examples_c/hello_sdl.wasm
$ ./wace examples_c/hello_sdl.wasm
INFO: OpenGL shaders: ENABLED
INFO: Created renderer: opengl
# Blue Window displayed for 2 seconds
Done.

$ make examples_c/triangle.wasm
$ ./wace examples_c/triangle.wasm
# A colorfully shaded triangle is rendered

Running WebAssembly spec tests

wac includes a runtest.py test driver which can be used for running tests from the WebAssembly specification.

Check out the spec:

git clone https://github.com/WebAssembly/spec

You will need wat2wasm to compile the spec tests. Check-out and build wabt (wabbit):

git clone --recursive https://github.com/WebAssembly/wabt
make -C wabt gcc-release

Run the func.wast test file (to test function calls) from the spec:

./runtest.py --wat2wasm ./wabt/out/gcc/Release/wat2wasm --interpreter ./wac spec/test/core/func.wast

Run all the spec tests apart from a few that currently fail (mostly due to runtest.py missing support for some syntax used in those test files):

BROKE_TESTS="comments exports imports linking names data elem inline-module"
for t in $(ls spec/test/core/*.wast | grep -Fv "${BROKE_TESTS// /$'\n'}"); do
    echo -e "\nTESTING ${t}"
    ./runtest.py ${t} || break
done

Standalone Builds using Fooboot

wac and wace can be built to run as standalone bootable programs using fooboot:

cd wac
git clone https://github.com/kanaka/fooboot
make PLATFORM=fooboot clean
make PLATFORM=fooboot wac wace examples_wat/addTwo.wasm

The fooboot/runfoo script can be used to boot wac/wace with QEMU. fooboot/runfoo also creates a connection on a serial port (COM2) that allows files to be read from the host system:

fooboot/runfoo wac --repl examples_wat/addTwo.wasm
QEMU waiting for connection on: disconnected:tcp:localhost:21118,server
webassembly> addTwo 2 3
0x5:i32

The standalone wac/wace builds can also be built into an ISO image that can boot directly on real hardware. You will need Grub 2 and the Grub PC/BIOS binary files (grub-pc-bin) and the xorriso program to be able to do this. Also, the wasm modules that you wish to run must be built into the binary to become part of a simple in-memory file-system:

echo "examples_wat/addTwo.wasm" > mem_fs_files
make PLATFORM=fooboot \
     FOO_TARGETS="wac" \
     FOO_CMDLINE="examples_wat/addTwo.wasm addTwo 3 4" \
     boot.iso

You can now boot the ISO with QEMU like this:

qemu-system-i386 -cdrom boot.iso

Or you can burn the ISO to a USB device and boot from it on real hardware. This will destroy any data on the USB device! Also, make completely sure that /dev/MY_USB_DEVICE is really the USB device you want to overwrite and not your hard drive. You have been warned!

sudo dd if=boot.iso of=/dev/MY_USB_DEVICE && sync
# Now boot you can boot from the USB device

License

MPL-2.0 (see LICENSE).

wac's People

Contributors

badboy avatar geky avatar kanaka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wac's Issues

performance?

Hello Kanaka.
I'm inquiring about wac's performance.

Does wac also use a JIT like wasm? If not, how performant is wac as a bytecode interpreter?

Thank You
~Kevin.

Use wast2json instead for generating tests?

wabt provides the wast2json tool to parse the wast files. It then generates a .json file, and a collection of .wasm files for each module. This might be simpler than parsing yourself in runtest.py.

Dynamic memory allocation and sandboxing

Hi there,

thank you for sharing your WebAssembly interpreter.
I'm planning to embed WebAssembly in a C++ program so your implementation seems like an excellent starting point.

If I understood the approach correctly, you directly expose C symbols as host imports to the WebAssembly client. This is great for quickly exposing third party libraries like SDL, but what about commands like malloc? Won't these allocate memory in the host's address space rather than the client's linear memory? This does not go well with my idea of isolating the client from the host :)

To completely sandbox the client, I guess it would be necessary to wrap any exported host functions, mapping client to host pointers, and write custom memory management routines.

Do you have any thoughts on this topic?
Do you have further plans with this project, and would you be interested in pull requests in case my fiddling produces something useful?

Cheers,
Martin

-Wformat error

platform_libc.c:18:53: error: format specifies type 'int' but the argument has type 'unsigned long' [-Werror,-Wformat]
        FATAL("Could not allocate %d bytes for %s", (int)nmemb * size, name);
                                  ~~                ^~~~~~~~~~~~~~~~~
                                  %lu
./util.h:20:21: note: expanded from macro 'FATAL'
    fprintf(stderr, __VA_ARGS__); exit(1); \
                    ^~~~~~~~~~~
platform_libc.c:28:53: error: format specifies type 'int' but the argument has type 'unsigned long' [-Werror,-Wformat]
        FATAL("Could not allocate %d bytes for %s", (int)nmemb * size, name);
                                  ~~                ^~~~~~~~~~~~~~~~~
                                  %lu
./util.h:20:21: note: expanded from macro 'FATAL'
    fprintf(stderr, __VA_ARGS__); exit(1); \
                    ^~~~~~~~~~~
2 errors generated.
make: *** [platform_libc.o] Error 1

I can fix the Makefile on my own but you may want to fix this. :)

cannot pull kanaka/emscripten from docker hub

I have a bad feeling this is something dumb I'm doing,
but I cannot pull the docker the image.
Things I have tried:
setting the time with ntpdate
reconfiguring tzdata (it threw my clock off by 10min?)
docker pull kanaka/emscripten
sudo docker pull kanaka/emscripten
creating a docker hub account
docker login followed by pull
sudo docker login followed by pull
su to root, docker login, docker pull.

every single time, the downloads finish, but I get 'authentication required'.
I believe my Docker installation is correct- it's from the Jessie repos-
and pulling other images just works:

$ docker pull mono
Using default tag: latest
latest: Pulling from library/mono
5233d9aed181: Already exists 
666418377e5b: Pull complete 
75a87e0601e5: Pull complete 
8e3dafbe502d: Pull complete 
Digest: sha256:32b9d2580388688b8d57742e659f2d021d3d90f036141b1fee81ded64062dd9e
Status: Downloaded newer image for mono:latest

heap-buffer-overflow in setup_call

Version

385e13c

Compile

CFLAGS="-g -fsanitize=address" make

ASAN Report

root@9dc6ce043bcb:~/Ablation/wasm-fuzz/fuzz_out_wac/crashes# ./wace id:000207,sig:11,src:001719,op:python,pos:0
=================================================================
==6667==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf46029f4 at pc 0x56652d14 bp 0xffff6568 sp 0xffff6558
READ of size 4 at 0xf46029f4 thread T0
    #0 0x56652d13 in setup_call /root/Ablation/wac-asan/wa.c:525
    #1 0x566544fb in interpret /root/Ablation/wac-asan/wa.c:738
    #2 0x56665459 in load_module /root/Ablation/wac-asan/wa.c:1911
    #3 0x566670e9 in main /root/Ablation/wac-asan/wace.c:64
    #4 0xf7472ed4 in __libc_start_main ../csu/libc-start.c:308
    #5 0x5664a704 in _start (/root/Ablation/wac-asan/wace+0x3704)

0xf46029f4 is located 4 bytes to the right of 112-byte region [0xf4602980,0xf46029f0)
allocated by thread T0 here:
    #0 0xf7ac59f7 in __interceptor_calloc ../../../../../src/libsanitizer/asan/asan_malloc_linux.cc:153
    #1 0x56666299 in acalloc /root/Ablation/wac-asan/platform_libc.c:16
    #2 0x56660a22 in load_module /root/Ablation/wac-asan/wa.c:1502
    #3 0x566670e9 in main /root/Ablation/wac-asan/wace.c:64
    #4 0xf7472ed4 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow /root/Ablation/wac-asan/wa.c:525 in setup_call

Reproduce

./wace https://github.com/haruki3hhh/fuzzing/blob/main/wac/id%3A000207%2Csig%3A11%2Csrc%3A001719%2Cop%3Apython%2Cpos%3A0

examples cannot stat wasm files

running examples in README.md, and each one produces an error similar to:

> root@f2b8deabf964:/wac# ./wace examples_c/hello1.wasm
> Error(platform_libc.c:47): could stat file 'examples_c/hello1.wasm'

segmentation fault when running clang built wasm

When running wac on sha1sum.wasm from the vmir project, I get the following error:

./wac ~/clones/vmir/examples/prebuilt/sha1sum.wasm
zsh: segmentation fault (core dumped)  ./wac ~/clones/vmir/examples/prebuilt/sha1sum.wasm

I'm using:

  • latest master of wac, commit 1197a2d
  • 64-bit Arch Linux with the official gcc package (gcc 8.1.1)
  • lib32-libedit from AUR

When compiling with -O1 -g and using bt full in gdb, this is the output:

(gdb) r ~/clones/vmir/examples/prebuilt/sha1sum.wasm
Starting program: /home/afr/clones/wac/wac ~/clones/vmir/examples/prebuilt/sha1sum.wasm

Program received signal SIGSEGV, Segmentation fault.
0xf7dae279 in __memset_sse2 () from /usr/lib32/libc.so.6
(gdb) bt full
#0  0xf7dae279 in __memset_sse2 () from /usr/lib32/libc.so.6
No symbol table info available.
#1  0x565655b0 in thunk_out_i_iii (m=0xf7b2d010, function=0x5658b2b8, type=0x5658a6dc) at thunks.c:454
        res = <optimized out>
#2  0x5655fc11 in thunk_out (m=0xf7b2d010, fidx=2) at wa.c:544
        func = 0x5658b2b8
        type = 0x5658a6dc
        thunk_mask = 8454417
        p = <optimized out>
#3  0x565601e3 in interpret (m=0xf7b2d010) at wa.c:809
        block = <optimized out>
        prev_pages = <optimized out>
        sval = <optimized out>
        didx = <optimized out>
        delta = <optimized out>
        bytes = 0xf7fd0000 ""
        stack = 0xf7b2d074
        cur_pc = 492
        block = <optimized out>
        arg = <optimized out>
        val = <optimized out>
        fidx = <optimized out>
        tidx = <optimized out>
        cond = <optimized out>
        depth = <optimized out>
        count = <optimized out>
        flags = <optimized out>
        offset = <optimized out>
        addr = <optimized out>
        maddr = <optimized out>
        mem_end = <optimized out>
        depths = <optimized out>
        opcode = 16 '\020'
        a = <optimized out>
        b = <optimized out>
        c = 4294967104
        d = <optimized out>
        e = <optimized out>
        f = 6221221224307818496
        g = <optimized out>
        h = <optimized out>
        i = -1.03310949e+34
        j = <optimized out>
        k = <optimized out>
        l = 1.3447704791445071e-313
        overflow = false
#4  0x56563be8 in invoke (m=0xf7b2d010, entry=<optimized out>, argc=-1, argv=0xffffd0c0) at wa.c:1997
        fidx = 6
        type = 0x5658a6a0
        result = <optimized out>
#5  0x5655eec7 in main (argc=<optimized out>, argv=<optimized out>) at wac.c:63
        mod_path = <optimized out>
        entry = <optimized out>
        line = <optimized out>
        repl = 0
        debug = 0
        res = 0
        option_index = 0
        c = <optimized out>
        long_options = {{name = 0x5657545c "repl", has_arg = 0, flag = 0xffffcfac, val = 1}, {name = 0x56575461 "debug", has_arg = 0, flag = 0xffffcfb0, val = 1}, {name = 0x0, has_arg = 0, flag = 0x0, val = 0}}
        opts = {disable_memory_bounds = false, mangle_table_index = false, dlsym_trim_underscore = false}
        m = <optimized out>
(gdb)

The problematic line could be line 454 in thunks.c?

Multiple heap-buffer-overflow vulnerabilities in interpret

Vuln-1

Version

385e13c

Compile

CFLAGS="-g -fsanitize=address" make

ASAN Report

root@9dc6ce043bcb:~/Ablation/wac-asan# ./wace ../wasm-fuzz/fuzz_out_wac/crashes/id\:000000\,sig\:11\,src\:000236\,op\:python\,pos\:0 
=================================================================
==2066617==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf3b0046f at pc 0x5664afca bp 0xffcf5ae8 sp 0xffcf5ad8
WRITE of size 1 at 0xf3b0046f thread T0
    #0 0x5664afc9 in interpret /root/Ablation/wac-asan/wa.c:993
    #1 0x56658459 in load_module /root/Ablation/wac-asan/wa.c:1911
    #2 0x5665a0e9 in main /root/Ablation/wac-asan/wace.c:64
    #3 0xf7387ed4 in __libc_start_main ../csu/libc-start.c:308
    #4 0x5663d704 in _start (/root/Ablation/wac-asan/wace+0x3704)

0xf3b0046f is located 1 bytes to the left of 1-byte region [0xf3b00470,0xf3b00471)
allocated by thread T0 here:
    #0 0xf79da9f7 in __interceptor_calloc ../../../../../src/libsanitizer/asan/asan_malloc_linux.cc:153
    #1 0x56659299 in acalloc /root/Ablation/wac-asan/platform_libc.c:16
    #2 0x566562b4 in load_module /root/Ablation/wac-asan/wa.c:1708
    #3 0x5665a0e9 in main /root/Ablation/wac-asan/wace.c:64
    #4 0xf7387ed4 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow /root/Ablation/wac-asan/wa.c:993 in interpret
Shadow bytes around the buggy address:
  0x3e760030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e760040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e760050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e760060: fa fa fa fa fa fa fa fa fa fa 01 fa fa fa 01 fa
  0x3e760070: fa fa 01 fa fa fa 00 04 fa fa 00 04 fa fa 06 fa
=>0x3e760080: fa fa fd fd fa fa 07 fa fa fa fd fd fa[fa]01 fa
  0x3e760090: fa fa 04 fa fa fa 01 fa fa fa 01 fa fa fa 04 fa
  0x3e7600a0: fa fa 00 fa fa fa 04 fa fa fa 04 fa fa fa 04 fa
  0x3e7600b0: fa fa 00 fa fa fa 04 fa fa fa 00 fa fa fa 00 01
  0x3e7600c0: fa fa 00 02 fa fa 00 04 fa fa 00 07 fa fa 00 07
  0x3e7600d0: fa fa 00 00 fa fa 00 00 fa fa 00 07 fa fa 00 04
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2066617==ABORTING

Reproduce

./wace <PoC>

PoC

PoC

Multiple heap-buffer-overflow in load_module

Version

385e13c

Compile

CFLAGS="-g -fsanitize=address" make

ASAN Report

root@9dc6ce043bcb:~/Ablation/wasm-fuzz/fuzz_out_wac/crashes# ./wace id:000220,sig:11,src:001688,op:python,pos:0
=================================================================
==4631==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf3c004d4 at pc 0x56665510 bp 0xffcd8328 sp 0xffcd8318
WRITE of size 4 at 0xf3c004d4 thread T0
    #0 0x5666550f in load_module /root/Ablation/wac-asan/wa.c:1806
    #1 0x566680e9 in main /root/Ablation/wac-asan/wace.c:64
    #2 0xf73c7ed4 in __libc_start_main ../csu/libc-start.c:308
    #3 0x5664b704 in _start (/root/Ablation/wac-asan/wace+0x3704)

0xf3c004d4 is located 0 bytes to the right of 4-byte region [0xf3c004d0,0xf3c004d4)
allocated by thread T0 here:
    #0 0xf7a1a9f7 in __interceptor_calloc ../../../../../src/libsanitizer/asan/asan_malloc_linux.cc:153
    #1 0x56667299 in acalloc /root/Ablation/wac-asan/platform_libc.c:16
    #2 0x566640f7 in load_module /root/Ablation/wac-asan/wa.c:1694
    #3 0x566680e9 in main /root/Ablation/wac-asan/wace.c:64
    #4 0xf73c7ed4 in __libc_start_main ../csu/libc-start.c:308

Reproduce

./wace https://github.com/haruki3hhh/fuzzing/blob/main/wac/id%3A000220%2Csig%3A11%2Csrc%3A001688%2Cop%3Apython%2Cpos%3A0

wac crashes

Hi, I did a bit of fuzzing of and found issues that can make wac crash. I provide files to reproduce the issues.

Issue 1: NULL pointer dereference of function

Sample file: https://drive.google.com/open?id=1JnSjtTw6SeKQksgGa2NrzP_syM0bFG82

If a code section is parsed before imports, m->functions can be NULL and will later cause a null pointer dereference. For example:

Block *function = &m->functions[m->import_count+b];

Issue 2: Read out-of-bounds in read functions read_string(), read_LEB, read_uint32.

Sample file: https://drive.google.com/open?id=1yKmr0Om_Ypg1nnz8VPnp5LuQFic2WMLc

The read functions do not make sure that the file has as least the remaining amount of data they attempt to read. For example the read_string functions reads the length of a string in str_len and then memcpy's that length without checking that the remaining file size it at least that long.

char *read_string(uint8_t *bytes, uint32_t *pos, uint32_t *result_len) {
    uint32_t str_len = read_LEB(bytes, pos, 32);
    char * str = malloc(str_len+1);
    memcpy(str, bytes+*pos, str_len);

The read functions should take a bytes_len as a parameter and check the remaining size.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.