├── .github └── workflows │ └── swift.yml ├── .gitignore ├── CONTRIBUTING.md ├── Cloud ├── Docker │ ├── Dockerfile │ ├── DuktapeBuilder │ │ ├── .gitignore │ │ ├── Dockerfile │ │ └── build.sh │ ├── FuzzilliBuilder │ │ ├── .gitignore │ │ ├── Dockerfile │ │ └── build.sh │ ├── JSCBuilder │ │ ├── .gitignore │ │ ├── Dockerfile │ │ └── build.sh │ ├── JerryScriptBuilder │ │ ├── .gitignore │ │ ├── Dockerfile │ │ └── build.sh │ ├── README.md │ ├── SpidermonkeyBuilder │ │ ├── .gitignore │ │ ├── Dockerfile │ │ └── build.sh │ ├── V8Builder │ │ ├── .gitignore │ │ ├── Dockerfile │ │ └── build.sh │ └── build.sh ├── GCE │ ├── README.md │ ├── config-template.sh │ ├── push.sh │ ├── start.sh │ └── stop.sh ├── README.md └── Triage │ ├── README.md │ └── check.sh ├── Compiler ├── .gitignore ├── Dockerfile ├── README.md ├── bin │ ├── dune │ └── fuzzilli_compiler.ml ├── build-pbs.sh ├── dune-project ├── fuzzilli_compiler.opam ├── package.json ├── src │ ├── ProgramBuilder.ml │ ├── ProgramBuilder.mli │ ├── VariableScope.ml │ ├── VariableScope.mli │ ├── compiler.ml │ ├── dune │ ├── proto │ │ ├── dune │ │ ├── operations_pb.ml │ │ ├── operations_pb.mli │ │ ├── operations_pp.ml │ │ ├── operations_pp.mli │ │ ├── operations_types.ml │ │ ├── operations_types.mli │ │ ├── program_pb.ml │ │ ├── program_pb.mli │ │ ├── program_pp.ml │ │ ├── program_pp.mli │ │ ├── program_types.ml │ │ ├── program_types.mli │ │ ├── typesystem_pb.ml │ │ ├── typesystem_pb.mli │ │ ├── typesystem_pp.ml │ │ ├── typesystem_pp.mli │ │ ├── typesystem_types.ml │ │ └── typesystem_types.mli │ ├── translate.ml │ ├── translate.mli │ ├── util.ml │ └── util.mli ├── supportedBuiltins.txt └── test │ ├── array_assign.ml │ ├── array_assign_sugared.ml │ ├── array_decl.ml │ ├── array_hole.ml │ ├── array_spread.ml │ ├── basic_break.ml │ ├── basic_compare_test.ml │ ├── basic_continue.ml │ ├── basic_for.ml │ ├── basic_func_call.ml │ ├── basic_func_ret.ml │ ├── basic_while.ml │ ├── binary_ops.ml │ ├── bitwise_ops.ml │ ├── create_array.ml │ ├── del_test.ml │ ├── do_while.ml │ ├── dune │ ├── empty_assignment_scope.ml │ ├── exp_statement.ml │ ├── for_in.ml │ ├── for_in_scope2.ml │ ├── for_in_scoping.ml │ ├── for_of.ml │ ├── func_call_with_spread.ml │ ├── func_dec_order.ml │ ├── func_exp_test.ml │ ├── func_param_scoping.ml │ ├── if_else.ml │ ├── in_test.ml │ ├── instance_of.ml │ ├── load_array_index.ml │ ├── load_bigint.ml │ ├── load_bool.ml │ ├── load_float.ml │ ├── load_infinity.ml │ ├── load_null.ml │ ├── load_property.ml │ ├── load_regex.ml │ ├── logical_ops.ml │ ├── lone_if.ml │ ├── new.ml │ ├── object_creation.ml │ ├── prog_10.ml │ ├── prog_1007.ml │ ├── prop_name_assignment.ml │ ├── single_constant.ml │ ├── single_let.ml │ ├── single_string_literal.ml │ ├── spread_object.ml │ ├── store_property_sugared.ml │ ├── sugared_assignment.ml │ ├── ternary.ml │ ├── test.ml │ ├── this.ml │ ├── throw.ml │ ├── typeof.ml │ ├── unary_minus.ml │ ├── unary_ops.ml │ ├── undefined.ml │ ├── update.ml │ ├── util.ml │ ├── v8_natives.ml │ ├── var_hoisting_1.ml │ ├── var_hoisting_2.ml │ ├── var_hoisting_3.ml │ ├── var_hoisting_shadow.ml │ ├── varied_func_types.ml │ ├── void.ml │ ├── with.ml │ └── with_load_scope.ml ├── Docs ├── HowFuzzilliWorks.md ├── ProcessingModel.md ├── TypeDetermination.md └── images │ ├── fuzzing_with_fuzzil.png │ ├── generative_engine.png │ ├── hybrid_engine.png │ └── mutation_engine.png ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Benchmarks │ └── main.swift ├── FuzzILTool │ └── main.swift ├── Fuzzilli │ ├── Configuration.swift │ ├── Core │ │ ├── CodeGenerator.swift │ │ ├── CodeGenerators.swift │ │ ├── Component.swift │ │ ├── Environment.swift │ │ ├── Events.swift │ │ ├── FuzzEngine.swift │ │ ├── HybridEngine.swift │ │ ├── JavaScriptEnvironment.swift │ │ ├── Logging.swift │ │ ├── MultiEngine.swift │ │ ├── MutationEngine.swift │ │ ├── ProgramBuilder.swift │ │ ├── ProgramGeneratorStats.swift │ │ ├── ProgramOrigin.swift │ │ ├── ProgramTemplate.swift │ │ ├── ProgramTemplates.swift │ │ └── Timers.swift │ ├── Corpus │ │ ├── BasicCorpus.swift │ │ ├── Corpus.swift │ │ └── MarkovCorpus.swift │ ├── Evaluation │ │ ├── ProgramAspects.swift │ │ ├── ProgramCoverageEvaluator.swift │ │ └── ProgramEvaluator.swift │ ├── Execution │ │ ├── Execution.swift │ │ ├── REPRL.swift │ │ └── ScriptRunner.swift │ ├── FuzzIL │ │ ├── AbstractInterpreter.swift │ │ ├── Analyzer.swift │ │ ├── Blocks.swift │ │ ├── ClassUtils.swift │ │ ├── Code.swift │ │ ├── Context.swift │ │ ├── Instruction.swift │ │ ├── Operations.swift │ │ ├── Program.swift │ │ ├── ProgramComments.swift │ │ ├── ProgramTypes.swift │ │ ├── Semantics.swift │ │ ├── TypeCollectionStatus.swift │ │ ├── TypeInfo.swift │ │ ├── TypeSystem.swift │ │ └── Variable.swift │ ├── Fuzzer.swift │ ├── Lifting │ │ ├── Expression.swift │ │ ├── FuzzILLifter.swift │ │ ├── InliningPolicy.swift │ │ ├── JSExpressions.swift │ │ ├── JavaScriptLifter.swift │ │ ├── Lifter.swift │ │ ├── ScriptWriter.swift │ │ └── TypeCollectionAnalyzer.swift │ ├── Minimization │ │ ├── BlockReducer.swift │ │ ├── CallArgumentReducer.swift │ │ ├── GenericInstructionReducer.swift │ │ ├── InliningReducer.swift │ │ ├── Minimizer.swift │ │ ├── ReplaceReducer.swift │ │ └── Verifier.swift │ ├── Modules │ │ ├── Module.swift │ │ ├── NetworkSync.swift │ │ ├── Statistics.swift │ │ ├── Storage.swift │ │ └── ThreadSync.swift │ ├── Mutators │ │ ├── BaseInstructionMutator.swift │ │ ├── CodeGenMutator.swift │ │ ├── CombineMutator.swift │ │ ├── ConcatMutator.swift │ │ ├── InputMutator.swift │ │ ├── JITStressMutator.swift │ │ ├── Mutator.swift │ │ ├── MutatorSettings.swift │ │ └── OperationMutator.swift │ ├── Protobuf │ │ ├── ProtoUtils.swift │ │ ├── README.md │ │ ├── operations.pb.swift │ │ ├── operations.proto │ │ ├── program.pb.swift │ │ ├── program.proto │ │ ├── sync.pb.swift │ │ ├── sync.proto │ │ ├── typesystem.pb.swift │ │ └── typesystem.proto │ └── Util │ │ ├── Arguments.swift │ │ ├── CInterop.swift │ │ ├── Error.swift │ │ ├── Misc.swift │ │ ├── MockFuzzer.swift │ │ ├── MovingAverage.swift │ │ ├── Random.swift │ │ ├── RingBuffer.swift │ │ ├── VariableMap.swift │ │ ├── VariableSet.swift │ │ └── WeightedList.swift ├── FuzzilliCli │ ├── CodeGeneratorWeights.swift │ ├── Profiles │ │ ├── DuktapeProfile.swift │ │ ├── JSCProfile.swift │ │ ├── JerryscriptProfile.swift │ │ ├── Profile.swift │ │ ├── QjsProfile.swift │ │ ├── SpidermonkeyProfile.swift │ │ └── V8Profile.swift │ ├── ProgramTemplateWeights.swift │ ├── TerminalUI.swift │ └── main.swift ├── JS │ ├── LICENSE │ ├── README.md │ ├── generateSwift.sh │ ├── helpers.js │ ├── helpers.swift │ ├── initTypeCollection.js │ ├── initTypeCollection.swift │ ├── printTypes.js │ └── printTypes.swift ├── REPRLRun │ └── main.swift ├── libcoverage │ ├── coverage.c │ └── include │ │ └── libcoverage.h ├── libreprl │ ├── include │ │ └── libreprl.h │ ├── libreprl-posix.c │ └── libreprl-windows.c └── libsocket │ ├── include │ └── libsocket.h │ ├── socket-posix.c │ └── socket-win32.c ├── Targets ├── ChakraCore │ ├── README.md │ ├── chakracore.patch │ └── fuzzbuild.sh ├── JavaScriptCore │ ├── Patches │ │ └── webkit.patch │ ├── README.md │ ├── REVISION │ └── fuzzbuild.sh ├── Jerryscript │ ├── Patches │ │ └── jerryscript.patch │ ├── README.md │ ├── REVISION │ └── fuzzbuild.sh ├── QJS │ ├── Patches │ │ └── Fuzzilli-instrumentation-for-QJS.patch │ ├── README.md │ └── REVISION ├── README.md ├── Spidermonkey │ ├── Patches │ │ └── .gitkeep │ ├── README.md │ ├── REVISION │ └── fuzzbuild.sh ├── V8 │ ├── Patches │ │ └── .gitkeep │ ├── README.md │ ├── REVISION │ └── fuzzbuild.sh ├── coverage.c └── duktape │ └── README.md └── Tests └── FuzzilliTests ├── AnalyzerTest.swift ├── EnvironmentTest.swift ├── InliningTest.swift ├── InterpreterTest.swift ├── LifterTest.swift ├── MutationsTest.swift ├── ProgramBuilderTest.swift ├── ProgramSerializationTest.swift ├── RingBufferTest.swift ├── TestUtils.swift ├── TypeSystemTest.swift ├── VariableMapTest.swift └── VariableSetTest.swift /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | macOS: 11 | runs-on: macos-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build 16 | run: swift build -v 17 | - name: Run tests 18 | run: swift test -v 19 | 20 | linux: 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: fwal/setup-swift@v1.8.0 25 | - name: Swift Version 26 | run: swift --version 27 | - uses: actions/checkout@v2 28 | - name: Build 29 | run: swift build -v 30 | - name: Run tests 31 | run: swift test -v 32 | 33 | windows: 34 | runs-on: windows-latest 35 | 36 | steps: 37 | - uses: compnerd/gha-setup-swift@main 38 | with: 39 | branch: swift-5.5-release 40 | tag: 5.5-RELEASE 41 | - uses: actions/checkout@v2 42 | - name: Build 43 | run: swift build -v 44 | - name: Run tests 45 | run: swift test -v 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Package.resolved 2 | .DS_Store 3 | /.build 4 | /Packages 5 | /*.xcodeproj 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google.com/conduct/). 29 | -------------------------------------------------------------------------------- /Cloud/Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update 7 | RUN apt-get -y upgrade 8 | 9 | # Required for jsc 10 | RUN apt-get install -y libicu-dev 11 | 12 | RUN mkdir /home/fuzzer 13 | WORKDIR /home/fuzzer 14 | 15 | # Add JavaScriptCore binary 16 | ADD JSCBuilder/out jsc 17 | # Add Spidermonkey binary 18 | ADD SpidermonkeyBuilder/out spidermonkey 19 | # Add v8 binary 20 | ADD V8Builder/out v8 21 | # Add duktape binary 22 | ADD DuktapeBuilder/out duktape 23 | # Add JerryScript binary 24 | ADD JerryScriptBuilder/out jerryscript 25 | 26 | # Add Fuzzilli binaries 27 | ADD FuzzilliBuilder/out/Fuzzilli Fuzzilli 28 | ADD FuzzilliBuilder/out/REPRLRun REPRLRun 29 | ADD FuzzilliBuilder/out/Benchmarks Benchmarks 30 | 31 | RUN mkdir fuzz 32 | -------------------------------------------------------------------------------- /Cloud/Docker/DuktapeBuilder/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | -------------------------------------------------------------------------------- /Cloud/Docker/DuktapeBuilder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | RUN apt-get install -y git make clang bc curl python-pip nodejs npm 8 | 9 | # Update NodeJS and NPM to the latest versions 10 | RUN npm install -g n 11 | RUN n latest 12 | RUN npm install -g npm 13 | 14 | # Dependency of the duktape build process 15 | RUN pip install pyyaml 16 | 17 | RUN useradd -m builder 18 | 19 | RUN git clone https://github.com/svaarala/duktape.git /home/builder/duktape 20 | WORKDIR /home/builder/duktape 21 | 22 | # The hash of the most recent commit is passed in from the build script, to ensure proper caching behavior 23 | ARG rev 24 | RUN git pull && git checkout $rev 25 | 26 | # Update system packages first 27 | RUN apt-get -y update && apt-get -y upgrade 28 | 29 | # Make normally to pull down NodeJS deps 30 | RUN make 31 | 32 | # Assume that the current master branch maintains duk-fuzzilli 33 | # No need to patch, as the fuzz target is maintained in the duktape repo 34 | # Start building! 35 | RUN make build/duk-fuzzilli 36 | -------------------------------------------------------------------------------- /Cloud/Docker/DuktapeBuilder/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | FUZZILLI_ROOT=../../.. 7 | 8 | # Get the hash of the most recent commit on master. This is to ensure proper caching behavior in docker 9 | REV=$(git ls-remote https://github.com/svaarala/duktape.git | grep refs/heads/master | awk '{print $1;}') 10 | 11 | # Since fuzzilli is integrated as a duktape make target, no need to pull over patches or a build script 12 | 13 | # Fetch the source code, get the current master commit, and compile the engine 14 | docker build --build-arg rev=$REV -t duktape_builder . 15 | 16 | # Copy build products 17 | mkdir -p out 18 | docker create --name temp_container duktape_builder 19 | docker cp temp_container:/home/builder/duktape/build/duk-fuzzilli out/duk-fuzzilli 20 | docker rm temp_container 21 | 22 | # Nothing extra to clean up! 23 | -------------------------------------------------------------------------------- /Cloud/Docker/FuzzilliBuilder/.gitignore: -------------------------------------------------------------------------------- 1 | fuzzilli 2 | out 3 | -------------------------------------------------------------------------------- /Cloud/Docker/FuzzilliBuilder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | 8 | RUN useradd -m builder 9 | WORKDIR /home/builder 10 | 11 | ADD fuzzilli fuzzilli 12 | 13 | RUN cd fuzzilli && swift test && swift build -c release 14 | -------------------------------------------------------------------------------- /Cloud/Docker/FuzzilliBuilder/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | FUZZILLI_ROOT=../../.. 7 | 8 | # Setup build context 9 | rm -rf fuzzilli && mkdir fuzzilli 10 | cp -r $FUZZILLI_ROOT/Sources fuzzilli 11 | cp -r $FUZZILLI_ROOT/Tests fuzzilli 12 | cp -r $FUZZILLI_ROOT/Package.swift fuzzilli 13 | 14 | # Compile Fuzzilli 15 | docker build -t fuzzilli_builder . 16 | 17 | # Copy build products 18 | mkdir -p out 19 | docker create --name temp_container fuzzilli_builder 20 | docker cp temp_container:/home/builder/fuzzilli/.build/release/FuzzilliCli out/Fuzzilli 21 | docker cp temp_container:/home/builder/fuzzilli/.build/release/REPRLRun out/REPRLRun 22 | docker cp temp_container:/home/builder/fuzzilli/.build/release/Benchmarks out/Benchmarks 23 | docker rm temp_container 24 | 25 | # Clean up 26 | rm -rf fuzzilli 27 | -------------------------------------------------------------------------------- /Cloud/Docker/JSCBuilder/.gitignore: -------------------------------------------------------------------------------- 1 | webkit.patch 2 | fuzzbuild.sh 3 | out 4 | -------------------------------------------------------------------------------- /Cloud/Docker/JSCBuilder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | RUN apt-get install -y git cmake clang-12 ruby libicu-dev python3-dev 8 | 9 | RUN useradd -m builder 10 | 11 | # Fetch WebKit source code 12 | RUN git clone https://github.com/WebKit/WebKit.git /home/builder/webkit 13 | WORKDIR /home/builder/webkit 14 | 15 | # Docker will attempt to cache the output of every step. That's fine (and useful to speed things up, e.g. by avoiding 16 | # the need to download the entire source repository again every time!). However, whenever the following ARG is changed 17 | # (i.e. we are building a new version of the engine), a cache miss occurs (because the build context changed) and all 18 | # steps from here on are rerun. That, however, means we might be operating on an old checkout of the source code from 19 | # the cache, and so we update it again before checking out the requested revision. 20 | ARG rev=master 21 | 22 | # Update system packages first 23 | RUN apt-get -y update && apt-get -y upgrade 24 | 25 | # Fetch latest source code and checkout requested source revision 26 | RUN git pull 27 | RUN git checkout $rev 28 | 29 | # Upload and apply patches 30 | ADD Patches Patches 31 | RUN for i in `ls Patches`; do patch -p1 < Patches/$i; done 32 | 33 | # Start building! 34 | ADD fuzzbuild.sh fuzzbuild.sh 35 | RUN ./fuzzbuild.sh 36 | -------------------------------------------------------------------------------- /Cloud/Docker/JSCBuilder/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | FUZZILLI_ROOT=../../.. 7 | 8 | # Setup build context 9 | REV=$(cat $FUZZILLI_ROOT/Targets/JavaScriptCore/REVISION) 10 | cp -R $FUZZILLI_ROOT/Targets/JavaScriptCore/Patches . 11 | cp $FUZZILLI_ROOT/Targets/JavaScriptCore/fuzzbuild.sh . 12 | 13 | # Fetch the source code, apply patches, and compile the engine 14 | docker build --build-arg rev=$REV -t jsc_builder . 15 | 16 | # Copy build products 17 | mkdir -p out 18 | docker create --name temp_container jsc_builder 19 | docker cp temp_container:/home/builder/webkit/FuzzBuild/Debug/bin/jsc out/jsc 20 | docker rm temp_container 21 | 22 | # Clean up 23 | rm -r Patches 24 | rm fuzzbuild.sh 25 | -------------------------------------------------------------------------------- /Cloud/Docker/JerryScriptBuilder/.gitignore: -------------------------------------------------------------------------------- 1 | jerryscript.patch 2 | fuzzbuild.sh 3 | out 4 | *.swp 5 | -------------------------------------------------------------------------------- /Cloud/Docker/JerryScriptBuilder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | RUN apt-get install -y git python3 python3-virtualenv clang cmake 8 | 9 | RUN useradd -m builder 10 | 11 | # Fetch JerryScript source code 12 | RUN git clone https://github.com/jerryscript-project/jerryscript.git /home/builder/jerryscript 13 | WORKDIR /home/builder/jerryscript 14 | 15 | # Docker will attempt to cache the output of every step. That's fine (and useful to speed things up, e.g. by avoiding 16 | # the need to download the entire source repository again every time!). However, whenever the following ARG is changed 17 | # (i.e. we are building a new version of the engine), a cache miss occurs (because the build context changed) and all 18 | # steps from here on are rerun. That, however, means we might be operating on an old checkout of the source code from 19 | # the cache, and so we update it again before checking out the requested revision. 20 | ARG rev=master 21 | 22 | # Update system packages first 23 | RUN apt-get -y update && apt-get -y upgrade 24 | 25 | # Fetch latest source code and checkout requested source revision 26 | RUN git pull 27 | RUN git checkout $rev 28 | 29 | # Upload and apply patches 30 | ADD Patches Patches 31 | RUN for i in `ls Patches`; do patch -p1 < Patches/$i; done 32 | 33 | # Start building! 34 | ADD fuzzbuild.sh fuzzbuild.sh 35 | RUN ./fuzzbuild.sh 36 | -------------------------------------------------------------------------------- /Cloud/Docker/JerryScriptBuilder/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | FUZZILLI_ROOT=../../.. 7 | 8 | # Setup build context 9 | REV=$(cat $FUZZILLI_ROOT/Targets/Jerryscript/REVISION) 10 | cp -R $FUZZILLI_ROOT/Targets/Jerryscript/Patches . 11 | cp $FUZZILLI_ROOT/Targets/Jerryscript/fuzzbuild.sh . 12 | 13 | # Fetch the source code, apply patches, and compile the engine 14 | docker build --build-arg rev=$REV -t jerryscript_builder . 15 | 16 | # Copy build products 17 | mkdir -p out 18 | docker create --name temp_container jerryscript_builder 19 | docker cp temp_container:/home/builder/jerryscript/build/bin/jerry out/jerry 20 | docker rm temp_container 21 | 22 | # Clean up 23 | rm -r Patches 24 | rm fuzzbuild.sh 25 | -------------------------------------------------------------------------------- /Cloud/Docker/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzilli in Docker 2 | 3 | Scripts and Dockerfiles to create a docker image for fuzzing with Fuzzilli. 4 | 5 | ## Overview 6 | 7 | The container image produced by the main build script ([build.sh](./build.sh)) will contain 8 | 9 | - The Fuzzilli binary at ~/Fuzzilli compiled from the current source code, and 10 | - One or more JavaScript engines compiled as specified in their respective [target directory](../../Targets). The necessary files to run the engine (the binary, possibly various libraries, and any other resource files required by the engine) will be located in a subdirectory of the home directory: ~/jsc, ~/spidermonkey, ~/v8, ~/duktape, ~/jerryscript 11 | 12 | The container image will *not* contain any temporary build artifacts, source code, etc. to reduce its size. 13 | 14 | ## Quickstart 15 | 16 | 1. Make sure docker is installed 17 | 2. Run `./build.sh [jsc|spidermonkey|v8|duktape|jerryscript|major]` 18 | 19 | The build script might have to run as root, depending on how [docker is configured](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user). 20 | 21 | Afterwards, a docker image named "fuzzilli" will be available and can be used to fuzz any of the compiled JS engines (in this example JavaScriptCore) with Fuzzilli: `docker run -ti fuzzilli ./Fuzzilli --profile=jsc ./jsc/jsc` 22 | 23 | It is also possible to only rebuild Fuzzilli and use previously compiled engines by running `./build.sh fuzzilli` 24 | 25 | Under the hood, here is roughly what happens during building: 26 | 27 | - Depending on the arguments, the [root build script](./build.sh) will invoke a number of builders, located in the \*Builder subdirectories. Fuzzilli is always (re-)build (by the FuzzilliBuilder) as well as all of the requested JS engines 28 | - The builders will generally first copy all the necessary files (Fuzzilli soure code, engine patches, target revision, etc.) from the repository root into the builder's directory (this is necessary as they have to be in a subdirectory for docker to be able to access them), then build the docker image, fetching any source code, checking out the requested revision (from the [target's](../../Targets) REVISION file) and building the final product (e.g. Fuzzilli or a JS engine). Afterwards, all build products are copied out of the container image into the out/ directory of that builder. 29 | - Finally, the root build script copies all available build products (that may include build products from previous builds if an engine had been built before and not rebuilt this time) into the final fuzzilli docker image. 30 | -------------------------------------------------------------------------------- /Cloud/Docker/SpidermonkeyBuilder/.gitignore: -------------------------------------------------------------------------------- 1 | firefox.patch 2 | fuzzbuild.sh 3 | out 4 | -------------------------------------------------------------------------------- /Cloud/Docker/SpidermonkeyBuilder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | RUN apt-get install -y git python3 python3-pip python3-virtualenv clang-10 8 | 9 | RUN useradd -m builder 10 | 11 | # Fetch Firefox source code 12 | RUN git clone https://github.com/mozilla/gecko-dev.git /home/builder/firefox 13 | WORKDIR /home/builder/firefox 14 | 15 | # Docker will attempt to cache the output of every step. That's fine (and useful to speed things up, e.g. by avoiding 16 | # the need to download the entire source repository again every time!). However, whenever the following ARG is changed 17 | # (i.e. we are building a new version of the engine), a cache miss occurs (because the build context changed) and all 18 | # steps from here on are rerun. That, however, means we might be operating on an old checkout of the source code from 19 | # the cache, and so we update it again before checking out the requested revision. 20 | ARG rev=master 21 | 22 | # Update system packages first 23 | RUN apt-get -y update && apt-get -y upgrade 24 | 25 | # Fetch latest source code and checkout requested source revision 26 | RUN git pull 27 | RUN git checkout $rev 28 | 29 | # Prepare for building 30 | RUN ./mach bootstrap --application-choice=js 31 | 32 | # Upload and apply patches 33 | ADD Patches Patches 34 | RUN for i in `ls Patches`; do patch -p1 < Patches/$i; done 35 | 36 | # Start building! 37 | ADD fuzzbuild.sh js/src/fuzzbuild.sh 38 | RUN cd js/src && ./fuzzbuild.sh 39 | -------------------------------------------------------------------------------- /Cloud/Docker/SpidermonkeyBuilder/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | FUZZILLI_ROOT=../../.. 7 | 8 | # Setup build context 9 | REV=$(cat $FUZZILLI_ROOT/Targets/Spidermonkey/REVISION) 10 | cp -R $FUZZILLI_ROOT/Targets/Spidermonkey/Patches . 11 | cp $FUZZILLI_ROOT/Targets/Spidermonkey/fuzzbuild.sh . 12 | 13 | # Fetch the source code, apply patches, and compile the engine 14 | docker build --build-arg rev=$REV -t spidermonkey_builder . 15 | 16 | # Copy build products 17 | mkdir -p out 18 | docker create --name temp_container spidermonkey_builder 19 | docker cp temp_container:/home/builder/firefox/js/src/fuzzbuild_OPT.OBJ/dist/bin/js out/js 20 | docker rm temp_container 21 | 22 | # Clean up 23 | rm -r Patches 24 | rm fuzzbuild.sh 25 | -------------------------------------------------------------------------------- /Cloud/Docker/V8Builder/.gitignore: -------------------------------------------------------------------------------- 1 | v8.patch 2 | fuzzbuild.sh 3 | out 4 | -------------------------------------------------------------------------------- /Cloud/Docker/V8Builder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | RUN apt-get install -y python python3 git curl 8 | 9 | RUN useradd -m builder 10 | 11 | # Fetch v8 source code 12 | WORKDIR /home/builder 13 | RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 14 | ENV PATH="${PATH}:/home/builder/depot_tools" 15 | RUN gclient 16 | RUN gclient metrics --opt-out 17 | RUN mkdir v8 && cd v8 && fetch v8 18 | WORKDIR /home/builder/v8/v8 19 | RUN git checkout master 20 | 21 | # Docker will attempt to cache the output of every step. That's fine (and useful to speed things up, e.g. by avoiding 22 | # the need to download the entire source repository again every time!). However, whenever the following ARG is changed 23 | # (i.e. we are building a new version of the engine), a cache miss occurs (because the build context changed) and all 24 | # steps from here on are rerun. That, however, means we might be operating on an old checkout of the source code from 25 | # the cache, and so we update it again before checking out the requested revision. 26 | ARG rev=master 27 | 28 | # Update system packages first 29 | RUN apt-get -y update && apt-get -y upgrade 30 | 31 | # Fetch latest source code and checkout requested source revision 32 | RUN git pull 33 | RUN git checkout $rev 34 | RUN gclient sync 35 | 36 | # Upload and apply patches 37 | ADD Patches Patches 38 | RUN for i in `ls Patches`; do patch -p1 < Patches/$i; done 39 | 40 | # Start building! 41 | ADD fuzzbuild.sh fuzzbuild.sh 42 | RUN ./fuzzbuild.sh 43 | -------------------------------------------------------------------------------- /Cloud/Docker/V8Builder/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | FUZZILLI_ROOT=../../.. 7 | 8 | # Setup build context 9 | REV=$(cat $FUZZILLI_ROOT/Targets/V8/REVISION) 10 | cp -R $FUZZILLI_ROOT/Targets/V8/Patches . 11 | cp $FUZZILLI_ROOT/Targets/V8/fuzzbuild.sh . 12 | 13 | # Fetch the source code, apply patches, and compile the engine 14 | docker build --build-arg rev=$REV -t v8_builder . 15 | 16 | # Copy build products 17 | mkdir -p out 18 | docker create --name temp_container v8_builder 19 | docker cp temp_container:/home/builder/v8/v8/out/fuzzbuild/d8 out/d8 20 | docker cp temp_container:/home/builder/v8/v8/out/fuzzbuild/snapshot_blob.bin out/snapshot_blob.bin 21 | docker cp temp_container:/home/builder/v8/v8/out/fuzzbuild/icudtl.dat out/icudtl.dat 22 | docker rm temp_container 23 | 24 | # Clean up 25 | rm -r Patches 26 | rm fuzzbuild.sh 27 | -------------------------------------------------------------------------------- /Cloud/Docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | CONTAINER_NAME=fuzzilli 6 | 7 | if [ $# -eq 0 ]; then 8 | echo "Usage: $0 [fuzzilli|jsc|spidermonkey|v8|duktape|jerryscript|major]" 9 | exit 1 10 | fi 11 | 12 | BUILD_JSC=false 13 | BUILD_SPIDERMONKEY=false 14 | BUILD_V8=false 15 | BUILD_DUKTAPE=false 16 | BUILD_JERRYSCRIPT=false 17 | 18 | while test $# -gt 0 19 | do 20 | case "$1" in 21 | fuzzilli) 22 | # We'll build fuzzilli anyway :) 23 | ;; 24 | jsc) 25 | BUILD_JSC=true 26 | ;; 27 | spidermonkey) 28 | BUILD_SPIDERMONKEY=true 29 | ;; 30 | v8) 31 | BUILD_V8=true 32 | ;; 33 | duktape) 34 | BUILD_DUKTAPE=true 35 | ;; 36 | jerryscript) 37 | BUILD_JERRYSCRIPT=true 38 | ;; 39 | major) 40 | BUILD_JSC=true 41 | BUILD_SPIDERMONKEY=true 42 | BUILD_V8=true 43 | ;; 44 | *) 45 | echo "Usage: $0 [fuzzilli|jsc|spidermonkey|v8|duktape|jerryscript|major]" 46 | exit 1 47 | ;; 48 | esac 49 | shift 50 | done 51 | 52 | # 53 | # Always build Fuzzilli 54 | # 55 | echo "[*] Building Fuzzilli" 56 | ./FuzzilliBuilder/build.sh 57 | 58 | # 59 | # Selectively (re)build the JavaScript engines 60 | # 61 | 62 | # Ensure output directories are always present as they will be copied into the final container 63 | mkdir -p JSCBuilder/out 64 | mkdir -p SpidermonkeyBuilder/out 65 | mkdir -p V8Builder/out 66 | mkdir -p DuktapeBuilder/out 67 | mkdir -p JerryScriptBuilder/out 68 | 69 | if [ "$BUILD_JSC" = true ]; then 70 | echo "[*] Building JavaScriptCore" 71 | ./JSCBuilder/build.sh 72 | fi 73 | 74 | if [ "$BUILD_SPIDERMONKEY" = true ]; then 75 | echo "[*] Building Spidermonkey" 76 | ./SpidermonkeyBuilder/build.sh 77 | fi 78 | 79 | if [ "$BUILD_V8" = true ]; then 80 | echo "[*] Building V8" 81 | ./V8Builder/build.sh 82 | fi 83 | 84 | if [ "$BUILD_DUKTAPE" = true ]; then 85 | echo "[*] Building Duktape" 86 | ./DuktapeBuilder/build.sh 87 | fi 88 | 89 | if [ "$BUILD_JERRYSCRIPT" = true ]; then 90 | echo "[*] Building JerryScript" 91 | ./JerryScriptBuilder/build.sh 92 | fi 93 | 94 | # 95 | # Build the final container image which only contains the binaries (no intermediate build artifacts, source code, etc.). 96 | # 97 | echo "[*] Packing Fuzzilli container image" 98 | docker build -t $CONTAINER_NAME . 99 | -------------------------------------------------------------------------------- /Cloud/GCE/config-template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # GCE configuration. 4 | # 5 | # Generally, only the PROJECT_ID and PROJECT_NUMBER as well as the Fuzzilli options and NUM_WORKERS need to be changed. 6 | # 7 | 8 | # The GCP project to use. See https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects 9 | PROJECT_ID=YOUR_PROJECT_ID 10 | PROJECT_NUMBER=YOUR_PROJECT_NUMBER 11 | 12 | # The name of the session, can be an arbitrary string. 13 | # This also serves as the prefix for instance names. For example, the root instance will be named $SESSION-root. 14 | SESSION="fuzzilli" 15 | 16 | # The path to the JavaScript engine binary in the container 17 | BINARY=./v8/d8 18 | # Common arguments to pass to every Fuzzilli instance. See ./Fuzzilli --help 19 | FUZZILLI_ARGS="--profile=v8" 20 | # Arguments for the root instance. See ./Fuzzilli --help 21 | FUZZILLI_ROOT_ARGS="--exportStatistics" 22 | 23 | # Region and zone where compute instances are created. See https://cloud.google.com/compute/docs/regions-zones 24 | REGION=us-east1 25 | ZONE=$REGION-b 26 | 27 | # By default, the default service account: https://cloud.google.com/iam/docs/service-accounts#default 28 | SERVICE_ACCOUNT=$PROJECT_NUMBER-compute@developer.gserviceaccount.com 29 | 30 | # The docker container and OS image to use. 31 | CONTAINER_NAME=fuzzilli 32 | CONTAINER_IMAGE=gcr.io/$PROJECT_ID/$CONTAINER_NAME:latest 33 | # By default, use the latest stable OS image 34 | OS_IMAGE=$(gcloud compute --project=$PROJECT_ID images list --filter="family=cos-stable" --format="value(NAME)") 35 | 36 | # Total number of worker instances. Adjust this as desired 37 | NUM_WORKERS=128 38 | 39 | # How many workers to run per machine, using --jobs=N. 40 | # NUM_WORKERS / NUM_WORKERS_PER_MACHINE worker machines will be started. 41 | # This number should roughly equal the number of cores on the worker machines. 42 | NUM_WORKERS_PER_MACHINE=8 43 | 44 | # How many workers a single master instance can handle at most. 45 | # This will determine the depth of the instace hierarchy. 46 | # There is usually no need to change this number. 47 | MAX_WORKERS_PER_MASTER=32 48 | 49 | # 2 cores, 8 GB 50 | ROOT_MACHINE_TYPE=e2-standard-2 51 | MASTER_MACHINE_TYPE=e2-standard-2 52 | # 8 cores, 8 GB 53 | WORKER_MACHINE_TYPE=e2-highcpu-8 54 | -------------------------------------------------------------------------------- /Cloud/GCE/push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Push the current fuzzilli image (built from the Docker/ directory) to the GCE docker registry. 4 | # 5 | 6 | set -e 7 | 8 | source config.sh 9 | 10 | docker tag fuzzilli gcr.io/$PROJECT_ID/$CONTAINER_NAME 11 | docker push gcr.io/$PROJECT_ID/$CONTAINER_NAME 12 | -------------------------------------------------------------------------------- /Cloud/GCE/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Terminates all worker instances. 4 | # 5 | 6 | set -e 7 | 8 | source config.sh 9 | 10 | if [ $# -eq 0 ]; then 11 | echo "Usage: $0 [root|masters|workers|all]" 12 | exit 1 13 | fi 14 | 15 | STOP_ROOT=false 16 | STOP_MASTERS=false 17 | STOP_WORKERS=false 18 | 19 | while test $# -gt 0 20 | do 21 | case "$1" in 22 | root) 23 | STOP_ROOT=true 24 | ;; 25 | masters) 26 | STOP_MASTERS=true 27 | ;; 28 | workers) 29 | STOP_WORKERS=true 30 | ;; 31 | all) 32 | STOP_ROOT=true 33 | STOP_MASTERS=true 34 | STOP_WORKERS=true 35 | ;; 36 | *) 37 | echo "Usage: $0 [root|masters|workers|all]" 38 | exit 1 39 | ;; 40 | esac 41 | shift 42 | done 43 | 44 | if [ "$STOP_WORKERS" = true ]; then 45 | echo "[*] Deleting worker instances" 46 | WORKERS=$(gcloud compute --project=$PROJECT_ID instances list --filter="labels.role=worker && labels.session=$SESSION" --format="value(name)") 47 | 48 | if [ ! -z "$WORKERS" ]; then 49 | while read -r instance; do 50 | gcloud compute --project=$PROJECT_ID instances delete $instance --quiet --zone $ZONE & 51 | done <<< "$WORKERS" 52 | else 53 | echo "Nothing to delete" 54 | fi 55 | 56 | wait 57 | fi 58 | 59 | if [ "$STOP_MASTERS" = true ]; then 60 | echo "[*] Deleting master instances" 61 | MASTERS=$(gcloud compute --project=$PROJECT_ID instances list --filter="labels.role=master && labels.session=$SESSION" --format="value(name)") 62 | 63 | if [ ! -z "$MASTERS" ]; then 64 | while read -r instance; do 65 | gcloud compute --project=$PROJECT_ID instances delete $instance --quiet --zone $ZONE & 66 | done <<< "$MASTERS" 67 | else 68 | echo "Nothing to delete" 69 | fi 70 | 71 | wait 72 | fi 73 | 74 | if [ "$STOP_ROOT" = true ]; then 75 | echo "[*] Deleting root instance" 76 | ROOT=$(gcloud compute --project=$PROJECT_ID instances list --filter="labels.role=root && labels.session=$SESSION" --format="value(name)") 77 | if [ ! -z "$ROOT" ]; then 78 | gcloud compute --project=$PROJECT_ID instances delete $ROOT --quiet --zone $ZONE 79 | else 80 | echo "Nothing to delete" 81 | fi 82 | fi 83 | -------------------------------------------------------------------------------- /Cloud/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzilli in the Cloud 2 | 3 | Easy distributed fuzzing with Fuzzilli. 4 | 5 | # Docker/ 6 | 7 | Scripts and Dockerfiles to build Fuzzilli as well as any of the supported engines. The fuzzilli docker image can then be used for fuzzing locally or be uploaded to a docker registry for distributed fuzzing, see below. 8 | 9 | # GCE/ 10 | 11 | Scripts to setup and tear down distributed fuzzing with Fuzzilli on Google Compute Engine. 12 | 13 | # Triage/ 14 | 15 | Rudimentary crash triaging. 16 | -------------------------------------------------------------------------------- /Cloud/Triage/README.md: -------------------------------------------------------------------------------- 1 | # Triage 2 | 3 | Small script to help with crash triaging. 4 | 5 | ## Usage 6 | 7 | The script requires a local build of the target JS engine. For JavaScriptCore and Spidermonkey an ASAN build is recommended. Once the build is complete, simply run the script, providing it with the path to the JS shell, the path to the crashes/ directory that fuzzilli produced, and the commandline arguments for the JS shell (get those from the [respective profile](../../Sources/FuzzilliCli/Profiles)): 8 | 9 | ./check.sh ./crashes ~/WebKit/AsanBuild/Debug/bin/jsc --validateOptions=true --useConcurrentJIT=false --useConcurrentGC=false --thresholdForJITSoon=10 --thresholdForJITAfterWarmUp=10 --thresholdForOptimizeAfterWarmUp=100 --thresholdForOptimizeAfterLongWarmUp=100 --thresholdForOptimizeAfterLongWarmUp=100 --thresholdForFTLOptimizeAfterWarmUp=1000 --thresholdForFTLOptimizeSoon=1000 --gcAtEnd=true 10 | 11 | The file will produce a log with all output from stdout and stderr (which includes Asan crash reports). Afterwards, you can grep (and uniquify) the log for certain keywords such as 12 | - for JSC: "trap", "assert", "segv", "Sanitizer" 13 | - for Spidernmonkey: "trap", "assert", "segv", "Sanitizer" 14 | - for V8: "Debug check failed", "Check failed", "assert", "fatal", "received" 15 | -------------------------------------------------------------------------------- /Cloud/Triage/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ $# -lt 2 ]; then 6 | echo "Usage: $0 path/to/js/shell path/to/crashes [arguments, ...]" 7 | exit 1 8 | fi 9 | 10 | CRASHES_DIR=$1 11 | shift 12 | JS_SHELL=$1 13 | shift 14 | 15 | for f in $(find $1 -maxdepth 1 -name '*js'); do 16 | echo $f 17 | ASAN_OPTIONS=detect_leaks=0 timeout 5s $JS_SHELL $@ $f 18 | done &> log 19 | -------------------------------------------------------------------------------- /Compiler/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _esy 3 | *.merlin 4 | esy.lock 5 | node_modules 6 | fuzzilli_compiler.install -------------------------------------------------------------------------------- /Compiler/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV SHELL=bash 5 | 6 | RUN apt-get -y update && apt-get -y upgrade 7 | RUN apt-get install -y curl opam npm 8 | RUN npm install -g --unsafe-perm esy 9 | 10 | RUN useradd -m builder 11 | WORKDIR /home/builder 12 | USER builder 13 | 14 | ADD --chown=builder:builder ./ Compiler 15 | WORKDIR Compiler 16 | 17 | # Set up Opam & Ocaml properly, for OCaml v 4.10.0 18 | # Pin flow, and setup the package.json 19 | RUN opam init -a --disable-sandboxing 20 | RUN opam switch create 4.10.0 && \ 21 | eval $(opam env) && \ 22 | opam pin add -y flow_parser https://github.com/facebook/flow.git && \ 23 | sed -i 's/.*REPLACE ME.*/ "flow_parser": "link:\/home\/builder\/.opam\/4.10.0\/.opam-switch\/sources\/flow_parser\/flow_parser.opam"/' package.json 24 | 25 | # Install dependencies 26 | RUN esy install 27 | 28 | # And build! 29 | RUN esy build 30 | 31 | # Run the tests to verify that the compiler works correctly 32 | RUN esy x test 33 | 34 | # Finally, copy the compiler binary into the current directory for easy access 35 | RUN cp _esy/default/build/default/bin/fuzzilli_compiler.exe . -------------------------------------------------------------------------------- /Compiler/bin/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name fuzzilli_compiler) 3 | (public_name fuzzilli_compiler) 4 | (libraries compiler core flow_parser ocaml-protoc proto str) 5 | (preprocess (pps ppx_jane ppx_deriving ppx_deriving.show))) -------------------------------------------------------------------------------- /Compiler/bin/fuzzilli_compiler.ml: -------------------------------------------------------------------------------- 1 | let do_compile infile outfile ~(emit_ast : bool) ~(emit_builtins: bool) ~(v8_natives: bool) ~(use_placeholder: bool)= 2 | try 3 | let file_string = Core.In_channel.read_all infile in 4 | let (prog, err) = Compiler.string_to_flow_ast file_string in 5 | let (loc_type, a) = prog in 6 | let prog_string = Compiler.print_ast_statement_list a.statements in 7 | if emit_ast then 8 | print_endline ("Provided AST: \n" ^ prog_string ^ "\n") 9 | else (); 10 | let inst_list = Compiler.flow_ast_to_inst_list prog emit_builtins v8_natives use_placeholder in 11 | let prog = Compiler.inst_list_to_prog inst_list in 12 | Compiler.write_proto_obj_to_file prog outfile 13 | with 14 | Invalid_argument x -> print_endline ("Invalid arg: " ^ x) 15 | | Parse_error.Error y -> 16 | let _, err_list = List.split y in 17 | Printf.printf "Compiler Error %s\n" (Parse_error.PP.error (List.hd err_list)) 18 | | y -> print_endline ("Other compiler error" ^ (Core.Exn.to_string y)) 19 | 20 | let command = 21 | let open Core in 22 | Command.basic 23 | ~summary:"Compile a JS source file to Fuzzil" 24 | Command.Let_syntax.( 25 | let%map_open 26 | infile = (anon ("infile" %: Filename.arg_type)) 27 | and outfile = (anon ("outfile" %: string)) 28 | and emit_ast = flag "-ast" no_arg ~doc: "Print the Flow_ast" 29 | and emit_builtins = flag "-builtins" no_arg ~doc: "Print all builtins encountered" 30 | and v8_natives = flag "-v8-natives" no_arg ~doc: "Include v8 natives, as funtions without the leading %. Requires the builtins be included in the fuzzilli profile for v8. Currently only uses a hardcoded list in util.ml" 31 | and use_placeholder = flag "-use-placeholder" no_arg ~doc: "Replaces each unknown builtin with 'placeholder'." 32 | in 33 | fun () -> do_compile infile outfile ~emit_ast ~emit_builtins ~v8_natives ~use_placeholder) 34 | 35 | let () = 36 | Core.Command.run command 37 | -------------------------------------------------------------------------------- /Compiler/build-pbs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ocaml-protoc -I ../Sources/Fuzzilli/Protobuf/ ../Sources/Fuzzilli/Protobuf/operations.proto -ml_out ./src/proto 3 | ocaml-protoc -I ../Sources/Fuzzilli/Protobuf/ ../Sources/Fuzzilli/Protobuf/program.proto -ml_out ./src/proto 4 | ocaml-protoc -I ../Sources/Fuzzilli/Protobuf/ ../Sources/Fuzzilli/Protobuf/typesystem.proto -ml_out ./src/proto 5 | -------------------------------------------------------------------------------- /Compiler/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /Compiler/fuzzilli_compiler.opam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Compiler/fuzzilli_compiler.opam -------------------------------------------------------------------------------- /Compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fuzzilli_compiler", 3 | "version": "0.1", 4 | "esy": { 5 | "build": "dune build -p #{self.name}" 6 | }, 7 | "dependencies": { 8 | "@opam/core": "*", 9 | "@opam/ocaml-lsp-server": "*", 10 | "@opam/ocaml-protoc": "*", 11 | "flow_parser": "*", 12 | "ocaml": "~4.10", 13 | "@opam/alcotest": "*", 14 | "@opam/uuidm": "*" 15 | }, 16 | "resolutions": { 17 | "flow_parser": "link:{REPLACE ME PATH HERE}/.opam/4.10.0/.opam-switch/sources/flow_parser/flow_parser.opam" 18 | }, 19 | "scripts": { 20 | "build-pbs": "./build-pbs.sh" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Compiler/src/VariableScope.mli: -------------------------------------------------------------------------------- 1 | val get_vars_to_hoist : (Loc.t, Loc.t) Flow_ast.Statement.t list -> string list * string list -------------------------------------------------------------------------------- /Compiler/src/compiler.ml: -------------------------------------------------------------------------------- 1 | 2 | (* Translate an ast to a list of instructions *) 3 | let flow_ast_to_inst_list = Translate.flow_ast_to_inst_list 4 | 5 | (* Util Interface *) 6 | let print_ast_statement_list = Util.print_statement_list 7 | 8 | (* Run the flow_ast parser *) 9 | let string_to_flow_ast = Util.string_to_flow_ast 10 | 11 | (* Output a generated Protobuf*) 12 | let write_proto_obj_to_file = Util.write_proto_obj_to_file 13 | 14 | (* Wrap a list of instructions in the program structure *) 15 | let inst_list_to_prog = Util.inst_list_to_prog 16 | 17 | (* Pretty print support *) 18 | let pp_instruction = Program_pp.pp_instruction 19 | 20 | let init_test_builder = ProgramBuilder.init_builder false false false 21 | 22 | (* Give the test interface access to the protobufs *) 23 | module Program_types = Program_types 24 | module Operations_types = Operations_types 25 | module Typesystem_types = Typesystem_types 26 | 27 | (* Make ProgramBuilder available to our test suite*) 28 | module ProgramBuilder = ProgramBuilder -------------------------------------------------------------------------------- /Compiler/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name compiler) 3 | (libraries core flow_parser ocaml-protoc proto str) 4 | (preprocess (pps ppx_jane ppx_deriving ppx_deriving.show))) -------------------------------------------------------------------------------- /Compiler/src/proto/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name proto) 3 | (wrapped false) 4 | (libraries ocaml-protoc) 5 | (preprocess (pps ppx_gen_rec ppx_deriving.std sedlex.ppx))) 6 | -------------------------------------------------------------------------------- /Compiler/src/proto/program_pb.mli: -------------------------------------------------------------------------------- 1 | (** program.proto Binary Encoding *) 2 | 3 | 4 | (** {2 Protobuf Encoding} *) 5 | 6 | val encode_instruction_operation : Program_types.instruction_operation -> Pbrt.Encoder.t -> unit 7 | (** [encode_instruction_operation v encoder] encodes [v] with the given [encoder] *) 8 | 9 | val encode_instruction : Program_types.instruction -> Pbrt.Encoder.t -> unit 10 | (** [encode_instruction v encoder] encodes [v] with the given [encoder] *) 11 | 12 | val encode_type_collection_status : Program_types.type_collection_status -> Pbrt.Encoder.t -> unit 13 | (** [encode_type_collection_status v encoder] encodes [v] with the given [encoder] *) 14 | 15 | val encode_type_quality : Program_types.type_quality -> Pbrt.Encoder.t -> unit 16 | (** [encode_type_quality v encoder] encodes [v] with the given [encoder] *) 17 | 18 | val encode_type_info : Program_types.type_info -> Pbrt.Encoder.t -> unit 19 | (** [encode_type_info v encoder] encodes [v] with the given [encoder] *) 20 | 21 | val encode_program : Program_types.program -> Pbrt.Encoder.t -> unit 22 | (** [encode_program v encoder] encodes [v] with the given [encoder] *) 23 | 24 | 25 | (** {2 Protobuf Decoding} *) 26 | 27 | val decode_instruction_operation : Pbrt.Decoder.t -> Program_types.instruction_operation 28 | (** [decode_instruction_operation decoder] decodes a [instruction_operation] value from [decoder] *) 29 | 30 | val decode_instruction : Pbrt.Decoder.t -> Program_types.instruction 31 | (** [decode_instruction decoder] decodes a [instruction] value from [decoder] *) 32 | 33 | val decode_type_collection_status : Pbrt.Decoder.t -> Program_types.type_collection_status 34 | (** [decode_type_collection_status decoder] decodes a [type_collection_status] value from [decoder] *) 35 | 36 | val decode_type_quality : Pbrt.Decoder.t -> Program_types.type_quality 37 | (** [decode_type_quality decoder] decodes a [type_quality] value from [decoder] *) 38 | 39 | val decode_type_info : Pbrt.Decoder.t -> Program_types.type_info 40 | (** [decode_type_info decoder] decodes a [type_info] value from [decoder] *) 41 | 42 | val decode_program : Pbrt.Decoder.t -> Program_types.program 43 | (** [decode_program decoder] decodes a [program] value from [decoder] *) 44 | -------------------------------------------------------------------------------- /Compiler/src/proto/program_pp.mli: -------------------------------------------------------------------------------- 1 | (** program.proto Pretty Printing *) 2 | 3 | 4 | (** {2 Formatters} *) 5 | 6 | val pp_instruction_operation : Format.formatter -> Program_types.instruction_operation -> unit 7 | (** [pp_instruction_operation v] formats v *) 8 | 9 | val pp_instruction : Format.formatter -> Program_types.instruction -> unit 10 | (** [pp_instruction v] formats v *) 11 | 12 | val pp_type_collection_status : Format.formatter -> Program_types.type_collection_status -> unit 13 | (** [pp_type_collection_status v] formats v *) 14 | 15 | val pp_type_quality : Format.formatter -> Program_types.type_quality -> unit 16 | (** [pp_type_quality v] formats v *) 17 | 18 | val pp_type_info : Format.formatter -> Program_types.type_info -> unit 19 | (** [pp_type_info v] formats v *) 20 | 21 | val pp_program : Format.formatter -> Program_types.program -> unit 22 | (** [pp_program v] formats v *) 23 | -------------------------------------------------------------------------------- /Compiler/src/proto/typesystem_pb.mli: -------------------------------------------------------------------------------- 1 | (** typesystem.proto Binary Encoding *) 2 | 3 | 4 | (** {2 Protobuf Encoding} *) 5 | 6 | val encode_type_ext : Typesystem_types.type_ext -> Pbrt.Encoder.t -> unit 7 | (** [encode_type_ext v encoder] encodes [v] with the given [encoder] *) 8 | 9 | val encode_type_ : Typesystem_types.type_ -> Pbrt.Encoder.t -> unit 10 | (** [encode_type_ v encoder] encodes [v] with the given [encoder] *) 11 | 12 | val encode_type_extension : Typesystem_types.type_extension -> Pbrt.Encoder.t -> unit 13 | (** [encode_type_extension v encoder] encodes [v] with the given [encoder] *) 14 | 15 | val encode_function_signature : Typesystem_types.function_signature -> Pbrt.Encoder.t -> unit 16 | (** [encode_function_signature v encoder] encodes [v] with the given [encoder] *) 17 | 18 | 19 | (** {2 Protobuf Decoding} *) 20 | 21 | val decode_type_ext : Pbrt.Decoder.t -> Typesystem_types.type_ext 22 | (** [decode_type_ext decoder] decodes a [type_ext] value from [decoder] *) 23 | 24 | val decode_type_ : Pbrt.Decoder.t -> Typesystem_types.type_ 25 | (** [decode_type_ decoder] decodes a [type_] value from [decoder] *) 26 | 27 | val decode_type_extension : Pbrt.Decoder.t -> Typesystem_types.type_extension 28 | (** [decode_type_extension decoder] decodes a [type_extension] value from [decoder] *) 29 | 30 | val decode_function_signature : Pbrt.Decoder.t -> Typesystem_types.function_signature 31 | (** [decode_function_signature decoder] decodes a [function_signature] value from [decoder] *) 32 | -------------------------------------------------------------------------------- /Compiler/src/proto/typesystem_pp.ml: -------------------------------------------------------------------------------- 1 | [@@@ocaml.warning "-27-30-39"] 2 | 3 | let rec pp_type_ext fmt (v:Typesystem_types.type_ext) = 4 | match v with 5 | | Typesystem_types.Extension_idx x -> Format.fprintf fmt "@[Extension_idx(%a)@]" Pbrt.Pp.pp_int32 x 6 | | Typesystem_types.Extension x -> Format.fprintf fmt "@[Extension(%a)@]" pp_type_extension x 7 | 8 | and pp_type_ fmt (v:Typesystem_types.type_) = 9 | let pp_i fmt () = 10 | Format.pp_open_vbox fmt 1; 11 | Pbrt.Pp.pp_record_field "definite_type" Pbrt.Pp.pp_int32 fmt v.Typesystem_types.definite_type; 12 | Pbrt.Pp.pp_record_field "possible_type" Pbrt.Pp.pp_int32 fmt v.Typesystem_types.possible_type; 13 | Pbrt.Pp.pp_record_field "ext" pp_type_ext fmt v.Typesystem_types.ext; 14 | Format.pp_close_box fmt () 15 | in 16 | Pbrt.Pp.pp_brk pp_i fmt () 17 | 18 | and pp_type_extension fmt (v:Typesystem_types.type_extension) = 19 | let pp_i fmt () = 20 | Format.pp_open_vbox fmt 1; 21 | Pbrt.Pp.pp_record_field "properties" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.Typesystem_types.properties; 22 | Pbrt.Pp.pp_record_field "methods" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.Typesystem_types.methods; 23 | Pbrt.Pp.pp_record_field "group" Pbrt.Pp.pp_string fmt v.Typesystem_types.group; 24 | Pbrt.Pp.pp_record_field "signature" (Pbrt.Pp.pp_option pp_function_signature) fmt v.Typesystem_types.signature; 25 | Format.pp_close_box fmt () 26 | in 27 | Pbrt.Pp.pp_brk pp_i fmt () 28 | 29 | and pp_function_signature fmt (v:Typesystem_types.function_signature) = 30 | let pp_i fmt () = 31 | Format.pp_open_vbox fmt 1; 32 | Pbrt.Pp.pp_record_field "input_types" (Pbrt.Pp.pp_list pp_type_) fmt v.Typesystem_types.input_types; 33 | Pbrt.Pp.pp_record_field "output_type" (Pbrt.Pp.pp_option pp_type_) fmt v.Typesystem_types.output_type; 34 | Format.pp_close_box fmt () 35 | in 36 | Pbrt.Pp.pp_brk pp_i fmt () 37 | -------------------------------------------------------------------------------- /Compiler/src/proto/typesystem_pp.mli: -------------------------------------------------------------------------------- 1 | (** typesystem.proto Pretty Printing *) 2 | 3 | 4 | (** {2 Formatters} *) 5 | 6 | val pp_type_ext : Format.formatter -> Typesystem_types.type_ext -> unit 7 | (** [pp_type_ext v] formats v *) 8 | 9 | val pp_type_ : Format.formatter -> Typesystem_types.type_ -> unit 10 | (** [pp_type_ v] formats v *) 11 | 12 | val pp_type_extension : Format.formatter -> Typesystem_types.type_extension -> unit 13 | (** [pp_type_extension v] formats v *) 14 | 15 | val pp_function_signature : Format.formatter -> Typesystem_types.function_signature -> unit 16 | (** [pp_function_signature v] formats v *) 17 | -------------------------------------------------------------------------------- /Compiler/src/proto/typesystem_types.ml: -------------------------------------------------------------------------------- 1 | [@@@ocaml.warning "-27-30-39"] 2 | 3 | 4 | type type_ext = 5 | | Extension_idx of int32 6 | | Extension of type_extension 7 | 8 | and type_ = { 9 | definite_type : int32; 10 | possible_type : int32; 11 | ext : type_ext; 12 | } 13 | 14 | and type_extension = { 15 | properties : string list; 16 | methods : string list; 17 | group : string; 18 | signature : function_signature option; 19 | } 20 | 21 | and function_signature = { 22 | input_types : type_ list; 23 | output_type : type_ option; 24 | } 25 | 26 | let rec default_type_ext () : type_ext = Extension_idx (0l) 27 | 28 | and default_type_ 29 | ?definite_type:((definite_type:int32) = 0l) 30 | ?possible_type:((possible_type:int32) = 0l) 31 | ?ext:((ext:type_ext) = Extension_idx (0l)) 32 | () : type_ = { 33 | definite_type; 34 | possible_type; 35 | ext; 36 | } 37 | 38 | and default_type_extension 39 | ?properties:((properties:string list) = []) 40 | ?methods:((methods:string list) = []) 41 | ?group:((group:string) = "") 42 | ?signature:((signature:function_signature option) = None) 43 | () : type_extension = { 44 | properties; 45 | methods; 46 | group; 47 | signature; 48 | } 49 | 50 | and default_function_signature 51 | ?input_types:((input_types:type_ list) = []) 52 | ?output_type:((output_type:type_ option) = None) 53 | () : function_signature = { 54 | input_types; 55 | output_type; 56 | } 57 | -------------------------------------------------------------------------------- /Compiler/src/proto/typesystem_types.mli: -------------------------------------------------------------------------------- 1 | (** typesystem.proto Types *) 2 | 3 | 4 | 5 | (** {2 Types} *) 6 | 7 | type type_ext = 8 | | Extension_idx of int32 9 | | Extension of type_extension 10 | 11 | and type_ = { 12 | definite_type : int32; 13 | possible_type : int32; 14 | ext : type_ext; 15 | } 16 | 17 | and type_extension = { 18 | properties : string list; 19 | methods : string list; 20 | group : string; 21 | signature : function_signature option; 22 | } 23 | 24 | and function_signature = { 25 | input_types : type_ list; 26 | output_type : type_ option; 27 | } 28 | 29 | 30 | (** {2 Default values} *) 31 | 32 | val default_type_ext : unit -> type_ext 33 | (** [default_type_ext ()] is the default value for type [type_ext] *) 34 | 35 | val default_type_ : 36 | ?definite_type:int32 -> 37 | ?possible_type:int32 -> 38 | ?ext:type_ext -> 39 | unit -> 40 | type_ 41 | (** [default_type_ ()] is the default value for type [type_] *) 42 | 43 | val default_type_extension : 44 | ?properties:string list -> 45 | ?methods:string list -> 46 | ?group:string -> 47 | ?signature:function_signature option -> 48 | unit -> 49 | type_extension 50 | (** [default_type_extension ()] is the default value for type [type_extension] *) 51 | 52 | val default_function_signature : 53 | ?input_types:type_ list -> 54 | ?output_type:type_ option -> 55 | unit -> 56 | function_signature 57 | (** [default_function_signature ()] is the default value for type [function_signature] *) 58 | -------------------------------------------------------------------------------- /Compiler/src/translate.mli: -------------------------------------------------------------------------------- 1 | (* Converts a Flow AST program to a program taken as input for protobuf generation*) 2 | val flow_ast_to_inst_list : (Loc.t, Loc.t) Flow_ast.Program.t -> bool -> bool -> bool -> Program_types.instruction list -------------------------------------------------------------------------------- /Compiler/src/util.mli: -------------------------------------------------------------------------------- 1 | val string_to_flow_ast : string -> (Loc.t, Loc.t) Flow_ast.Program.t * (Loc.t * Parse_error.t) list 2 | 3 | val encode_newline : string -> string 4 | 5 | val convert_comp_op : Flow_ast.Expression.Binary.operator -> Operations_types.compare 6 | 7 | val is_compare_op : Flow_ast.Expression.Binary.operator -> bool 8 | 9 | val print_statement : ('M, 'T) Flow_ast.Statement.t -> string 10 | 11 | val print_unary_expression : ('M, 'T) Flow_ast.Expression.Unary.t -> string 12 | 13 | val print_unary_operator : Flow_ast.Expression.Unary.operator -> string 14 | 15 | val print_binary_operator : Flow_ast.Expression.Binary.operator -> string 16 | 17 | val print_logical_operator : Flow_ast.Expression.Logical.operator -> string 18 | 19 | val print_expression : ('M, 'T) Flow_ast.Expression.t -> string 20 | 21 | val print_statement_list: (('M, 'T) Flow_ast.Statement.t) list -> string 22 | 23 | val print_literal: ('T) Flow_ast.Literal.t -> string 24 | 25 | val trim_flow_ast_string : string -> string 26 | 27 | val write_proto_obj_to_file : Program_types.program -> string -> unit 28 | 29 | val inst_list_to_prog : Program_types.instruction list -> Program_types.program 30 | 31 | val regex_flag_str_to_int : string -> int32 32 | 33 | val is_supported_builtin : string -> bool -> bool -------------------------------------------------------------------------------- /Compiler/supportedBuiltins.txt: -------------------------------------------------------------------------------- 1 | Reflect 2 | PrepareFunctionForOptimization 3 | Promise 4 | Infinity 5 | isNaN 6 | Int16Array 7 | Symbol 8 | Object 9 | Int32Array 10 | Map 11 | WeakMap 12 | isFinite 13 | parseInt 14 | Math 15 | Int8Array 16 | Set 17 | Function 18 | RegExp 19 | OptimizeOsr 20 | GetOptimizationStatus 21 | Uint32Array 22 | JSON 23 | String 24 | parseFloat 25 | WeakSet 26 | Uint8Array 27 | BigInt 28 | undefined 29 | NaN 30 | NeverOptimizeFunction 31 | DeoptimizeNow 32 | ToFastProperties 33 | Number 34 | Proxy 35 | ArrayBuffer 36 | gc 37 | DeoptimizeFunction 38 | OptimizeFunctionOnNextCall 39 | Float32Array 40 | eval 41 | Float64Array 42 | DataView 43 | Uint16Array 44 | Array 45 | this 46 | arguments 47 | Uint8ClampedArray 48 | Boolean -------------------------------------------------------------------------------- /Compiler/test/array_assign.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 12; 6 | const v1 = [v0,v0,v0]; 7 | const v2 = 10; 8 | v1[v2 + 20] = 30;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int12_temp, load_int_12 = build_load_integer 12L builder in 13 | let array_temp, load_array = build_create_array [int12_temp; int12_temp; int12_temp] builder in 14 | let int10_temp, load_int_10 = build_load_integer 10L builder in 15 | let int20_temp, load_int_20 = build_load_integer 20L builder in 16 | let add_res_temp, add_inst = build_binary_op int10_temp int20_temp Plus builder in 17 | let int30_temp, load_int_30 = build_load_integer 30L builder in 18 | let store_comp_inst = build_store_computed_prop array_temp add_res_temp int30_temp builder in 19 | let res = [load_int_12; load_array; load_int_10; load_int_20; add_inst; load_int_30; store_comp_inst] in 20 | List.map inst_to_prog_inst res 21 | 22 | let test () = 23 | let (ast, errors) = Compiler.string_to_flow_ast input in 24 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 25 | Alcotest.(check (list Util.inst_testable)) "array_assign" correct prog -------------------------------------------------------------------------------- /Compiler/test/array_assign_sugared.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 12; 6 | const v1 = [v0,v0,v0]; 7 | const v2 = 10; 8 | v1[v2 + 20] += 30;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int12_temp, load_int_12 = build_load_integer 12L builder in 13 | let array_temp, load_array = build_create_array [int12_temp; int12_temp; int12_temp] builder in 14 | let int10_temp, load_int_10 = build_load_integer 10L builder in 15 | let int20_temp, load_int_20 = build_load_integer 20L builder in 16 | let add_res_temp, add_inst = build_binary_op int10_temp int20_temp Plus builder in 17 | let int30_temp, load_int_30 = build_load_integer 30L builder in 18 | let comp_prop_temp, load_comp_prop_inst = build_load_computed_prop array_temp add_res_temp builder in 19 | let add_res_temp2, add_inst2 = build_binary_op comp_prop_temp int30_temp Plus builder in 20 | let store_comp_inst = build_store_computed_prop array_temp add_res_temp add_res_temp2 builder in 21 | let res = [load_int_12; load_array; load_int_10; load_int_20; add_inst; load_int_30; load_comp_prop_inst; add_inst2; store_comp_inst] in 22 | List.map inst_to_prog_inst res 23 | 24 | let test () = 25 | let (ast, errors) = Compiler.string_to_flow_ast input in 26 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 27 | Alcotest.(check (list Util.inst_testable)) "array_assign_sugared" correct prog -------------------------------------------------------------------------------- /Compiler/test/array_decl.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "var [r, g, f] = [20, 15, 35]; 6 | " 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let _, l0_inst = build_load_integer 20L builder in 11 | let _, l1_inst = build_load_integer 15L builder in 12 | let _, l2_inst = build_load_integer 35L builder in 13 | let res = [l0_inst; l1_inst; l2_inst] in 14 | List.map inst_to_prog_inst res 15 | 16 | let test () = 17 | let (ast, errors) = Compiler.string_to_flow_ast input in 18 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 19 | Alcotest.(check (list Util.inst_testable)) "array_decl" correct prog 20 | -------------------------------------------------------------------------------- /Compiler/test/array_hole.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let a = [1,,1];" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let int_temp, load_int = build_load_integer 1L builder in 10 | let undef_temp, load_undef_inst = build_load_undefined builder in 11 | let int_temp2, load_int_2 = build_load_integer 1L builder in 12 | let _, create_array_inst = build_create_array [int_temp; undef_temp; int_temp2] builder in 13 | let res = [load_int; load_undef_inst; load_int_2; create_array_inst] in 14 | List.map inst_to_prog_inst res 15 | let test () = 16 | let (ast, errors) = Compiler.string_to_flow_ast input in 17 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 18 | Alcotest.(check (list Util.inst_testable)) "array_hole" correct prog -------------------------------------------------------------------------------- /Compiler/test/array_spread.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 0; 6 | const v1 = [v0,...v0,];" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let int_temp, load_int = build_load_integer 0L builder in 11 | let _, create_array_inst = build_create_array_with_spread [int_temp; int_temp] [false; true] builder in 12 | let res = [load_int; create_array_inst] in 13 | List.map inst_to_prog_inst res 14 | 15 | let test () = 16 | let (ast, errors) = Compiler.string_to_flow_ast input in 17 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 18 | Alcotest.(check (list Util.inst_testable)) "array_spread" correct prog -------------------------------------------------------------------------------- /Compiler/test/basic_break.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | let input = 4 | "const v0 = 1; 5 | while(v0){ 6 | break; 7 | }" 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | let int_temp, load_int = build_load_integer 1L builder in 12 | let int_temp0, load_int0 = build_load_integer 0L builder in 13 | let begin_while_inst = build_begin_while int_temp int_temp0 NotEqual builder in 14 | let break_inst = build_break_op builder in 15 | let reassign_inst = build_reassign_op int_temp int_temp builder in 16 | let end_while_inst = build_end_while builder in 17 | let res = [load_int; load_int0; begin_while_inst; break_inst; reassign_inst; end_while_inst] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "basic_break" correct prog -------------------------------------------------------------------------------- /Compiler/test/basic_compare_test.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 1 == 1; 6 | const v1 = 1 === 1; 7 | const v2 = 2 >= 2; 8 | const v3 = 7 < 9;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_1, load_1 = build_load_integer 1L builder in 13 | let int_1_2, load_1_2 = build_load_integer 1L builder in 14 | let _, equal_inst = build_compare_op int_1 int_1_2 Equal builder in 15 | let int_1_3, load_1_3 = build_load_integer 1L builder in 16 | let int_1_4, load_1_4 = build_load_integer 1L builder in 17 | let _, strict_equal_inst = build_compare_op int_1_3 int_1_4 StrictEqual builder in 18 | let int_2_0, load_2_0 = build_load_integer 2L builder in 19 | let int_2_1, load_2_1 = build_load_integer 2L builder in 20 | let _, greater_eq_inst = build_compare_op int_2_0 int_2_1 GreaterThanEqual builder in 21 | let int_7, load_7 = build_load_integer 7L builder in 22 | let int_9, load_9 = build_load_integer 9L builder in 23 | let _, less_than_inst = build_compare_op int_7 int_9 LessThan builder in 24 | let res = [load_1; load_1_2; equal_inst; load_1_3; load_1_4; strict_equal_inst; load_2_0; load_2_1; greater_eq_inst; load_7; load_9; less_than_inst] in 25 | List.map inst_to_prog_inst res 26 | 27 | let test () = 28 | let (ast, errors) = Compiler.string_to_flow_ast input in 29 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 30 | Alcotest.(check (list Util.inst_testable)) "basic_compare_test" correct prog -------------------------------------------------------------------------------- /Compiler/test/basic_continue.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "while(1){ 6 | continue; 7 | } 8 | " 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_temp_1, load_int1 = build_load_integer 1L builder in 13 | let int_temp_0, load_int0 = build_load_integer 0L builder in 14 | let begin_while = build_begin_while int_temp_1 int_temp_0 NotEqual builder in 15 | let continue_inst = build_continue builder in 16 | let int_temp_1_2, load_int2 = build_load_integer 1L builder in 17 | let reassign_inst = build_reassign_op int_temp_1 int_temp_1_2 builder in 18 | let end_while = build_end_while builder in 19 | let res = [load_int1; load_int0; begin_while; continue_inst; load_int2; reassign_inst; end_while] in 20 | List.map inst_to_prog_inst res 21 | 22 | let test () = 23 | let (ast, errors) = Compiler.string_to_flow_ast input in 24 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 25 | Alcotest.(check (list Util.inst_testable)) "basic_continue" correct prog -------------------------------------------------------------------------------- /Compiler/test/basic_for.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "for(let v0 = 2; v0 < 10; v0 = v0 + 1){ 6 | let v2 = v0 + 12; 7 | }" 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | let int_2_temp, load_int2 = build_load_integer 2L builder in 12 | let int_10_temp, load_int10 = build_load_integer 10L builder in 13 | let compare_temp, first_compare_inst = build_compare_op int_2_temp int_10_temp LessThan builder in 14 | let int_0_temp, load_int0 = build_load_integer 0L builder in 15 | let begin_while = build_begin_while compare_temp int_0_temp NotEqual builder in 16 | let int_12_temp, load_int12 = build_load_integer 12L builder in 17 | let add_temp, add_inst = build_binary_op int_2_temp int_12_temp Plus builder in 18 | let int_1_temp, load_int1 = build_load_integer 1L builder in 19 | let add_temp2, add_inst2 = build_binary_op int_2_temp int_1_temp Plus builder in 20 | let reassign_op = build_reassign_op int_2_temp add_temp2 builder in 21 | let int_10_temp2, load_int102 = build_load_integer 10L builder in 22 | let compare_temp2, second_compare_inst = build_compare_op int_2_temp int_10_temp2 LessThan builder in 23 | let reassign_op2 = build_reassign_op compare_temp compare_temp2 builder in 24 | let end_while = build_end_while builder in 25 | let res = [load_int2; load_int10; first_compare_inst; load_int0; begin_while; load_int12; add_inst; load_int1; add_inst2; reassign_op; load_int102; second_compare_inst; reassign_op2; end_while] in 26 | List.map inst_to_prog_inst res 27 | 28 | let test () = 29 | let (ast, errors) = Compiler.string_to_flow_ast input in 30 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 31 | Alcotest.(check (list Util.inst_testable)) "basic_for" correct prog 32 | -------------------------------------------------------------------------------- /Compiler/test/basic_func_call.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = isNaN(0);" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let builtin_temp, load_builtin = build_load_builtin "isNaN" builder in 10 | let temp_0, load_int = build_load_integer 0L builder in 11 | let _, call_inst = build_call builtin_temp [temp_0] builder in 12 | let res = [load_builtin; load_int; call_inst] in 13 | List.map inst_to_prog_inst res 14 | 15 | let test () = 16 | let (ast, errors) = Compiler.string_to_flow_ast input in 17 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 18 | Alcotest.(check (list Util.inst_testable)) "basic_func_call" correct prog -------------------------------------------------------------------------------- /Compiler/test/basic_func_ret.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "function v1(v2,v3) { 6 | let v4 = v2 * v3; 7 | return v4; 8 | } 9 | " 10 | 11 | let correct = 12 | let builder = init_builder false false false in 13 | let func_temp = get_new_intermed_temp builder in 14 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp ["v2"; "v3"] None false false false builder in 15 | (* TODO: This needs to be updated along with the function builder interface *) 16 | let v2_temp = match lookup_var_name builder "v2" with 17 | InScope x -> x 18 | | NotFound -> raise (Invalid_argument "improper variable lookup") in 19 | let v3_temp = match lookup_var_name builder "v3" with 20 | InScope x -> x 21 | | NotFound -> raise (Invalid_argument "improper variable lookup") in 22 | let bin_temp, bin_inst = build_binary_op v2_temp v3_temp Mult builder in 23 | let ret_inst = build_return_op bin_temp builder in 24 | let res = [begin_func_inst; bin_inst; ret_inst; end_func_inst] in 25 | List.map inst_to_prog_inst res 26 | 27 | let test () = 28 | let (ast, errors) = Compiler.string_to_flow_ast input in 29 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 30 | Alcotest.(check (list Util.inst_testable)) "basic_func_ret" correct prog -------------------------------------------------------------------------------- /Compiler/test/basic_while.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 0; 6 | const v1 = 20; 7 | while(v0 <= v1){ 8 | v0 += 1; 9 | }" 10 | 11 | let correct = 12 | let builder = init_builder false false false in 13 | let int_0, load_int_0 = build_load_integer 0L builder in 14 | let int_20, load_int_20 = build_load_integer 20L builder in 15 | let compare_temp, compare_inst_0 = build_compare_op int_0 int_20 LessThanEqual builder in 16 | let int_0_2, load_int_0_2 = build_load_integer 0L builder in 17 | let begin_while = build_begin_while compare_temp int_0_2 NotEqual builder in 18 | let int_1, load_int_1 = build_load_integer 1L builder in 19 | let bin_temp, bin_op = build_binary_op int_0 int_1 Plus builder in 20 | let reassign_op = build_reassign_op int_0 bin_temp builder in 21 | let compare_temp2, compare_inst2 = build_compare_op int_0 int_20 LessThanEqual builder in 22 | let reassign_op2 = build_reassign_op compare_temp compare_temp2 builder in 23 | let end_while = build_end_while builder in 24 | let res = [load_int_0; load_int_20; compare_inst_0; load_int_0_2; begin_while; load_int_1; bin_op; reassign_op; compare_inst2; reassign_op2; end_while] in 25 | List.map inst_to_prog_inst res 26 | 27 | let test () = 28 | let (ast, errors) = Compiler.string_to_flow_ast input in 29 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 30 | Alcotest.(check (list Util.inst_testable)) "basic_while" correct prog -------------------------------------------------------------------------------- /Compiler/test/bitwise_ops.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 1 | 2; 6 | const v1 = 3 & 4; 7 | " 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | let temp_1, load_1_temp = build_load_integer 1L builder in 12 | let temp_2, load_2_temp = build_load_integer 2L builder in 13 | let _, or_inst = build_binary_op temp_1 temp_2 BitOr builder in 14 | let temp_3, load_3_temp = build_load_integer 3L builder in 15 | let temp_4, load_4_temp = build_load_integer 4L builder in 16 | let _, and_inst = build_binary_op temp_3 temp_4 BitAnd builder in 17 | let res = [load_1_temp; load_2_temp; or_inst; load_3_temp; load_4_temp; and_inst] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "bitwise_ops" correct prog -------------------------------------------------------------------------------- /Compiler/test/create_array.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 15; 6 | const v1 = 20; 7 | const v2 = [v1,v0]; 8 | " 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_15_temp, load_int_15 = build_load_integer 15L builder in 13 | let int_20_temp, load_int_20 = build_load_integer 20L builder in 14 | let _, create_array = build_create_array [int_20_temp; int_15_temp] builder in 15 | let res = [load_int_15; load_int_20; create_array] in 16 | List.map inst_to_prog_inst res 17 | 18 | let test () = 19 | let (ast, errors) = Compiler.string_to_flow_ast input in 20 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 21 | Alcotest.(check (list Util.inst_testable)) "create_array" correct prog -------------------------------------------------------------------------------- /Compiler/test/del_test.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | 5 | let input = 6 | "const v0 = 1337; 7 | const v1 = [v0]; 8 | delete v1[0]; 9 | const v10 = \"function\"; 10 | delete v10.length; 11 | " 12 | 13 | let correct = 14 | let builder = init_builder false false false in 15 | let int1337, load_1337 = build_load_integer 1337L builder in 16 | let arr_temp, build_array = build_create_array [int1337] builder in 17 | let zero_temp, load_zero_temp = build_load_integer 0L builder in 18 | let _, del_inst = build_delete_computed_prop arr_temp zero_temp builder in 19 | let string_temp, load_string_temp = build_load_string "function" builder in 20 | let _, del_inst2 = build_delete_prop string_temp "length" builder in 21 | let res = [load_1337; build_array; load_zero_temp; del_inst; load_string_temp; del_inst2] in 22 | List.map inst_to_prog_inst res 23 | 24 | let test () = 25 | let (ast, errors) = Compiler.string_to_flow_ast input in 26 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 27 | Alcotest.(check (list Util.inst_testable)) "del_test" correct prog -------------------------------------------------------------------------------- /Compiler/test/do_while.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 0; 6 | do{ 7 | v0 = v0 + 1; 8 | }while(v0 < 10); 9 | " 10 | 11 | let correct = 12 | let builder = init_builder false false false in 13 | 14 | let int_0, load_int_0 = build_load_integer 0L builder in 15 | let int_0_1, load_int_0_1 = build_load_integer 0L builder in 16 | let dup_var, dup_inst = build_dup_op int_0_1 builder in 17 | let do_while_inst = build_begin_do_while dup_var int_0_1 NotEqual builder in 18 | let int_1, load_int_1 = build_load_integer 1L builder in 19 | let add_temp, add_inst = build_binary_op int_0 int_1 Plus builder in 20 | let reassign_inst = build_reassign_op int_0 add_temp builder in 21 | let int_10, load_int_10 = build_load_integer 10L builder in 22 | let compare_temp, compare_inst = build_compare_op int_0 int_10 LessThan builder in 23 | let reassign_inst_2 = build_reassign_op dup_var compare_temp builder in 24 | let end_do_while_inst = build_end_do_while builder in 25 | let res = [load_int_0; load_int_0_1; dup_inst; do_while_inst; load_int_1; add_inst; reassign_inst; load_int_10; 26 | compare_inst; reassign_inst_2; end_do_while_inst] in 27 | List.map inst_to_prog_inst res 28 | 29 | let test () = 30 | let (ast, errors) = Compiler.string_to_flow_ast input in 31 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 32 | Alcotest.(check (list Util.inst_testable)) "do_while" correct prog -------------------------------------------------------------------------------- /Compiler/test/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name test) 3 | (public_name test) 4 | (libraries alcotest compiler) 5 | (preprocess (pps ppx_deriving.show))) -------------------------------------------------------------------------------- /Compiler/test/empty_assignment_scope.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | " 6 | var x; 7 | while (x != 0) { 8 | x = 1; 9 | } 10 | " 11 | 12 | let correct = 13 | let builder = init_builder false false false in 14 | let undef_temp, undef_inst = build_load_undefined builder in 15 | let dup_temp, dup_inst = build_dup_op undef_temp builder in 16 | let compare_target_temp, build_compare_target = build_load_integer 0L builder in 17 | let compare_res_temp, build_compare = build_compare_op dup_temp compare_target_temp NotEqual builder in 18 | let zero_temp, load_zero_temp = build_load_integer 0L builder in 19 | let begin_while_inst = build_begin_while compare_res_temp zero_temp NotEqual builder in 20 | let one_temp, load_one_temp = build_load_integer 1L builder in 21 | let reassign_inst = build_reassign_op dup_temp one_temp builder in 22 | let second_compare_target_temp, build_second_compare_target_inst = build_load_integer 0L builder in 23 | let second_compare_temp, build_second_compare_inst = build_compare_op dup_temp second_compare_target_temp NotEqual builder in 24 | let second_reassign_inst = build_reassign_op compare_res_temp second_compare_temp builder in 25 | let end_while_inst = build_end_while builder in 26 | let res = [undef_inst; dup_inst; build_compare_target; build_compare; load_zero_temp; begin_while_inst; load_one_temp; reassign_inst; 27 | build_second_compare_target_inst; build_second_compare_inst; second_reassign_inst; end_while_inst ] in 28 | List.map inst_to_prog_inst res 29 | 30 | let test () = 31 | let (ast, errors) = Compiler.string_to_flow_ast input in 32 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 33 | Alcotest.(check (list Util.inst_testable)) "empty_assignment_scope" correct prog 34 | -------------------------------------------------------------------------------- /Compiler/test/exp_statement.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 0; 6 | const v1 = 5; 7 | v0 = v1 + 1; 8 | " 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_0, load_int_0 = build_load_integer 0L builder in 13 | let int_5, load_int_5 = build_load_integer 5L builder in 14 | let int_1, load_int_1 = build_load_integer 1L builder in 15 | let bin_temp, bin_inst = build_binary_op int_5 int_1 Plus builder in 16 | let reassign_inst = build_reassign_op int_0 bin_temp builder in 17 | let res = [load_int_0; load_int_5; load_int_1; bin_inst; reassign_inst] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "exp_statement" correct prog 24 | -------------------------------------------------------------------------------- /Compiler/test/for_in.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | 5 | let input = 6 | "const v0 = 12; 7 | const v1 = [v0,v0,v0,v0,v0]; 8 | for (const v2 in v1) { 9 | let v3 = v2; 10 | isNaN(v3); 11 | }" 12 | 13 | let correct = 14 | let builder = init_builder false false false in 15 | let int_12, load_int_12 = build_load_integer 12L builder in 16 | let arr_temp, create_arr_inst = build_create_array [int_12; int_12; int_12; int_12; int_12] builder in 17 | let left_temp = get_new_intermed_temp builder in 18 | let _, begin_for_in = build_begin_for_in_op left_temp arr_temp builder in 19 | let builtin_temp, load_builtin = build_load_builtin "isNaN" builder in 20 | let _, call_inst = build_call builtin_temp [left_temp] builder in 21 | let end_for_in = build_end_for_in_op builder in 22 | let res = [load_int_12; create_arr_inst; begin_for_in; load_builtin; call_inst; end_for_in] in 23 | List.map inst_to_prog_inst res 24 | 25 | let test () = 26 | let (ast, errors) = Compiler.string_to_flow_ast input in 27 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 28 | Alcotest.(check (list Util.inst_testable)) "for_in_scoping" correct prog 29 | -------------------------------------------------------------------------------- /Compiler/test/for_in_scope2.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "for (var x in x) { 6 | function x() { 7 | ; 8 | } 9 | let a = x(); 10 | } 11 | " 12 | 13 | let correct = 14 | let builder = init_builder false false false in 15 | let placeholder_temp, load_placeholder = build_load_builtin "placeholder" builder in 16 | let temp = get_new_intermed_temp builder in 17 | let _, begin_for_in = build_begin_for_in_op temp placeholder_temp builder in 18 | let func_temp = get_new_intermed_temp builder in 19 | let func_temp2, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 20 | let _, call_inst = build_call func_temp2 [] builder in 21 | let end_for_in = build_end_for_in_op builder in 22 | let res = [load_placeholder; begin_for_in; begin_func_inst; end_func_inst; call_inst; end_for_in] in 23 | List.map inst_to_prog_inst res 24 | 25 | let test () = 26 | let (ast, errors) = Compiler.string_to_flow_ast input in 27 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 28 | Alcotest.(check (list Util.inst_testable)) "for_in_scope2" correct prog 29 | -------------------------------------------------------------------------------- /Compiler/test/for_in_scoping.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let i = \"ABC\" 6 | for (i in [0]) { 7 | } 8 | let b = i + \"D\";" 9 | 10 | (* Note: Fuzzilli does not support using an existing var *) 11 | let correct = 12 | let builder = init_builder false false false in 13 | let string_temp, load_string_inst = build_load_string "ABC" builder in 14 | let int_temp, load_int_inst = build_load_integer 0L builder in 15 | let arr_temp, load_array_inst = build_create_array [int_temp] builder in 16 | let left_for_in = get_new_intermed_temp builder in 17 | let _, begin_for_in_inst = build_begin_for_in_op left_for_in arr_temp builder in 18 | let reassign_inst = build_reassign_op string_temp left_for_in builder in 19 | let end_for_in_inst = build_end_for_in_op builder in 20 | let string_temp2, load_string_inst2 = build_load_string "D" builder in 21 | let add_temp, add_inst = build_binary_op string_temp string_temp2 Plus builder in 22 | let res = [load_string_inst; load_int_inst; load_array_inst; begin_for_in_inst; reassign_inst; end_for_in_inst; load_string_inst2; add_inst] in 23 | List.map inst_to_prog_inst res 24 | 25 | let test () = 26 | let (ast, errors) = Compiler.string_to_flow_ast input in 27 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 28 | Alcotest.(check (list Util.inst_testable)) "for_in_scoping" correct prog 29 | -------------------------------------------------------------------------------- /Compiler/test/for_of.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 20; 6 | const v1 = [v0,v0,v0,v0,v0]; 7 | for (const v2 of v1){ 8 | isNaN(v2); 9 | }" 10 | 11 | let correct = 12 | let builder = init_builder false false false in 13 | let int_20, load_int_20 = build_load_integer 20L builder in 14 | let array_temp, create_array_inst = build_create_array [int_20; int_20; int_20; int_20; int_20] builder in 15 | let begin_for_of_temp, begin_for_of_inst = build_begin_for_of_op array_temp builder in 16 | let isNan_temp, load_IsNaN = build_load_builtin "isNaN" builder in 17 | let _, call_inst = build_call isNan_temp [begin_for_of_temp] builder in 18 | let end_for_of = build_end_for_of_op builder in 19 | let res = [load_int_20; create_array_inst; begin_for_of_inst; load_IsNaN; call_inst; end_for_of] in 20 | List.map inst_to_prog_inst res 21 | 22 | let test () = 23 | let (ast, errors) = Compiler.string_to_flow_ast input in 24 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 25 | Alcotest.(check (list Util.inst_testable)) "for_of" correct prog 26 | -------------------------------------------------------------------------------- /Compiler/test/func_call_with_spread.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "function test(v0, ...v101){ 6 | return v0 + v101[0]; 7 | } 8 | const v5 = [0,1]; 9 | const v17 = test(10,...v5); 10 | " 11 | 12 | let correct = 13 | let builder = init_builder false false false in 14 | let func_temp = get_new_intermed_temp builder in 15 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp ["v0"] (Some "v101") false false false builder in 16 | (* TODO: Update this along with the function interface *) 17 | let v0_temp = match lookup_var_name builder "v0" with 18 | InScope x -> x 19 | | NotFound -> raise (Invalid_argument "improper variable lookup") in 20 | let v101_temp = match lookup_var_name builder "v101" with 21 | InScope x -> x 22 | | NotFound -> raise (Invalid_argument "improper variable lookup") in 23 | let elem_temp, load_elem_inst = build_load_element v101_temp 0 builder in 24 | let bin_temp, bin_inst = build_binary_op v0_temp elem_temp Plus builder in 25 | let ret_inst = build_return_op bin_temp builder in 26 | 27 | let int_0, load_int_0 = build_load_integer 0L builder in 28 | let int_1, load_int_1 = build_load_integer 1L builder in 29 | 30 | let arr_temp, create_arr_temp = build_create_array [int_0; int_1] builder in 31 | let int_10, load_int_10 = build_load_integer 10L builder in 32 | let _, call_inst = build_call_with_spread func_temp [int_10; arr_temp] [false; true] builder in 33 | let res = [begin_func_inst; load_elem_inst; bin_inst; ret_inst; end_func_inst; load_int_0; load_int_1; create_arr_temp; load_int_10; call_inst] in 34 | List.map inst_to_prog_inst res 35 | 36 | let test () = 37 | let (ast, errors) = Compiler.string_to_flow_ast input in 38 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 39 | Alcotest.(check (list Util.inst_testable)) "func_call_with_spread" correct prog -------------------------------------------------------------------------------- /Compiler/test/func_dec_order.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "function b() {a();} 6 | function a() { 7 | return 7; 8 | } 9 | " 10 | 11 | let correct = 12 | let builder = init_builder false false false in 13 | let undef_temp, load_undef_inst = build_load_undefined builder in 14 | let func_temp = get_new_intermed_temp builder in 15 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 16 | let _, call_inst = build_call undef_temp [] builder in 17 | let func_temp2 = get_new_intermed_temp builder in 18 | let _, begin_func_inst_2, end_func_inst_2 = build_func_ops func_temp2 [] None false false false builder in 19 | let int_temp, load_int_inst = build_load_integer 7L builder in 20 | let ret_inst = build_return_op int_temp builder in 21 | let reassign_inst = build_reassign_op undef_temp func_temp2 builder in 22 | let res = [load_undef_inst; begin_func_inst; call_inst; end_func_inst; begin_func_inst_2; load_int_inst; ret_inst; end_func_inst_2; reassign_inst ] in 23 | List.map inst_to_prog_inst res 24 | 25 | let test () = 26 | let (ast, errors) = Compiler.string_to_flow_ast input in 27 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 28 | Alcotest.(check (list Util.inst_testable)) "func_dec_order" correct prog -------------------------------------------------------------------------------- /Compiler/test/func_exp_test.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = function () { const v1 = 12; return v1;} 6 | const v2 = v0(); 7 | " 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | (* TODO: This needs to be updated along with the function builder interface *) 12 | let func_temp = get_new_intermed_temp builder in 13 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 14 | let int_12, load_int_12 = build_load_integer 12L builder in 15 | let return_inst = build_return_op int_12 builder in 16 | let _, call_inst = build_call func_temp [] builder in 17 | let res = [begin_func_inst; load_int_12; return_inst; end_func_inst; call_inst] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "func_exp_test" correct prog -------------------------------------------------------------------------------- /Compiler/test/func_param_scoping.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | 5 | let input = 6 | "function test(a) { 7 | a[0] = 1.5; 8 | } 9 | a = new Array();" 10 | 11 | let correct = 12 | let builder = init_builder false false false in 13 | let func_temp = get_new_intermed_temp builder in 14 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp ["a"] None false false false builder in 15 | (* TODO: This needs to be updated along with the function builder interface *) 16 | let a_temp = match lookup_var_name builder "a" with 17 | InScope x -> x 18 | | NotFound -> raise (Invalid_argument "improper variable lookup") in 19 | 20 | let int_0, load_int_0 = build_load_integer 0L builder in 21 | let float_15, load_float = build_load_float 1.5 builder in 22 | let store_inst = build_store_computed_prop a_temp int_0 float_15 builder in 23 | let array_builtin, load_builtin_inst = build_load_builtin "Array" builder in 24 | let construct_temp, construct_inst = build_new_object array_builtin [] builder in 25 | let _, dup_op = build_dup_op construct_temp builder in 26 | let res = [begin_func_inst; load_int_0; load_float; store_inst; end_func_inst; load_builtin_inst; construct_inst; dup_op] in 27 | List.map inst_to_prog_inst res 28 | 29 | let test () = 30 | let (ast, errors) = Compiler.string_to_flow_ast input in 31 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 32 | Alcotest.(check (list Util.inst_testable)) "func_param_scoping" correct prog -------------------------------------------------------------------------------- /Compiler/test/if_else.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "if(1){ 6 | const v0 = 3; 7 | } else if(2){ 8 | const v1 = 4; 9 | } else { 10 | const v2 = 5; 11 | } 12 | " 13 | let correct = 14 | let builder = init_builder false false false in 15 | let int_1, load_int_1 = build_load_integer 1L builder in 16 | let begin_if_inst_1 = build_begin_if int_1 builder in 17 | let int_3, load_int_3 = build_load_integer 3L builder in 18 | let else_inst = build_begin_else builder in 19 | let int_2, load_int_2 = build_load_integer 2L builder in 20 | let begin_if_inst_2 = build_begin_if int_2 builder in 21 | let int_4, load_int_4 = build_load_integer 4L builder in 22 | let int_5, load_int_5 = build_load_integer 5L builder in 23 | let end_if_inst = build_end_if builder in 24 | let res = [load_int_1; begin_if_inst_1; load_int_3; else_inst; load_int_2; begin_if_inst_2; load_int_4; else_inst; 25 | load_int_5; end_if_inst; end_if_inst] in 26 | List.map inst_to_prog_inst res 27 | 28 | let test () = 29 | let (ast, errors) = Compiler.string_to_flow_ast input in 30 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 31 | Alcotest.(check (list Util.inst_testable)) "if_else" correct prog 32 | -------------------------------------------------------------------------------- /Compiler/test/in_test.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 10; 6 | const v3 = [15,20]; 7 | const v4 = v0 in v3; 8 | " 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_10, load_int_10 = build_load_integer 10L builder in 13 | let int_15, load_int_15 = build_load_integer 15L builder in 14 | let int_20, load_int_20 = build_load_integer 20L builder in 15 | let array_temp, create_array_inst = build_create_array [int_15; int_20] builder in 16 | let _, in_inst = build_in_op int_10 array_temp builder in 17 | let res = [load_int_10; load_int_15; load_int_20; create_array_inst; in_inst] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "in_test" correct prog 24 | -------------------------------------------------------------------------------- /Compiler/test/instance_of.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 1; 6 | const v1 = 2; 7 | const v2 = v1 instanceof v0; 8 | " 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_1, load_int_1 = build_load_integer 1L builder in 13 | let int_2, load_int_2 = build_load_integer 2L builder in 14 | let _, instance_inst = build_instanceof_op int_2 int_1 builder in 15 | let res = [load_int_1; load_int_2; instance_inst] in 16 | List.map inst_to_prog_inst res 17 | 18 | let test () = 19 | let (ast, errors) = Compiler.string_to_flow_ast input in 20 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 21 | Alcotest.(check (list Util.inst_testable)) "instance_of" correct prog 22 | -------------------------------------------------------------------------------- /Compiler/test/load_array_index.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v3 = [0,1,2]; 6 | const v5 = v3[0]; 7 | const v7 = v3[v5];" 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | let int_0, load_int_0 = build_load_integer 0L builder in 12 | let int_1, load_int_1 = build_load_integer 1L builder in 13 | let int_2, load_int_2 = build_load_integer 2L builder in 14 | let arr_temp, create_arr = build_create_array [int_0; int_1; int_2] builder in 15 | let elem_temp, load_elem_inst = build_load_element arr_temp 0 builder in 16 | let _, load_comp_elem_inst = build_load_computed_prop arr_temp elem_temp builder in 17 | let res = [load_int_0; load_int_1; load_int_2; create_arr; load_elem_inst; load_comp_elem_inst] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "load_array_index" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_bigint.ml: -------------------------------------------------------------------------------- 1 | open Compiler.ProgramBuilder 2 | open Program_types 3 | 4 | let input = 5 | "const v0 = 9007199254740991n;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, load_big_int_inst = build_load_bigInt 9007199254740991.0 builder in 10 | let res = [load_big_int_inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "load_bigint" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_bool.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = true; 6 | const v1 = false;" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let _, load_true = build_load_bool true builder in 11 | let _, load_false = build_load_bool false builder in 12 | let res = [load_true; load_false] in 13 | List.map inst_to_prog_inst res 14 | 15 | let test () = 16 | let (ast, errors) = Compiler.string_to_flow_ast input in 17 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 18 | Alcotest.(check (list Util.inst_testable)) "load_bool" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_float.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 20.15;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, inst = build_load_float 20.15 builder in 10 | let res = [inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "load_float" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_infinity.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = Infinity;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, inst = build_load_float infinity builder in 10 | let res = [inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "load_infinity" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_null.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = null;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, load_null = build_load_null builder in 10 | let res = [load_null] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "load_null" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_property.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = {}; 6 | const v1 = 13.37; 7 | const v2 = v0.__proto__;" 8 | let correct = 9 | let builder = init_builder false false false in 10 | let obj_temp, create_obj = build_create_object [] [] builder in 11 | let float_temp, load_float_inst = build_load_float 13.37 builder in 12 | let _, load_prop_inst = build_load_prop obj_temp "__proto__" builder in 13 | let res = [create_obj; load_float_inst; load_prop_inst] in 14 | List.map inst_to_prog_inst res 15 | 16 | let test () = 17 | let (ast, errors) = Compiler.string_to_flow_ast input in 18 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 19 | Alcotest.(check (list Util.inst_testable)) "load_property" correct prog -------------------------------------------------------------------------------- /Compiler/test/load_regex.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | (* Note: this produces compiler warnings, but seems to work fine *) 5 | let input = 6 | "const v0 = /\w+\s/i; 7 | const v1 = /\w+\s/g; 8 | const v2 = /\w+\s/m; 9 | const v3 = /\w+\s/s; 10 | const v4 = /\w+\s/u; 11 | const v5 = /\w+\s/y;" 12 | 13 | let correct = 14 | let builder = init_builder false false false in 15 | let _, regexp1 = build_load_regex "\\w+\\s" "i" builder in 16 | let _, regexp2 = build_load_regex "\\w+\\s" "g" builder in 17 | let _, regexp3 = build_load_regex "\\w+\\s" "m" builder in 18 | let _, regexp4 = build_load_regex "\\w+\\s" "s" builder in 19 | let _, regexp5 = build_load_regex "\\w+\\s" "u" builder in 20 | let _, regexp6 = build_load_regex "\\w+\\s" "y" builder in 21 | let res = [regexp1; regexp2; regexp3; regexp4; regexp5; regexp6] in 22 | List.map inst_to_prog_inst res 23 | 24 | let test () = 25 | let (ast, errors) = Compiler.string_to_flow_ast input in 26 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 27 | Alcotest.(check (list Util.inst_testable)) "load_regex" correct prog -------------------------------------------------------------------------------- /Compiler/test/logical_ops.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 1 || 2; 6 | const v1 = 3 && 4;" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let int_1, load_int_1 = build_load_integer 1L builder in 11 | let int_2, load_int_2 = build_load_integer 2L builder in 12 | let _, or_inst = build_binary_op int_1 int_2 LogicalOr builder in 13 | let int_3, load_int_3 = build_load_integer 3L builder in 14 | let int_4, load_int_4 = build_load_integer 4L builder in 15 | let _, and_inst = build_binary_op int_3 int_4 LogicalAnd builder in 16 | let res = [load_int_1; load_int_2; or_inst; load_int_3; load_int_4; and_inst] in 17 | List.map inst_to_prog_inst res 18 | 19 | let test () = 20 | let (ast, errors) = Compiler.string_to_flow_ast input in 21 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 22 | Alcotest.(check (list Util.inst_testable)) "Logical_ops" correct prog -------------------------------------------------------------------------------- /Compiler/test/lone_if.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 12; 6 | if(v0){ 7 | let v1 = 12; 8 | }" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let temp_12, load_int_12 = build_load_integer 12L builder in 13 | let begin_if_inst = build_begin_if temp_12 builder in 14 | let _, load_int_12_2 = build_load_integer 12L builder in 15 | let begin_else = build_begin_else builder in 16 | let end_if = build_end_if builder in 17 | let res = [load_int_12; begin_if_inst; load_int_12_2; begin_else; end_if] in 18 | List.map inst_to_prog_inst res 19 | 20 | let test () = 21 | let (ast, errors) = Compiler.string_to_flow_ast input in 22 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 23 | Alcotest.(check (list Util.inst_testable)) "lone_if" correct prog -------------------------------------------------------------------------------- /Compiler/test/new.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v1 = new Uint8Array(); 6 | const v5 = new Float32Array(12);" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let uint8Array, load_uint8_builtin = build_load_builtin "Uint8Array" builder in 11 | let _, construct_uin8_inst = build_new_object uint8Array [] builder in 12 | let float32Array, load_float32_builtin = build_load_builtin "Float32Array" builder in 13 | let int_12_temp, load_12 = build_load_integer 12L builder in 14 | let _, construct_float32_inst = build_new_object float32Array [int_12_temp] builder in 15 | let res = [load_uint8_builtin; construct_uin8_inst; load_float32_builtin; load_12; construct_float32_inst] in 16 | List.map inst_to_prog_inst res 17 | 18 | let test () = 19 | let (ast, errors) = Compiler.string_to_flow_ast input in 20 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 21 | Alcotest.(check (list Util.inst_testable)) "new_test" correct prog -------------------------------------------------------------------------------- /Compiler/test/object_creation.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 1; 6 | const v1 = 2 7 | const v7 = {toString:1+2,e:v0+v1};" 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | let int_1, load_int_1 = build_load_integer 1L builder in 12 | let int_2, load_int_2 = build_load_integer 2L builder in 13 | let int_1_2, load_int_1_2 = build_load_integer 1L builder in 14 | let int_2_2, load_int_2_2 = build_load_integer 2L builder in 15 | let second_temp, second_add = build_binary_op int_1_2 int_2_2 Plus builder in 16 | let first_temp, first_add = build_binary_op int_1 int_2 Plus builder in 17 | let _, create_obj_inst = build_create_object ["toString"; "e"] [second_temp; first_temp] builder in 18 | let res = [load_int_1; load_int_2; load_int_1_2; load_int_2_2; second_add; first_add; create_obj_inst] in 19 | List.map inst_to_prog_inst res 20 | 21 | let test () = 22 | let (ast, errors) = Compiler.string_to_flow_ast input in 23 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 24 | Alcotest.(check (list Util.inst_testable)) "object_creation" correct prog -------------------------------------------------------------------------------- /Compiler/test/prog_10.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 4294967296; 6 | const v1 = isNaN; 7 | const v2 = v0 === v1; 8 | const v3 = v0 == v1; 9 | const v4 = 13.37; 10 | const v5 = [v4,v4]; 11 | const v6 = 1337; 12 | const v7 = {toString:v5,e:v6};" 13 | 14 | let correct = 15 | let builder = init_builder false false false in 16 | let large_int, load_large_int = build_load_integer 4294967296L builder in 17 | let isNaN, load_isNaN = build_load_builtin "isNaN" builder in 18 | let _, compare_inst = build_compare_op large_int isNaN StrictEqual builder in 19 | let _, compare_inst_2 = build_compare_op large_int isNaN Equal builder in 20 | let float, load_float = build_load_float 13.37 builder in 21 | let array, create_array = build_create_array [float; float] builder in 22 | let int_1337, load_int_1337 = build_load_integer 1337L builder in 23 | let _, create_object = build_create_object ["toString"; "e"] [array; int_1337] builder in 24 | let res = [load_large_int; load_isNaN; compare_inst; compare_inst_2; load_float; create_array; load_int_1337; create_object] in 25 | List.map inst_to_prog_inst res 26 | 27 | let test () = 28 | let (ast, errors) = Compiler.string_to_flow_ast input in 29 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 30 | Alcotest.(check (list Util.inst_testable)) "prog_10" correct prog -------------------------------------------------------------------------------- /Compiler/test/prog_1007.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 2147483647; 6 | const v1 = \"function\"; 7 | try { 8 | const v2 = v1.repeat(v0); 9 | } catch(v3) { 10 | }" 11 | 12 | let correct = 13 | let builder = init_builder false false false in 14 | let large_int, load_large_int = build_load_integer 2147483647L builder in 15 | let func_string, load_func_string = build_load_string "function" builder in 16 | let begin_try = build_begin_try_op builder in 17 | let _, call_method = build_call_method func_string [large_int] "repeat" builder in 18 | let begin_catch = build_begin_catch_op "foobarbaz" builder in 19 | let end_catch = build_end_try_catch_op builder in 20 | let res = [load_large_int; load_func_string; begin_try; call_method; begin_catch; end_catch] in 21 | List.map inst_to_prog_inst res 22 | 23 | let test () = 24 | let (ast, errors) = Compiler.string_to_flow_ast input in 25 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 26 | Alcotest.(check (list Util.inst_testable)) "prog_1007" correct prog -------------------------------------------------------------------------------- /Compiler/test/prop_name_assignment.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = \"function\"; 6 | const v1 = 13.37; 7 | const v2 = v0.__proto__; 8 | v2.toString = v1;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let func_string, load_func_string = build_load_string "function" builder in 13 | let float, load_float = build_load_float 13.37 builder in 14 | let prop_temp, load_prop = build_load_prop func_string "__proto__" builder in 15 | let store_prop = build_store_prop prop_temp float "toString" builder in 16 | let res = [load_func_string; load_float; load_prop; store_prop] in 17 | List.map inst_to_prog_inst res 18 | 19 | let test () = 20 | let (ast, errors) = Compiler.string_to_flow_ast input in 21 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 22 | Alcotest.(check (list Util.inst_testable)) "prop_name_assignment" correct prog -------------------------------------------------------------------------------- /Compiler/test/single_constant.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 0;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, inst = build_load_integer 0L builder in 10 | let res = [inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "single_constant" correct prog -------------------------------------------------------------------------------- /Compiler/test/single_let.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 0; 6 | v0 = 12;" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let int_0_temp, load_0_inst = build_load_integer 0L builder in 11 | let int_12_temp, load_12_inst = build_load_integer 12L builder in 12 | let reassign_inst = build_reassign_op int_0_temp int_12_temp builder in 13 | let res = [load_0_inst; load_12_inst; reassign_inst] in 14 | List.map inst_to_prog_inst res 15 | 16 | let test () = 17 | let (ast, errors) = Compiler.string_to_flow_ast input in 18 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 19 | Alcotest.(check (list Util.inst_testable)) "single_let" correct prog -------------------------------------------------------------------------------- /Compiler/test/single_string_literal.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = \"foobarbaz\";" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, inst = build_load_string "foobarbaz" builder in 10 | let res = [inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "single_string_literal" correct prog -------------------------------------------------------------------------------- /Compiler/test/spread_object.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 1; 6 | const v1 = 2 7 | const v7 = {toString:1+2,e:v0+v1}; 8 | const v11 = {foobar:3+4,...v7};" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_1, load_int_1 = build_load_integer 1L builder in 13 | let int_2, load_int_2 = build_load_integer 2L builder in 14 | let int_1_2, load_int_1_2 = build_load_integer 1L builder in 15 | let int_2_2, load_int_2_2 = build_load_integer 2L builder in 16 | let second_temp, second_add = build_binary_op int_1_2 int_2_2 Plus builder in 17 | let first_temp, first_add = build_binary_op int_1 int_2 Plus builder in 18 | let obj, create_obj_inst = build_create_object ["toString"; "e"] [second_temp; first_temp] builder in 19 | 20 | let int_3, load_int_3 = build_load_integer 3L builder in 21 | let int_4, load_int_4 = build_load_integer 4L builder in 22 | let third_temp, third_add = build_binary_op int_3 int_4 Plus builder in 23 | let _, spread_inst = build_create_object_with_spread ["foobar"] [third_temp; obj] builder in 24 | let res = [load_int_1; load_int_2; load_int_1_2; load_int_2_2; second_add; first_add; create_obj_inst; load_int_3; load_int_4; third_add; spread_inst] in 25 | List.map inst_to_prog_inst res 26 | 27 | let test () = 28 | let (ast, errors) = Compiler.string_to_flow_ast input in 29 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 30 | Alcotest.(check (list Util.inst_testable)) "spread_object" correct prog -------------------------------------------------------------------------------- /Compiler/test/store_property_sugared.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = {}; 6 | const v1 = 13.37; 7 | v0.a = 10; 8 | v0.a += v1;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let obj, create_obj = build_create_object [] [] builder in 13 | let float, load_float = build_load_float 13.37 builder in 14 | let int, load_int = build_load_integer 10L builder in 15 | let store_prop = build_store_prop obj int "a" builder in 16 | let temp_prop, load_prop = build_load_prop obj "a" builder in 17 | let add_temp, add_inst = build_binary_op temp_prop float Plus builder in 18 | let store_prop2 = build_store_prop obj add_temp "a" builder in 19 | let res = [create_obj; load_float; load_int; store_prop; load_prop; add_inst; store_prop2] in 20 | List.map inst_to_prog_inst res 21 | 22 | let test () = 23 | let (ast, errors) = Compiler.string_to_flow_ast input in 24 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 25 | Alcotest.(check (list Util.inst_testable)) "store_property_sugared" correct prog -------------------------------------------------------------------------------- /Compiler/test/sugared_assignment.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 1; 6 | v0 += 10; 7 | let v1 = 2; 8 | v1 -= 20;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let int_1, load_int_1 = build_load_integer 1L builder in 13 | let int_10, load_int_10 = build_load_integer 10L builder in 14 | let add_temp, add_inst = build_binary_op int_1 int_10 Plus builder in 15 | let reassign_op = build_reassign_op int_1 add_temp builder in 16 | let int_2, load_int_2 = build_load_integer 2L builder in 17 | let int_20, load_int_20 = build_load_integer 20L builder in 18 | let sub_temp, sub_inst = build_binary_op int_2 int_20 Minus builder in 19 | let reassign_op2 = build_reassign_op int_2 sub_temp builder in 20 | let res = [load_int_1; load_int_10; add_inst; reassign_op; load_int_2; load_int_20; sub_inst; reassign_op2] in 21 | List.map inst_to_prog_inst res 22 | 23 | let test () = 24 | let (ast, errors) = Compiler.string_to_flow_ast input in 25 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 26 | Alcotest.(check (list Util.inst_testable)) "sugared_assignment" correct prog -------------------------------------------------------------------------------- /Compiler/test/ternary.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let a = 1 < 0 ? 2 : 3;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let int_0, load_int_0 = build_load_integer 0L builder in 10 | let int_1, load_int_1 = build_load_integer 1L builder in 11 | let int_0_2, load_int_0_2 = build_load_integer 0L builder in 12 | let compare_temp, compare_inst = build_compare_op int_1 int_0_2 LessThan builder in 13 | let begin_if_inst = build_begin_if compare_temp builder in 14 | let int_2, load_int_2 = build_load_integer 2L builder in 15 | let reassign_inst = build_reassign_op int_0 int_2 builder in 16 | let begin_else = build_begin_else builder in 17 | let int_3, load_int_3 = build_load_integer 3L builder in 18 | let reassign_inst2 = build_reassign_op int_0 int_3 builder in 19 | let end_if = build_end_if builder in 20 | let res = [load_int_0; load_int_1; load_int_0_2; compare_inst; begin_if_inst; load_int_2; reassign_inst; begin_else; load_int_3; reassign_inst2; end_if] in 21 | List.map inst_to_prog_inst res 22 | 23 | let test () = 24 | let (ast, errors) = Compiler.string_to_flow_ast input in 25 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 26 | Alcotest.(check (list Util.inst_testable)) "ternary_test" correct prog -------------------------------------------------------------------------------- /Compiler/test/this.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = this" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, inst = build_load_builtin "this" builder in 10 | let res = [inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "this" correct prog -------------------------------------------------------------------------------- /Compiler/test/throw.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 0; 6 | throw v0;" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let temp_0, load_0 = build_load_integer 0L builder in 11 | let inst = build_throw_op temp_0 builder in 12 | let res = [load_0; inst] in 13 | List.map inst_to_prog_inst res 14 | 15 | let test () = 16 | let (ast, errors) = Compiler.string_to_flow_ast input in 17 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 18 | Alcotest.(check (list Util.inst_testable)) "throw" correct prog -------------------------------------------------------------------------------- /Compiler/test/typeof.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 12; 6 | const v2 = typeof v0;" 7 | 8 | let correct = 9 | let builder = init_builder false false false in 10 | let int, load_int = build_load_integer 12L builder in 11 | let _, typeof_inst = build_typeof_op int builder in 12 | let res = [load_int; typeof_inst] in 13 | List.map inst_to_prog_inst res 14 | 15 | let test () = 16 | let (ast, errors) = Compiler.string_to_flow_ast input in 17 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 18 | Alcotest.(check (list Util.inst_testable)) "typeof" correct prog -------------------------------------------------------------------------------- /Compiler/test/unary_minus.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v2 = -256;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let pos_temp, load_int = build_load_integer 256L builder in 10 | let _, unary_inst = build_unary_op pos_temp Minus builder in 11 | let res = [load_int; unary_inst] in 12 | List.map inst_to_prog_inst res 13 | 14 | let test () = 15 | let (ast, errors) = Compiler.string_to_flow_ast input in 16 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 17 | Alcotest.(check (list Util.inst_testable)) "unary_minus" correct prog -------------------------------------------------------------------------------- /Compiler/test/unary_ops.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = !true; 6 | const v1 = !false; 7 | const v2 = !v0; 8 | const v3 = ~5;" 9 | 10 | let correct = 11 | let builder = init_builder false false false in 12 | let true_temp, load_true = build_load_bool true builder in 13 | let not_temp, not_inst_1 = build_unary_op true_temp Not builder in 14 | let false_temp, load_false = build_load_bool false builder in 15 | let _, not_inst_2 = build_unary_op false_temp Not builder in 16 | let _, not_inst_3 = build_unary_op not_temp Not builder in 17 | let int_5, load_int_5 = build_load_integer 5L builder in 18 | let _, bit_not_inst = build_unary_op int_5 BitNot builder in 19 | let res = [load_true; not_inst_1; load_false; not_inst_2; not_inst_3; load_int_5; bit_not_inst] in 20 | List.map inst_to_prog_inst res 21 | 22 | let test () = 23 | let (ast, errors) = Compiler.string_to_flow_ast input in 24 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 25 | Alcotest.(check (list Util.inst_testable)) "unary_ops" correct prog -------------------------------------------------------------------------------- /Compiler/test/undefined.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let a = undefined;" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let _, inst = build_load_undefined builder in 10 | let res = [inst] in 11 | List.map inst_to_prog_inst res 12 | 13 | let test () = 14 | let (ast, errors) = Compiler.string_to_flow_ast input in 15 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 16 | Alcotest.(check (list Util.inst_testable)) "undefined" correct prog -------------------------------------------------------------------------------- /Compiler/test/update.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = 5; 6 | v0++; 7 | --v0;" 8 | 9 | let correct = 10 | let builder = init_builder false false false in 11 | let int_5, load_int_5 = build_load_integer 5L builder in 12 | let _, post_inc = build_unary_op int_5 PostInc builder in 13 | let _, pre_inc = build_unary_op int_5 PreDec builder in 14 | let res = [load_int_5; post_inc; pre_inc] in 15 | List.map inst_to_prog_inst res 16 | 17 | let test () = 18 | let (ast, errors) = Compiler.string_to_flow_ast input in 19 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 20 | Alcotest.(check (list Util.inst_testable)) "update" correct prog -------------------------------------------------------------------------------- /Compiler/test/util.ml: -------------------------------------------------------------------------------- 1 | let inst_testable = Alcotest.testable Compiler.pp_instruction (=) 2 | 3 | let type_ext = Typesystem_types.{ 4 | properties = []; 5 | methods = []; 6 | group = ""; 7 | signature = None; 8 | } 9 | 10 | 11 | let default_input_type = Typesystem_types.{ 12 | definite_type = 4095l; 13 | possible_type = 4095l; 14 | ext = Extension type_ext 15 | } 16 | 17 | let spread_input_type = Typesystem_types.{ 18 | definite_type = 2147483648l; 19 | possible_type = 2147483648l; 20 | ext = Extension type_ext 21 | } 22 | 23 | let default_output_type = Typesystem_types.{ 24 | definite_type = Int32.shift_left 1l 8; 25 | possible_type = Int32.shift_left 1l 8; 26 | ext = Extension type_ext 27 | } 28 | -------------------------------------------------------------------------------- /Compiler/test/v8_natives.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v0 = function () { const v1 = 12; return v1;} 6 | %PrepareFunctionForOptimization(v0);" 7 | 8 | let correct = 9 | let builder = init_builder false true true in 10 | let func_temp = get_new_intermed_temp builder in 11 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 12 | let int_12, load_12 = build_load_integer 12L builder in 13 | let ret_inst = build_return_op int_12 builder in 14 | let builtin_temp, load_builtin = build_load_builtin "PrepareFunctionForOptimization" builder in 15 | let _, call_inst = build_call builtin_temp [func_temp] builder in 16 | let res = [begin_func_inst; load_12; ret_inst; end_func_inst; load_builtin; call_inst] in 17 | List.map inst_to_prog_inst res 18 | 19 | let test () = 20 | let (ast, errors) = Compiler.string_to_flow_ast input in 21 | let prog = Compiler.flow_ast_to_inst_list ast false true true in 22 | Alcotest.(check (list Util.inst_testable)) "v8_natives" correct prog -------------------------------------------------------------------------------- /Compiler/test/var_hoisting_1.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "function foo() { 6 | if(1){ 7 | var asdf = 12; 8 | } 9 | isNaN(asdf); 10 | } 11 | foo(); 12 | " 13 | 14 | let correct = 15 | let builder = init_builder false false false in 16 | let func_temp = get_new_intermed_temp builder in 17 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 18 | let undef_temp, load_undef_inst = build_load_undefined builder in 19 | let integer_temp, load_integer_inst = build_load_integer 1L builder in 20 | let begin_if_inst = build_begin_if integer_temp builder in 21 | let load_hoisted_temp, load_hoisted_inst = build_load_integer 12L builder in 22 | let reassign_inst = build_reassign_op undef_temp load_hoisted_temp builder in 23 | let begin_else_inst = build_begin_else builder in 24 | let end_if_inst = build_end_if builder in 25 | let print_temp, load_print_inst = build_load_builtin "isNaN" builder in 26 | let _, call_print_inst = build_call print_temp [undef_temp] builder in 27 | let _, call_foo_inst = build_call func_temp [] builder in 28 | let res = [begin_func_inst; load_undef_inst; load_integer_inst; begin_if_inst; load_hoisted_inst; reassign_inst; begin_else_inst; end_if_inst; load_print_inst; call_print_inst; end_func_inst; call_foo_inst] in 29 | List.map inst_to_prog_inst res 30 | 31 | let test () = 32 | let (ast, errors) = Compiler.string_to_flow_ast input in 33 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 34 | Alcotest.(check (list Util.inst_testable)) "var_hoisting_1" correct prog 35 | -------------------------------------------------------------------------------- /Compiler/test/var_hoisting_2.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "function foo() { 6 | for(var i = 1; i < 2; i++){ 7 | var asdf = i; 8 | } 9 | isNaN(asdf); 10 | } 11 | foo(); 12 | " 13 | 14 | let correct = 15 | let builder = init_builder false false false in 16 | let func_temp = get_new_intermed_temp builder in 17 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 18 | let undef_temp, load_undef_inst = build_load_undefined builder in 19 | let integer_temp, load_integer_inst = build_load_integer 1L builder in 20 | let compare_temp_target, load_compare_temp_inst = build_load_integer 2L builder in 21 | let compare_temp, compare_inst = build_compare_op integer_temp compare_temp_target LessThan builder in 22 | let i_temp, load_i_inital_inst = build_load_integer 0L builder in 23 | let begin_while_inst = build_begin_while compare_temp i_temp NotEqual builder in 24 | let reassign_hoisted_inst = build_reassign_op undef_temp integer_temp builder in 25 | let _, inc_loop_var_inst = build_unary_op integer_temp PostInc builder in 26 | let loop_compare_temp, load_loop_temp_inst = build_load_integer 2L builder in 27 | let recompare_temp, recompare_inst = build_compare_op integer_temp loop_compare_temp LessThan builder in 28 | let reassign_loop_compare = build_reassign_op compare_temp recompare_temp builder in 29 | let end_while__inst = build_end_while builder in 30 | let print_temp, load_print_inst = build_load_builtin "isNaN" builder in 31 | let _, call_print_inst = build_call print_temp [undef_temp] builder in 32 | let _, call_foo_inst = build_call func_temp [] builder in 33 | let res = [begin_func_inst; load_undef_inst; load_integer_inst; load_compare_temp_inst; compare_inst; load_i_inital_inst; 34 | begin_while_inst; reassign_hoisted_inst; inc_loop_var_inst; load_loop_temp_inst; recompare_inst; reassign_loop_compare; end_while__inst; 35 | load_print_inst; call_print_inst; end_func_inst; call_foo_inst] in 36 | List.map inst_to_prog_inst res 37 | 38 | let test () = 39 | let (ast, errors) = Compiler.string_to_flow_ast input in 40 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 41 | Alcotest.(check (list Util.inst_testable)) "var_hoisting_2" correct prog 42 | -------------------------------------------------------------------------------- /Compiler/test/var_hoisting_3.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "function foo() { 6 | let t = 1; 7 | if(t){ 8 | if(t){ 9 | var asdf = 12; 10 | } 11 | } 12 | isNaN(asdf); 13 | } 14 | foo(); 15 | " 16 | 17 | let correct = 18 | let builder = init_builder false false false in 19 | let func_temp = get_new_intermed_temp builder in 20 | let _, begin_func_inst, end_func_inst = build_func_ops func_temp [] None false false false builder in 21 | let undef_temp, load_undef_inst = build_load_undefined builder in 22 | let integer_temp, load_integer_inst = build_load_integer 1L builder in 23 | 24 | let begin_if_inst = build_begin_if integer_temp builder in 25 | let load_hoisted_temp, load_hoisted_inst = build_load_integer 12L builder in 26 | let reassign_inst = build_reassign_op undef_temp load_hoisted_temp builder in 27 | let begin_else_inst = build_begin_else builder in 28 | let end_if_inst = build_end_if builder in 29 | 30 | let print_temp, load_print_inst = build_load_builtin "isNaN" builder in 31 | let _, call_print_inst = build_call print_temp [undef_temp] builder in 32 | let _, call_foo_inst = build_call func_temp [] builder in 33 | let res = [begin_func_inst; load_undef_inst; load_integer_inst; begin_if_inst; begin_if_inst; load_hoisted_inst; reassign_inst; begin_else_inst; end_if_inst; begin_else_inst; end_if_inst; load_print_inst; call_print_inst; end_func_inst; call_foo_inst] in 34 | List.map inst_to_prog_inst res 35 | 36 | let test () = 37 | let (ast, errors) = Compiler.string_to_flow_ast input in 38 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 39 | Alcotest.(check (list Util.inst_testable)) "var_hoisting_3" correct prog 40 | -------------------------------------------------------------------------------- /Compiler/test/var_hoisting_shadow.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "for(x in [0]){} 6 | if(1){ 7 | var x = 12; 8 | } 9 | isNaN(x); 10 | " 11 | 12 | let correct = 13 | let builder = init_builder false false false in 14 | let undef_temp, load_undef_inst = build_load_undefined builder in 15 | let integer_temp, load_integer_inst = build_load_integer 0L builder in 16 | let array_temp, array_build_inst = build_create_array [integer_temp] builder in 17 | let for_in_temp = get_new_intermed_temp builder in 18 | let _, begin_for_in_inst = build_begin_for_in_op for_in_temp array_temp builder in 19 | let reassign_inst = build_reassign_op undef_temp for_in_temp builder in 20 | let end_for_in_inst = build_end_for_in_op builder in 21 | let if_condition_temp, load_if_condition_inst = build_load_integer 1L builder in 22 | let begin_if_inst = build_begin_if if_condition_temp builder in 23 | let load_hoisted_temp, load_hoisted_inst = build_load_integer 12L builder in 24 | let reassign_inst2 = build_reassign_op undef_temp load_hoisted_temp builder in 25 | let begin_else_inst = build_begin_else builder in 26 | let end_if_inst = build_end_if builder in 27 | let print_temp, load_print_inst = build_load_builtin "isNaN" builder in 28 | let _, call_print_inst = build_call print_temp [undef_temp] builder in 29 | let res = [load_undef_inst; load_integer_inst; array_build_inst; begin_for_in_inst; reassign_inst; end_for_in_inst; load_if_condition_inst; begin_if_inst; load_hoisted_inst; reassign_inst2; begin_else_inst; end_if_inst; load_print_inst; call_print_inst] in 30 | List.map inst_to_prog_inst res 31 | 32 | let test () = 33 | let (ast, errors) = Compiler.string_to_flow_ast input in 34 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 35 | Alcotest.(check (list Util.inst_testable)) "var_hoisting_shadow" correct prog 36 | -------------------------------------------------------------------------------- /Compiler/test/void.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let a = void (1 + 2);" 6 | 7 | let correct = 8 | let builder = init_builder false false false in 9 | let int_1, load_int_1 = build_load_integer 1L builder in 10 | let int_2, load_int_2 = build_load_integer 2L builder in 11 | let _, binary_op = build_binary_op int_1 int_2 Plus builder in 12 | let _, load_undef = build_load_undefined builder in 13 | let res = [load_int_1; load_int_2; binary_op; load_undef] in 14 | List.map inst_to_prog_inst res 15 | 16 | let test () = 17 | let (ast, errors) = Compiler.string_to_flow_ast input in 18 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 19 | Alcotest.(check (list Util.inst_testable)) "void" correct prog -------------------------------------------------------------------------------- /Compiler/test/with.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "let v0 = 10; 6 | let v4 = 13; 7 | const v2 = [12]; 8 | with (v2) { 9 | const v3 = 0.0; 10 | const v9 = v0 - v4; 11 | }" 12 | 13 | 14 | (* TODO: This likely is incorrect *) 15 | let correct = 16 | let builder = init_builder false false false in 17 | let int_10, load_int_10 = build_load_integer 10L builder in 18 | let int_13, load_int_13 = build_load_integer 13L builder in 19 | let int_12, load_int_12 = build_load_integer 12L builder in 20 | let arr_temp, create_array_inst = build_create_array [int_12] builder in 21 | let begin_with = build_begin_with_op arr_temp builder in 22 | let float, load_float = build_load_float 0.0 builder in 23 | let _, sub_inst = build_binary_op int_10 int_13 Minus builder in 24 | let end_with = build_end_with_op builder in 25 | let res = [load_int_10; load_int_13; load_int_12; create_array_inst; begin_with; load_float; sub_inst; end_with] in 26 | List.map inst_to_prog_inst res 27 | 28 | let test () = 29 | let (ast, errors) = Compiler.string_to_flow_ast input in 30 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 31 | Alcotest.(check (list Util.inst_testable)) "with" correct prog -------------------------------------------------------------------------------- /Compiler/test/with_load_scope.ml: -------------------------------------------------------------------------------- 1 | open Program_types 2 | open Compiler.ProgramBuilder 3 | 4 | let input = 5 | "const v9 = {__proto__:0,length:0}; 6 | with (v9) { 7 | const v11 = length; 8 | } 9 | " 10 | 11 | (* TODO: This may not be correct, as length should come from v9 *) 12 | let correct = 13 | let builder = init_builder false false false in 14 | let int_0_1, load_int_0_1 = build_load_integer 0L builder in 15 | let int_0_2, load_int_0_2 = build_load_integer 0L builder in 16 | let obj_temp, create_obj = build_create_object ["__proto__"; "length"] [int_0_1; int_0_2] builder in 17 | let begin_with = build_begin_with_op obj_temp builder in 18 | let _, load_builtin = build_load_builtin "placeholder" builder in 19 | let end_with = build_end_with_op builder in 20 | let res = [load_int_0_1; load_int_0_2; create_obj; begin_with; load_builtin; end_with] in 21 | List.map inst_to_prog_inst res 22 | 23 | let test () = 24 | let (ast, errors) = Compiler.string_to_flow_ast input in 25 | let prog = Compiler.flow_ast_to_inst_list ast false false true in 26 | Alcotest.(check (list Util.inst_testable)) "with" correct prog -------------------------------------------------------------------------------- /Docs/ProcessingModel.md: -------------------------------------------------------------------------------- 1 | # Processing Model 2 | 3 | Fuzzilli's processing and threading model is fairly simple: every Fuzzer 4 | instance has an associated sequential 5 | [DispatchQueue](https://developer.apple.com/documentation/dispatch/dispatchqueue) 6 | on which all interactions with the fuzzer must happen. This architecture avoids 7 | race conditions as work items are processed sequentially. It also makes it 8 | rather straight forward to run multiple Fuzzer instances in one process. 9 | Whenever code wants to interact with a fuzzer instance (i.e. call methods on 10 | it, access properties, etc.) but (potentially) executes on a different 11 | DispatchQueue, it has to first enqueue an operation into the Fuzzer's queue. 12 | For that, the Fuzzer class exposes the `sync` and `async` functions which 13 | essentially just enqueue the given work item into the fuzzer's DispatchQueue 14 | and which can safely be called from a separate thread: 15 | 16 | fuzzer.async { 17 | // Can now interact with the fuzzer         18 | fuzzer.importProgram(someProgram) 19 | } 20 | 21 | Any code that is invoked by Fuzzilli (e.g. Mutators, Module initializers, 22 | CodeGenerators, Event and Timer handlers, etc.) will always execute on the 23 | fuzzer's dispatch queue and thus does not need to worry about enqueuing tasks 24 | first. Only if code uses separate DispatchQueues, threads, etc. must it ensure 25 | that it always interacts with the fuzzer on the correct queue. See e.g. the 26 | ThreadSync or NetworkSync module for examples of this. 27 | 28 | The dispatch queue of a typical Fuzzer instance commonly contains (some of) the 29 | following items: 30 | 31 | * A call to Fuzzer.fuzzOne to perform one iteration of fuzzing. When fuzzOne 32 | finishes, it will schedule the next fuzzing iteration. 33 | * Handler blocks for any timers scheduled via the Fuzzer.timers API that have 34 | recently triggered 35 | * Handlers for incoming network connections and data if the NetworkSync module 36 | is active 37 | * Handlers for messages from other fuzzers if any of the fuzzer synchronization 38 | modules are active 39 | * Program executions scheduled by the minimizer (which runs on a separate queue 40 | since it often takes a long time to complete) 41 | -------------------------------------------------------------------------------- /Docs/images/fuzzing_with_fuzzil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Docs/images/fuzzing_with_fuzzil.png -------------------------------------------------------------------------------- /Docs/images/generative_engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Docs/images/generative_engine.png -------------------------------------------------------------------------------- /Docs/images/hybrid_engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Docs/images/hybrid_engine.png -------------------------------------------------------------------------------- /Docs/images/mutation_engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Docs/images/mutation_engine.png -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // 3 | // Copyright 2019 Google LLC 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // https://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import PackageDescription 18 | 19 | let package = Package( 20 | name: "Fuzzilli", 21 | platforms: [ 22 | .macOS(.v10_13), 23 | ], 24 | products: [ 25 | .library(name: "Fuzzilli", targets: ["Fuzzilli"]), 26 | ], 27 | dependencies: [ 28 | .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.6.0"), 29 | ], 30 | targets: [ 31 | .target(name: "libsocket", dependencies: []), 32 | .target(name: "libreprl", dependencies: []), 33 | // Using '-c release' when building uses '-O2', so '-O3' provides a performance gain 34 | .target(name: "libcoverage", dependencies: [], cSettings: [.unsafeFlags(["-O3"])], linkerSettings: [.linkedLibrary("rt", .when(platforms: [.linux]))]), 35 | .target(name: "Fuzzilli", dependencies: ["SwiftProtobuf", "libsocket", "libreprl", "libcoverage", "JS"]), 36 | .target(name: "REPRLRun", dependencies: ["libreprl"]), 37 | .target(name: "FuzzilliCli", dependencies: ["Fuzzilli"]), 38 | .target(name: "JS", dependencies: []), 39 | .target(name: "Benchmarks", dependencies: ["Fuzzilli"]), 40 | .target(name: "FuzzILTool", dependencies: ["Fuzzilli"]), 41 | 42 | .testTarget(name: "FuzzilliTests", dependencies: ["Fuzzilli"]), 43 | ], 44 | swiftLanguageVersions: [.v5] 45 | ) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fuzzilli-WASM 2 | A toy fuzzer for wasm fuzzing based on [Fuzzilli](https://github.com/googleprojectzero/fuzzilli), which will generate grammatically and semantically correct javascript code containing wasm features for fuzzing. 3 | 4 | ## WASM Features 5 | - [Global](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) 6 | 7 | - [Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module) 8 | 9 | - [Memory](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory) 10 | 11 | - [Instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance) 12 | 13 | - [Table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table) 14 | 15 | - [Method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly#Methods) 16 | 17 | ## Build 18 | Same as [Fuzzilli](https://github.com/googleprojectzero/fuzzilli) 19 | -------------------------------------------------------------------------------- /Sources/Benchmarks/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | import Fuzzilli 17 | 18 | // Tiny benchmarking suite 19 | 20 | // How often to repeat every benchmark 21 | let repetitions = 10 22 | 23 | // Current timestamp in seconds (with at least millisecond precision) 24 | func now() -> Double { 25 | return Date().timeIntervalSince1970 26 | } 27 | 28 | 29 | func benchmarkCodeGeneration() { 30 | let corpus = BasicCorpus(minSize: 1, maxSize: 1000, minMutationsPerSample: 5) 31 | let fuzzer = makeMockFuzzer(corpus: corpus) 32 | let b = fuzzer.makeBuilder() 33 | 34 | for _ in 0..<1000 { 35 | b.generate(n: 100) 36 | let program = b.finalize() 37 | 38 | // Add to corpus since generate() does splicing as well 39 | fuzzer.corpus.add(program, ProgramAspects(outcome: .succeeded)) 40 | } 41 | } 42 | 43 | // TODO add more, e.g. for mutators 44 | var benchmarks: [String: () -> ()] = [ 45 | "CodeGenerationBenchmark": benchmarkCodeGeneration 46 | ] 47 | 48 | for (name, benchmark) in benchmarks { 49 | var totalTime = 0.0 50 | for _ in 0.. 22 | 23 | /// The current active engine. 24 | private var activeEngine: FuzzEngine 25 | 26 | /// The number of fuzzing iterations to complete per engine. 27 | private let iterationsPerEngine: Int 28 | 29 | /// The number of fuzzing iterations. 30 | private var currentIteration = 0 31 | 32 | public init(engines: WeightedList, initialActive: FuzzEngine? = nil, iterationsPerEngine: Int) { 33 | assert(iterationsPerEngine > 0) 34 | self.iterationsPerEngine = iterationsPerEngine 35 | self.engines = engines 36 | self.activeEngine = initialActive ?? engines.randomElement() 37 | super.init(name: "MultiEngine") 38 | } 39 | 40 | override func initialize() { 41 | for engine in engines { 42 | engine.initialize(with: self.fuzzer) 43 | } 44 | } 45 | 46 | public func fuzzOne(_ group: DispatchGroup) { 47 | activeEngine.fuzzOne(group) 48 | currentIteration += 1 49 | if currentIteration % iterationsPerEngine == 0 { 50 | activeEngine = engines.randomElement() 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Core/ProgramGeneratorStats.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | public struct ProgramGeneratorStats { 16 | private var validSamples = 0 17 | private var invalidSamples = 0 18 | 19 | mutating func producedValidSample() { 20 | validSamples += 1 21 | } 22 | 23 | mutating func producedInvalidSample() { 24 | invalidSamples += 1 25 | } 26 | 27 | var correctnessRate: Double { 28 | let totalSamples = validSamples + invalidSamples 29 | guard totalSamples > 0 else { return 1.0 } 30 | return Double(validSamples) / Double(totalSamples) 31 | } 32 | 33 | // TODO: Maybe also add a counter to track how often it generated new coverage? 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Core/ProgramOrigin.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | // Enum to identify the origin of a Program. 18 | public enum ProgramOrigin: Equatable { 19 | // The program was generated by this instance. 20 | case local 21 | 22 | // The program is part of a corpus that is being imported. 23 | case corpusImport(shouldMinimize: Bool) 24 | 25 | // The program was sent by a worker instance, identified by the UUID 26 | // Note: the UUID identifies the sending instance, which is not 27 | // necessarily the intance that originally generated the program. 28 | case worker(id: UUID) 29 | 30 | // The program was sent by our master instance. 31 | // As above, this does not necessarily mean that the master generated 32 | // this program, just that it was received from it. For example, it is 33 | // possible that another worker generated the program, sent it to the 34 | // master, and the master then sent it to us. In this case, the origin 35 | // would also be .master. 36 | case master 37 | 38 | /// Whether a program with this origin still requires minimization or not. 39 | public func requiresMinimization() -> Bool { 40 | switch self { 41 | case .local: 42 | return true 43 | case .corpusImport(let shouldMinimize): 44 | return shouldMinimize 45 | case .worker, .master: 46 | return false 47 | } 48 | } 49 | 50 | /// Whether the origin is another fuzzer instance. 51 | public func isFromOtherInstance() -> Bool { 52 | switch self { 53 | case .worker, .master: 54 | return true 55 | default: 56 | return false 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Evaluation/ProgramAspects.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Aspects of a program that make it special. 16 | public class ProgramAspects: CustomStringConvertible { 17 | let outcome: ExecutionOutcome 18 | 19 | public init(outcome: ExecutionOutcome) { 20 | self.outcome = outcome 21 | } 22 | 23 | public var description: String { 24 | return "execution outcome \(outcome)" 25 | } 26 | 27 | // The total number of aspects 28 | public var count: UInt64 { 29 | return 0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Evaluation/ProgramEvaluator.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | public protocol ProgramEvaluator: Component { 18 | /// Evaluates a program. 19 | /// 20 | /// - Parameter execution: An execution of the program to evaluate. 21 | /// - Returns: The programs special aspects if it has any, nil otherwise. 22 | func evaluate(_ execution: Execution) -> ProgramAspects? 23 | 24 | /// Evaluates a crash. 25 | /// 26 | /// - Parameter execution: An execution of the program to evaluate. 27 | /// - Returns: the programs special aspects if it has any, nil otherwise. 28 | func evaluateCrash(_ execution: Execution) -> ProgramAspects? 29 | 30 | /// Checks whether a program has the given aspects. 31 | func hasAspects(_ execution: Execution, _ aspects: ProgramAspects) -> Bool 32 | 33 | /// The current, accumulated score of all seen samples. E.g. total coverage. 34 | var currentScore: Double { get } 35 | 36 | /// Export the current state of this evaluator so it can be replicated. 37 | func exportState() -> Data 38 | 39 | /// Import a previously exported state. 40 | func importState(_ state: Data) throws 41 | 42 | // Resets the provided aspects and executes the program a second time. 43 | // If aspects are successfully collected from the second execution, returns 44 | // the intersection of the initally provided aspects with the aspects from the 45 | // second execution. If it fails at any point, returns nil 46 | func evaluateAndIntersect(_ program: Program, with aspects: ProgramAspects) -> ProgramAspects? 47 | 48 | /// Resets the internal state 49 | func resetState() 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Execution/Execution.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | /// The possible outcome of a program execution. 18 | public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { 19 | case crashed(Int) 20 | case failed(Int) 21 | case succeeded 22 | case timedOut 23 | 24 | public var description: String { 25 | switch self { 26 | case .crashed(let signal): 27 | return "Crashed (signal \(signal))" 28 | case .failed(let exitcode): 29 | return "Failed (exit code \(exitcode))" 30 | case .succeeded: 31 | return "Succeeded" 32 | case .timedOut: 33 | return "TimedOut" 34 | } 35 | } 36 | 37 | public func isCrash() -> Bool { 38 | if case .crashed = self { 39 | return true 40 | } else { 41 | return false 42 | } 43 | } 44 | } 45 | 46 | /// The result of executing a program. 47 | public protocol Execution { 48 | /// The execution outcome 49 | var outcome: ExecutionOutcome { get } 50 | 51 | /// The program's stdout 52 | var stdout: String { get } 53 | 54 | /// The program's stderr 55 | var stderr: String { get } 56 | 57 | /// The program's FuzzIL output 58 | var fuzzout: String { get } 59 | 60 | /// Execution time in microseconds 61 | var execTime: TimeInterval { get } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Execution/ScriptRunner.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | public protocol ScriptRunner: Component { 16 | /// Executes a script, waits for it to complete, and returns the result. 17 | func run(_ script: String, withTimeout timeout: UInt32) -> Execution 18 | 19 | /// Sets an environment variable for the child process. Must only be called before initialization. 20 | func setEnvironmentVariable(_ key: String, to value: String) 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/FuzzIL/Context.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Current context in the program 16 | public struct Context: OptionSet { 17 | public let rawValue: Int 18 | 19 | public init(rawValue: Int) { 20 | self.rawValue = rawValue 21 | } 22 | 23 | // Default script context 24 | public static let script = Context(rawValue: 1 << 0) 25 | // Inside a function definition 26 | public static let function = Context(rawValue: 1 << 1) 27 | // Inside a generator function definition 28 | public static let generatorFunction = Context(rawValue: 1 << 2) 29 | // Inside an async function definition 30 | public static let asyncFunction = Context(rawValue: 1 << 3) 31 | // Inside a loop 32 | public static let loop = Context(rawValue: 1 << 4) 33 | // Inside a with statement 34 | public static let with = Context(rawValue: 1 << 5) 35 | // Inside a class definition 36 | public static let classDefinition = Context(rawValue: 1 << 6) 37 | // Inside a switch block 38 | public static let switchCase = Context(rawValue: 1 << 7) 39 | 40 | public static let empty = Context([]) 41 | } -------------------------------------------------------------------------------- /Sources/Fuzzilli/FuzzIL/ProgramComments.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | /// Comments that can be attached to a Program. 18 | public struct ProgramComments { 19 | private static let headerIndex = Int32(-1) 20 | private static let footerIndex = Int32(-2) 21 | 22 | public enum CommentPosition { 23 | case header 24 | case footer 25 | case instruction(Int) 26 | 27 | fileprivate func toKey() -> Int32 { 28 | switch self { 29 | case .header: 30 | return ProgramComments.headerIndex 31 | case .footer: 32 | return ProgramComments.footerIndex 33 | case .instruction(let index): 34 | assert(index >= 0 && index <= UInt16.max) 35 | return Int32(index + 2) 36 | } 37 | } 38 | } 39 | 40 | private var comments: [Int32:String] = [:] 41 | 42 | public init() {} 43 | 44 | public var isEmpty: Bool { 45 | return comments.isEmpty 46 | } 47 | 48 | public func at(_ position: CommentPosition) -> String? { 49 | let key = position.toKey() 50 | return comments[key] 51 | } 52 | 53 | public mutating func add(_ content: String, at position: CommentPosition) { 54 | let key = position.toKey() 55 | 56 | var comment = "" 57 | if let currentContent = comments[key] { 58 | comment += currentContent + "\n" 59 | } 60 | comment += content 61 | 62 | comments[key] = comment 63 | } 64 | 65 | public mutating func removeAll() { 66 | comments.removeAll() 67 | } 68 | } 69 | 70 | extension ProgramComments: ProtobufConvertible { 71 | public typealias ProtobufType = [Int32: String] 72 | 73 | public func asProtobuf() -> ProtobufType { 74 | return comments 75 | } 76 | 77 | public init(from protobuf: ProtobufType) { 78 | self.comments = protobuf 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/FuzzIL/TypeCollectionStatus.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | public enum TypeCollectionStatus: UInt8 { 15 | case success 16 | case error 17 | case timeout 18 | case notAttempted 19 | 20 | public init(from typeCollectionOutcome: ExecutionOutcome){ 21 | switch typeCollectionOutcome { 22 | case .crashed: self = .error 23 | case .failed: self = .error 24 | case .succeeded: self = .success 25 | case .timedOut: self = .timeout 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/FuzzIL/TypeInfo.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | public enum TypeQuality: UInt8 { 15 | case inferred 16 | case runtime 17 | } 18 | 19 | // Store known types for program variables at specific instructions 20 | public struct TypeInfo: Equatable { 21 | private let _index: UInt16 22 | public let type: Type 23 | public let quality: TypeQuality 24 | public var index: Int { 25 | return Int(_index) 26 | } 27 | 28 | public init(index: Int, type: Type, quality: TypeQuality) { 29 | self._index = UInt16(index) 30 | self.type = type 31 | self.quality = quality 32 | } 33 | 34 | public static func == (lhs: TypeInfo, rhs: TypeInfo) -> Bool { 35 | return lhs._index == rhs._index && lhs.type == rhs.type && lhs.quality == rhs.quality 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/FuzzIL/Variable.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A variable in the FuzzIL language. 16 | /// 17 | /// Variables names (numbers) are local to a program. Different programs 18 | /// will have the same variable names referring to different things. 19 | public struct Variable: Hashable, CustomStringConvertible { 20 | // We assume that programs will always have less than 64k variables 21 | private let num: UInt16 22 | 23 | public init(number: Int) { 24 | self.num = UInt16(number) 25 | } 26 | 27 | public var number: Int { 28 | return Int(num) 29 | } 30 | 31 | public var identifier: String { 32 | return "v\(number)" 33 | } 34 | 35 | public var description: String { 36 | return identifier 37 | } 38 | 39 | public static func ==(lhs: Variable, rhs: Variable) -> Bool { 40 | return lhs.number == rhs.number 41 | } 42 | 43 | public static func isValidVariableNumber(_ number: Int) -> Bool { 44 | return UInt16(exactly: number) != nil 45 | } 46 | } 47 | 48 | extension Variable: Comparable { 49 | public static func <(lhs: Variable, rhs: Variable) -> Bool { 50 | return lhs.number < rhs.number 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Lifting/InliningPolicy.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | public protocol InliningPolicy { 16 | func shouldInline(_ expr: Expression) -> Bool 17 | } 18 | 19 | public struct AlwaysInline: InliningPolicy { 20 | public init() {} 21 | public func shouldInline(_ expr: Expression) -> Bool { 22 | return true 23 | } 24 | } 25 | 26 | public struct NeverInline: InliningPolicy { 27 | public init() {} 28 | public func shouldInline(_ expr: Expression) -> Bool { 29 | return false 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Lifting/Lifter.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Lifts a FuzzIL program to the target language. 16 | public protocol Lifter { 17 | func lift(_ program: Program, withOptions options: LiftingOptions) -> String 18 | } 19 | 20 | extension Lifter { 21 | public func lift(_ program: Program) -> String { 22 | return lift(program, withOptions: []) 23 | } 24 | } 25 | 26 | public struct LiftingOptions: OptionSet { 27 | public let rawValue: Int 28 | public init(rawValue: Int) { 29 | self.rawValue = rawValue 30 | } 31 | 32 | public static let dumpTypes = LiftingOptions(rawValue: 1 << 0) 33 | public static let minify = LiftingOptions(rawValue: 1 << 1) 34 | public static let collectTypes = LiftingOptions(rawValue: 1 << 2) 35 | public static let includeComments = LiftingOptions(rawValue: 1 << 3) 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Lifting/TypeCollectionAnalyzer.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Determine what variable types we should collect given instruction 16 | struct TypeCollectionAnalyzer { 17 | // Special properties which significantly change variable type 18 | // We should recollect type if we change them 19 | let specialProperties = ["__proto__"] 20 | 21 | func analyze(_ instr: Instruction) -> [Variable] { 22 | switch instr.op { 23 | case is LoadInteger, is LoadBigInt, is LoadFloat, is LoadBoolean, is LoadNull, is LoadUndefined, 24 | is TypeOf, is InstanceOf, is In, is Dup, is Reassign, is ReassignWithBinop, is Compare, is BeginForIn: 25 | // No need to collect types for instructions interpreter can handle 26 | return [] 27 | case is BeginAnyFunctionDefinition: 28 | // No type collection on function definitions for now 29 | return [] 30 | case let op as StoreProperty where specialProperties.contains(op.propertyName): 31 | // Recollect type of this variable, because major change happened 32 | return [instr.input(0)] 33 | default: 34 | // By default, collect types for all outputs of an instruction 35 | return Array(instr.allOutputs) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Minimization/GenericInstructionReducer.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Removes simple instructions from a program if they are not required. 16 | struct GenericInstructionReducer: Reducer { 17 | func reduce(_ code: inout Code, with verifier: ReductionVerifier) { 18 | for instr in code.reversed() { 19 | if !instr.isSimple || instr.op is Nop { 20 | continue 21 | } 22 | 23 | verifier.tryNopping(instructionAt: instr.index, in: &code) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Minimization/ReplaceReducer.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Attemplts to replace code snippets with other, potentially shorter snippets. 16 | struct ReplaceReducer: Reducer { 17 | func reduce(_ code: inout Code, with verifier: ReductionVerifier) { 18 | for instr in code { 19 | switch instr.op { 20 | case let op as Construct: 21 | // Try replacing with a simple call 22 | let newOp = CallFunction(numArguments: op.numArguments, spreads: [Bool](repeating: false, count: op.numArguments)) 23 | verifier.tryReplacing(instructionAt: instr.index, with: Instruction(newOp, inouts: instr.inouts), in: &code) 24 | default: 25 | break 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Modules/Module.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // A module interacts with the fuzzer in some way but does not provide services 16 | // to the rest of the fuzzer and is not required for basic functionality. 17 | public protocol Module { 18 | // Will be called after the fuzzer is fully initialized and able to execute programs. 19 | // At this point, all other modules will have been instantiated but might not be initialized yet. 20 | // Useful if a module makes use of another module. 21 | func initialize(with fuzzer: Fuzzer) 22 | } 23 | 24 | extension Module { 25 | public func initialize(with fuzzer: Fuzzer) {} 26 | 27 | public var name: String { 28 | return String(describing: type(of: self)) 29 | } 30 | 31 | public static var name: String { 32 | return String(describing: self) 33 | } 34 | 35 | /// Returns the instance of this module on the provided fuzzer instance if it exists, nil otherwise. 36 | public static func instance(for fuzzer: Fuzzer) -> Self? { 37 | if let instance = fuzzer.modules[self.name] { 38 | return instance as? Self 39 | } else { 40 | return nil 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Mutators/CodeGenMutator.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A mutator that generates new code at random positions in a program. 16 | public class CodeGenMutator: BaseInstructionMutator { 17 | var analyzer = DeadCodeAnalyzer() 18 | 19 | public init() { 20 | super.init(maxSimultaneousMutations: defaultMaxSimultaneousMutations) 21 | } 22 | 23 | public override func beginMutation(of program: Program) { 24 | analyzer = DeadCodeAnalyzer() 25 | } 26 | 27 | public override func canMutate(_ instr: Instruction) -> Bool { 28 | analyzer.analyze(instr) 29 | return !analyzer.currentlyInDeadCode 30 | } 31 | 32 | public override func mutate(_ instr: Instruction, _ b: ProgramBuilder) { 33 | b.adopt(instr, keepTypes: true) 34 | b.generate(n: Int.random(in: 1...defaultMaxCodeGenerationAmount)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Mutators/CombineMutator.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A mutator that inserts a program in full into another one. 16 | public class CombineMutator: BaseInstructionMutator { 17 | var analyzer = DeadCodeAnalyzer() 18 | 19 | public init() {} 20 | 21 | public override func beginMutation(of program: Program) { 22 | analyzer = DeadCodeAnalyzer() 23 | } 24 | 25 | public override func canMutate(_ instr: Instruction) -> Bool { 26 | analyzer.analyze(instr) 27 | return !analyzer.currentlyInDeadCode 28 | } 29 | 30 | public override func mutate(_ instr: Instruction, _ b: ProgramBuilder) { 31 | b.adopt(instr, keepTypes: true) 32 | let other = b.fuzzer.corpus.randomElementForSplicing() 33 | b.trace("Inserting program \(other.id)") 34 | b.append(other) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Mutators/ConcatMutator.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A mutator that concatenates two programs together. 16 | public class ConcatMutator: Mutator { 17 | override func mutate(_ program: Program, using b: ProgramBuilder) -> Program? { 18 | let suffix = b.fuzzer.corpus.randomElementForSplicing() 19 | 20 | b.append(program) 21 | b.trace("Appending program \(suffix.id)") 22 | b.append(suffix) 23 | 24 | return b.finalize() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Mutators/JITStressMutator.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A mutator designed to call already JITed functions with different arguments or environment. 16 | /// 17 | /// In a way, this is a workaround for the fact that we don't have coverage feedback from JIT code. 18 | public class JITStressMutator: Mutator { 19 | public override init() {} 20 | 21 | override func mutate(_ program: Program, using b: ProgramBuilder) -> Program? { 22 | b.append(program) 23 | 24 | // Possibly change the environment 25 | b.generate(n: Int.random(in: 1...defaultMaxCodeGenerationAmount)) 26 | 27 | // Call an existing (and hopefully JIT compiled) function again 28 | guard let f = b.randVar(ofConservativeType: .function()) else { return nil } 29 | guard let arguments = b.randCallArguments(for: f) else { return nil } 30 | b.callFunction(f, withArgs: arguments) 31 | return b.finalize() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Mutators/Mutator.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A mutator takes an existing program and mutates it in some way, thus producing a new program. 16 | public class Mutator { 17 | public var stats = ProgramGeneratorStats() 18 | 19 | /// Mutates the given program. 20 | /// 21 | /// - Parameters: 22 | /// - program: The program to mutate. 23 | /// - fuzzer: The fuzzer context for the mutation. 24 | /// - Returns: The mutated program or nil if the given program could not be mutated. 25 | public func mutate(_ program: Program, for fuzzer: Fuzzer) -> Program? { 26 | let b = fuzzer.makeBuilder(forMutating: program) 27 | b.traceHeader("Mutating \(program.id) with \(name)") 28 | return mutate(program, using: b) 29 | } 30 | 31 | func mutate(_ program: Program, using b: ProgramBuilder) -> Program? { 32 | fatalError("This method must be overridden") 33 | } 34 | 35 | /// The name of this mutator. 36 | public var name: String { 37 | return String(describing: type(of: self)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Mutators/MutatorSettings.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | // The settings here strive to achieve a correctness rate of around 75%. Empirically, this appears to be roughly optimal: 17 | // higher than that, and samples are too similar to each other, lower than that, and too many samples are invalid. 18 | // TODO evaluate this independently for every mutator. 19 | 20 | let defaultMaxSimultaneousMutations = 7 21 | let defaultMaxCodeGenerationAmount = 5 22 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Protobuf/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzilli Protobuf Definitons 2 | 3 | Install the protoc compiler and the swift plugin: 4 | 5 | brew install swift-protobuf 6 | 7 | Then generate the swift files: 8 | 9 | protoc --swift_opt=Visibility=Public --swift_out=. program.proto operations.proto typesystem.proto sync.proto 10 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Protobuf/typesystem.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | package fuzzilli.protobuf; 17 | 18 | message Type { 19 | uint32 definiteType = 1; 20 | uint32 possibleType = 2; 21 | // The extension can be encoded as an index, referring to the 22 | // nth TypeExtension in the whole message. That way, it becomes 23 | // possible to encode duplicate extensions only once. 24 | oneof ext { 25 | uint32 extensionIdx = 3; 26 | TypeExtension extension = 4; 27 | } 28 | } 29 | 30 | message TypeExtension { 31 | repeated string properties = 1; 32 | repeated string methods = 2; 33 | string group = 3; 34 | FunctionSignature signature = 4; 35 | } 36 | 37 | message FunctionSignature { 38 | repeated Parameter parameters = 1; 39 | Type outputType = 2; 40 | } 41 | 42 | message Parameter { 43 | oneof param { 44 | PlainParameter plainParameter = 1; 45 | OptionalParameter optionalParameter = 2; 46 | RestParameter restParameter = 3; 47 | } 48 | } 49 | 50 | message PlainParameter { 51 | Type inputType = 1; 52 | } 53 | 54 | message OptionalParameter { 55 | Type inputType = 2; 56 | } 57 | 58 | message RestParameter { 59 | Type inputType = 2; 60 | } -------------------------------------------------------------------------------- /Sources/Fuzzilli/Util/CInterop.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | func convertToCArray(_ array: [String]) -> UnsafeMutablePointer?> { 18 | let buffer = UnsafeMutablePointer?>.allocate(capacity: array.count + 1) 19 | for (i, str) in array.enumerated() { 20 | #if os(Windows) 21 | buffer[i] = UnsafePointer(str.withCString(_strdup)) 22 | #else 23 | buffer[i] = UnsafePointer(str.withCString(strdup)) 24 | #endif 25 | } 26 | buffer[array.count] = nil 27 | return buffer 28 | } 29 | 30 | func freeCArray(_ array: UnsafeMutablePointer?>, numElems: Int) { 31 | for arg in array ..< array + numElems { 32 | free(UnsafeMutablePointer(mutating: arg.pointee!)) 33 | } 34 | array.deallocate() 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Util/Error.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | // Simple error enum for errors that only need to be displayed to the user. 18 | public enum FuzzilliError: Error { 19 | case instructionDecodingError(String) 20 | case typeDecodingError(String) 21 | case programDecodingError(String) 22 | case corpusImportError(String) 23 | case evaluatorStateImportError(String) 24 | case codeVerificationError(String) 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Util/Misc.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | func currentMillis() -> UInt64 { 18 | return UInt64(Date().timeIntervalSince1970 * 1000) 19 | } 20 | 21 | func uniqueElements(of list: [E]) -> [E] where E: Hashable { 22 | return Array(Set(list)) 23 | } 24 | 25 | func align(_ v: Int, to desiredAlignment: Int) -> Int { 26 | let remainder = v % desiredAlignment 27 | return remainder != 0 ? desiredAlignment - remainder : 0 28 | } 29 | 30 | func measureTime(_ operation: () -> R) -> (R, Double) { 31 | let start = Date() 32 | let r = operation() 33 | let end = Date() 34 | return (r, end.timeIntervalSince(start)) 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Util/MovingAverage.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Computes the average of some value over the last N samples. 16 | struct MovingAverage { 17 | private let n: Int 18 | private var lastN: [Double] 19 | private var sum = 0.0 20 | private var oldest = 0 21 | private var seen = 0 22 | 23 | var currentValue: Double { 24 | return sum / Double(min(seen, n)) 25 | } 26 | 27 | init(n: Int) { 28 | self.n = n 29 | lastN = [Double](repeating: 0, count: n) 30 | } 31 | 32 | mutating func add(_ value: Double) { 33 | seen += 1 34 | 35 | sum -= lastN[oldest] 36 | lastN[oldest] = value 37 | sum += value 38 | 39 | oldest = (oldest + 1) % n 40 | } 41 | 42 | mutating func add(_ value: Int) { 43 | add(Double(value)) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Fuzzilli/Util/WeightedList.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Hacky implementation of a weighted list of elements. 16 | /// 17 | /// An element with weight 2 is 2x more likely to be selected by randomElement() than an element with weight 1. And so on. 18 | public struct WeightedList: Sequence { 19 | fileprivate var array = [Element]() 20 | fileprivate var elems = [Element]() 21 | 22 | public init(_ values: [(Element, Int)]) { 23 | for (e, w) in values { 24 | append(e, withWeight: w) 25 | } 26 | } 27 | 28 | fileprivate init(_ array: [Element], _ elems: [Element]) { 29 | self.array = array 30 | self.elems = elems 31 | } 32 | 33 | public mutating func append(_ elem: Element, withWeight weight: Int) { 34 | for _ in 0.. Element { 41 | return chooseUniform(from: array) 42 | } 43 | 44 | public func makeIterator() -> Array.Iterator { 45 | return elems.makeIterator() 46 | } 47 | 48 | public var count: Int { 49 | return elems.count 50 | } 51 | 52 | public mutating func append(_ rhs: WeightedList) { 53 | array += rhs.array 54 | elems += rhs.elems 55 | } 56 | } 57 | 58 | public func +(lhs: WeightedList, rhs: WeightedList) -> WeightedList { 59 | return WeightedList(lhs.array + rhs.array, lhs.elems + rhs.elems) 60 | } 61 | -------------------------------------------------------------------------------- /Sources/FuzzilliCli/Profiles/DuktapeProfile.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Fuzzilli 16 | 17 | let duktapeProfile = Profile( 18 | processArguments: ["--reprl"], 19 | 20 | processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], 21 | 22 | codePrefix: """ 23 | function placeholder(){} 24 | function main() { 25 | """, 26 | 27 | codeSuffix: """ 28 | } 29 | main(); 30 | """, 31 | 32 | ecmaVersion: ECMAScriptVersion.es5, 33 | 34 | crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)"], 35 | 36 | additionalCodeGenerators: WeightedList([]), 37 | 38 | additionalProgramTemplates: WeightedList([]), 39 | 40 | disabledCodeGenerators: [], 41 | 42 | additionalBuiltins: [ 43 | "CBOR.encode" : .function([.plain(.anything)] => .object()), 44 | "CBOR.decode" : .function([.plain(.object())] => .object()), 45 | "Duktape.fin" : .function([.plain(.object()), .opt(.function())] => .undefined), 46 | "Duktape.act" : .function([.plain(.number)] => .object()), 47 | "Duktape.gc" : .function([] => .undefined), 48 | "Duktape.compact" : .function([.plain(.object())] => .undefined), 49 | "placeholder" : .function([] => .undefined), 50 | 51 | ] 52 | ) 53 | -------------------------------------------------------------------------------- /Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Fuzzilli 16 | 17 | let jerryscriptProfile = Profile( 18 | processArguments: ["-r","-s"], 19 | 20 | // processEnv: [:], 21 | processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], 22 | 23 | codePrefix: """ 24 | function placeholder(){} 25 | function main() { 26 | """, 27 | 28 | codeSuffix: """ 29 | } 30 | main(); 31 | """, 32 | 33 | ecmaVersion: ECMAScriptVersion.es5, 34 | 35 | crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)"], 36 | 37 | additionalCodeGenerators: WeightedList([]), 38 | 39 | additionalProgramTemplates: WeightedList([]), 40 | 41 | disabledCodeGenerators: [], 42 | 43 | additionalBuiltins: [: 44 | // "gc" : .function([] => .undefined), 45 | // "print" : .function([] => .undefined), 46 | // "resourceName" : .function([] => .undefined), 47 | // "placeholder" : .function([] => .undefined), 48 | ] 49 | ) 50 | -------------------------------------------------------------------------------- /Sources/FuzzilliCli/Profiles/Profile.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Fuzzilli 16 | 17 | struct Profile { 18 | let processArguments: [String] 19 | let processEnv: [String : String] 20 | let codePrefix: String 21 | let codeSuffix: String 22 | let ecmaVersion: ECMAScriptVersion 23 | 24 | // JavaScript code snippets that cause a crash in the target engine. 25 | // Used to verify that crashes can be detected. 26 | let crashTests: [String] 27 | 28 | let additionalCodeGenerators: WeightedList 29 | let additionalProgramTemplates: WeightedList 30 | 31 | let disabledCodeGenerators: [String] 32 | 33 | let additionalBuiltins: [String: Type] 34 | } 35 | 36 | let profiles = [ 37 | "qjs": qjsProfile, 38 | "jsc": jscProfile, 39 | "spidermonkey": spidermonkeyProfile, 40 | "v8": v8Profile, 41 | "duktape": duktapeProfile, 42 | "jerryscript": jerryscriptProfile, 43 | ] 44 | -------------------------------------------------------------------------------- /Sources/FuzzilliCli/Profiles/QjsProfile.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Fuzzilli 16 | 17 | let qjsProfile = Profile( 18 | processArguments: ["--reprl"], 19 | 20 | processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], 21 | 22 | codePrefix: """ 23 | function placeholder(){} 24 | function main() { 25 | """, 26 | 27 | codeSuffix: """ 28 | } 29 | main(); 30 | """, 31 | 32 | ecmaVersion: ECMAScriptVersion.es6, 33 | 34 | crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"], 35 | 36 | additionalCodeGenerators: WeightedList([]), 37 | 38 | additionalProgramTemplates: WeightedList([]), 39 | 40 | disabledCodeGenerators: [], 41 | 42 | additionalBuiltins: [ 43 | "placeholder" : .function([] => .undefined) 44 | ] 45 | ) 46 | -------------------------------------------------------------------------------- /Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Fuzzilli 16 | 17 | fileprivate let ForceSpidermonkeyIonGenerator = CodeGenerator("ForceSpidermonkeyIonGenerator", input: .function()) { b, f in 18 | guard let arguments = b.randCallArguments(for: f) else { return } 19 | 20 | let start = b.loadInt(0) 21 | let end = b.loadInt(100) 22 | let step = b.loadInt(1) 23 | b.forLoop(start, .lessThan, end, .Add, step) { _ in 24 | b.callFunction(f, withArgs: arguments) 25 | } 26 | } 27 | 28 | let spidermonkeyProfile = Profile( 29 | processArguments: [ 30 | "--baseline-warmup-threshold=10", 31 | "--ion-warmup-threshold=100", 32 | "--ion-check-range-analysis", 33 | "--ion-extra-checks", 34 | "--fuzzing-safe", 35 | "--reprl", 36 | ], 37 | 38 | processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], 39 | 40 | codePrefix: """ 41 | function placeholder(){} 42 | function main() { 43 | """, 44 | 45 | codeSuffix: """ 46 | gc(); 47 | } 48 | main(); 49 | """, 50 | 51 | ecmaVersion: ECMAScriptVersion.es6, 52 | 53 | crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"], 54 | 55 | additionalCodeGenerators: WeightedList([ 56 | (ForceSpidermonkeyIonGenerator, 10), 57 | ]), 58 | 59 | additionalProgramTemplates: WeightedList([]), 60 | 61 | disabledCodeGenerators: [], 62 | 63 | additionalBuiltins: [ 64 | "gc" : .function([] => .undefined), 65 | "enqueueJob" : .function([.plain(.function())] => .undefined), 66 | "drainJobQueue" : .function([] => .undefined), 67 | "bailout" : .function([] => .undefined), 68 | "placeholder" : .function([] => .undefined), 69 | 70 | ] 71 | ) 72 | -------------------------------------------------------------------------------- /Sources/FuzzilliCli/ProgramTemplateWeights.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Fuzzilli 16 | 17 | /// Assigned weights for the builtin program templates. 18 | let programTemplateWeights = [ 19 | "JIT1Function": 2, 20 | "JIT2Functions": 2, 21 | "TypeConfusionTemplate": 1, 22 | ] 23 | -------------------------------------------------------------------------------- /Sources/JS/LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | -------------------------------------------------------------------------------- /Sources/JS/README.md: -------------------------------------------------------------------------------- 1 | # Javascript code 2 | 3 | We want to use these JS snippets when creating programs which collect runtime types. 4 | To make them avaibale to the Lifter, we need to save them as strings in swift files. 5 | These files will be included in Swift package. 6 | To do so run: 7 | 8 | ./generateSwift.sh 9 | 10 | However this is not optimal nor nice. Refactor me if you can. 11 | -------------------------------------------------------------------------------- /Sources/JS/generateSwift.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd $(dirname $0) 3 | for filename in *.js; do 4 | newFilename="${filename/.js/.swift}" 5 | echo "Converting $filename to $newFilename" 6 | echo -e "// DO NOT EDIT!\n// Generated by $(basename -- "$0")" > $newFilename 7 | cat LICENSE >> $newFilename 8 | echo -e "public let ${filename/.js/}Script = \"\"\"\n$(grep -v '^//' $filename)\n\"\"\"" >> $newFilename 9 | done 10 | -------------------------------------------------------------------------------- /Sources/JS/printTypes.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | var varNumbers = getObjectKeys(types) 15 | // Do not use for in to avoid iterating over prototype properties 16 | for (var i=0;i 19 | #include 20 | #if defined(_WIN32) 21 | #include 22 | #endif 23 | 24 | #if defined(_WIN32) 25 | typedef SOCKET socket_t; 26 | 27 | // We need C11 or newer due to the use of `_Generic`. Windows does not have a 28 | // signed size type, so we construct the equivalent type by inspecting the type 29 | // of `size_t` and mapping from the signed to the unsigned version. 30 | #if __STDC_VERSION__-0 >= 201112l 31 | typedef __typeof__(_Generic((size_t)0, \ 32 | unsigned long long int : (long long int)0, \ 33 | unsigned long int : (long int)0, \ 34 | unsigned int : (int)0, \ 35 | unsigned short : (short)0, \ 36 | unsigned char : (char)0)) ssize_t; 37 | #endif 38 | #else 39 | typedef int socket_t; 40 | #define INVALID_SOCKET (-1) 41 | #endif 42 | 43 | socket_t socket_listen(const char* address, uint16_t port); 44 | socket_t socket_accept(socket_t socket); 45 | socket_t socket_connect(const char* address, uint16_t port); 46 | 47 | ssize_t socket_send(socket_t socket, const uint8_t* data, size_t length); 48 | ssize_t socket_recv(socket_t socket, uint8_t* buffer, size_t length); 49 | 50 | int socket_shutdown(socket_t socket); 51 | int socket_close(socket_t socket); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Targets/ChakraCore/README.md: -------------------------------------------------------------------------------- 1 | # [TODO] Target: ChakraCore 2 | 3 | To build ChakraCore (ch) for fuzzing: 4 | 5 | 1. Clone the ChakraCore from https://github.com/microsoft/ChakraCore 6 | 2. Apply chakracore.patch. The patch should apply cleanly to git commit 41ad58a9eebf8d52a83424c8fccfaacdb14105ec 7 | 3. Run the fuzzbuild.sh script in the ChakraCore root directory 8 | 4. FuzzBuild/Debug/ch will be the JavaScript shell for the fuzzer 9 | 10 | -------------------------------------------------------------------------------- /Targets/ChakraCore/fuzzbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2019 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ./build.sh --target-path=FuzzBuild --debug --static -j --cc=/usr/bin/clang-8 --cxx=/usr/bin/clang++-8 -------------------------------------------------------------------------------- /Targets/JavaScriptCore/README.md: -------------------------------------------------------------------------------- 1 | # Target: JavaScriptCore 2 | 3 | To build JavaScriptCore (jsc) for fuzzing: 4 | 5 | 1. Clone the WebKit mirror from https://github.com/WebKit/webkit 6 | 2. Apply Patches/\*. The patches should apply cleanly to the git revision specified in [./REVISION](./REVISION) 7 | (_Note_: If you clone WebKit from `git.webkit.org`, the commit hash will differ) 8 | 3. Run the fuzzbuild.sh script in the webkit root directory 9 | 4. FuzzBuild/Debug/bin/jsc will be the JavaScript shell for the fuzzer 10 | -------------------------------------------------------------------------------- /Targets/JavaScriptCore/REVISION: -------------------------------------------------------------------------------- 1 | a93223d586d16a983e11df71869382de5b450a5e 2 | -------------------------------------------------------------------------------- /Targets/JavaScriptCore/fuzzbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2019 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | export WEBKIT_OUTPUTDIR=FuzzBuild 18 | 19 | if [ "$(uname)" == "Darwin" ]; then 20 | ./Tools/Scripts/build-jsc --jsc-only --debug --cmakeargs="-DENABLE_STATIC_JSC=ON -DCMAKE_CXX_FLAGS='-fsanitize-coverage=trace-pc-guard -O3'" 21 | elif [ "$(uname)" == "Linux" ]; then 22 | ./Tools/Scripts/build-jsc --jsc-only --debug --cmakeargs="-DENABLE_STATIC_JSC=ON -DCMAKE_C_COMPILER='/usr/bin/clang-12' -DCMAKE_CXX_COMPILER='/usr/bin/clang++-12' -DCMAKE_CXX_FLAGS='-fsanitize-coverage=trace-pc-guard -O3 -lrt'" 23 | else 24 | echo "Unsupported operating system" 25 | fi 26 | -------------------------------------------------------------------------------- /Targets/Jerryscript/README.md: -------------------------------------------------------------------------------- 1 | # Target: JerryScript 2 | 3 | To build JerryScript for fuzzing: 4 | 5 | 1. Clone the JerryScript repository from https://github.com/jerryscript-project/jerryscript 6 | 2. Apply Patches/\*. The patches should apply cleanly to the git revision specified in [./REVISION](./REVISION) 7 | 3. Run the fuzzbuild.sh script in the jerryscript directory 8 | 4. ./build/bin/jerry will be the JavaScript shell for the fuzzer 9 | -------------------------------------------------------------------------------- /Targets/Jerryscript/REVISION: -------------------------------------------------------------------------------- 1 | 0f0041d720adfd83012e59435c2f1b555c1234d5 2 | -------------------------------------------------------------------------------- /Targets/Jerryscript/fuzzbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export CC=clang 4 | python tools/build.py --compile-flag=-fsanitize-coverage=trace-pc-guard --profile=es2015-subset --lto=off --compile-flag=-D_POSIX_C_SOURCE=200809 --compile-flag=-Wno-strict-prototypes --stack-limit=15 5 | -------------------------------------------------------------------------------- /Targets/QJS/README.md: -------------------------------------------------------------------------------- 1 | # Target: QuickJS 2 | 3 | To build QuickJS for fuzzing: 4 | 5 | 1. Clone the QuickJS mirror from https://github.com/horhof/quickjs 6 | 2. Apply Patches/\*. The patches should apply cleanly to the git revision specified in [./REVISION](./REVISION) 7 | 3. Build QuickJS with `make qjs` 8 | 4. The `qjs` binary will be the JavaScript shell for the fuzzer 9 | -------------------------------------------------------------------------------- /Targets/QJS/REVISION: -------------------------------------------------------------------------------- 1 | c389f9594e83776ffb86410016959a7676728bf9 2 | -------------------------------------------------------------------------------- /Targets/Spidermonkey/Patches/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Targets/Spidermonkey/Patches/.gitkeep -------------------------------------------------------------------------------- /Targets/Spidermonkey/README.md: -------------------------------------------------------------------------------- 1 | # Target: Spidermonkey 2 | 3 | To build Spidermonkey for fuzzing: 4 | 5 | 1. Clone the Firefox mirror from https://github.com/mozilla/gecko-dev 6 | 2. Run the fuzzbuild.sh script in the js/src directory of the firefox checkout 7 | 3. fuzzbuild_OPT.OBJ/dist/bin/js will be the JavaScript shell for the fuzzer 8 | -------------------------------------------------------------------------------- /Targets/Spidermonkey/REVISION: -------------------------------------------------------------------------------- 1 | 73330bf7355c0aef844c41d0d7eed2848e53c82f 2 | -------------------------------------------------------------------------------- /Targets/Spidermonkey/fuzzbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2019 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | FLAGS="-fsanitize-coverage=trace-pc-guard -g -DJS_MORE_DETERMINISTIC" 18 | 19 | export CXXFLAGS=$FLAGS 20 | export CC=clang-10 21 | export CXX=clang++-10 22 | 23 | mkdir fuzzbuild_OPT.OBJ 24 | cd fuzzbuild_OPT.OBJ 25 | /bin/sh ../configure.in --enable-debug --enable-optimize --disable-shared-js --enable-js-fuzzilli 26 | 27 | make -j$(getconf _NPROCESSORS_ONLN) 28 | -------------------------------------------------------------------------------- /Targets/V8/Patches/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P1umer/fuzzilli4wasm/5ecdc393366f9c4920eeda2c0bd934d0919a02f5/Targets/V8/Patches/.gitkeep -------------------------------------------------------------------------------- /Targets/V8/README.md: -------------------------------------------------------------------------------- 1 | # Target: v8 2 | 3 | To build v8 for fuzzing: 4 | 5 | 1. Follow the instructions at https://v8.dev/docs/build 6 | 2. Run the fuzzbuild.sh script in the v8 root directory 7 | 3. out/fuzzbuild/d8 will be the JavaScript shell for the fuzzer 8 | 9 | Note that sanitizer coverage for v8 is currently not supported on macOS as it is missing from v8's custom clang toolchain. 10 | -------------------------------------------------------------------------------- /Targets/V8/REVISION: -------------------------------------------------------------------------------- 1 | 8393133d6d6bb56923cce39a52884c264d8ded3d 2 | -------------------------------------------------------------------------------- /Targets/V8/fuzzbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2019 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | if [ "$(uname)" == "Darwin" ]; then 18 | # Sanitizer coverage doesn't work on macOS as it's not supported by the custom clang toolchain... 19 | gn gen out/fuzzbuild --args='is_debug=false dcheck_always_on=true v8_static_library=true v8_enable_slow_dchecks=true v8_enable_v8_checks=true v8_enable_verify_heap=true v8_enable_verify_csa=true target_cpu="x64"' 20 | elif [ "$(uname)" == "Linux" ]; then 21 | gn gen out/fuzzbuild --args='is_debug=false dcheck_always_on=true v8_static_library=true v8_enable_slow_dchecks=true v8_enable_v8_checks=true v8_enable_verify_heap=true v8_enable_verify_csa=true v8_fuzzilli=true sanitizer_coverage_flags="trace-pc-guard" target_cpu="x64"' 22 | else 23 | echo "Unsupported operating system" 24 | fi 25 | ninja -C ./out/fuzzbuild d8 26 | -------------------------------------------------------------------------------- /Targets/duktape/README.md: -------------------------------------------------------------------------------- 1 | # Target: duktape 2 | 3 | To build duktape for fuzzing: 4 | 5 | 1. Download the master branch of https://github.com/svaarala/duktape 6 | 2. Run `make duk-fuzzilli` 7 | 8 | The executable will be named `duk-fuzzilli`, in the duktape directory. -------------------------------------------------------------------------------- /Tests/FuzzilliTests/EnvironmentTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import Fuzzilli 17 | 18 | class EnvironmentTests: XCTestCase { 19 | 20 | func testJSEnvironmentConsistency() { 21 | // The constructor will already perform various consistency checks, so we don't repeat them here. 22 | let _ = JavaScriptEnvironment(additionalBuiltins: [:], additionalObjectGroups: []) 23 | } 24 | } 25 | 26 | extension EnvironmentTests { 27 | static var allTests : [(String, (EnvironmentTests) -> () throws -> Void)] { 28 | return [ 29 | ("testJSEnvironmentConsistency", testJSEnvironmentConsistency), 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/FuzzilliTests/InliningTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import Fuzzilli 17 | 18 | class InliningTests: XCTestCase { 19 | func testBasicInlining() { 20 | let fuzzer = makeMockFuzzer() 21 | let b = fuzzer.makeBuilder() 22 | 23 | let f = b.definePlainFunction(withSignature: FunctionSignature(withParameterCount: 3)) { args in 24 | b.beginIf(args[0]) { 25 | b.doReturn(value: args[1]) 26 | } 27 | b.beginElse() { 28 | b.doReturn(value: args[2]) 29 | } 30 | b.endIf() 31 | } 32 | var a1 = b.loadBool(true) 33 | var a2 = b.loadInt(1337) 34 | var r = b.callFunction(f, withArgs: [a1, a2]) 35 | b.unary(.BitwiseNot, r) 36 | 37 | let program = b.finalize() 38 | 39 | let reducer = InliningReducer() 40 | var inlinedCode = reducer.inline(f, in: program.code) 41 | 42 | // Must normalize the code after inlining. 43 | inlinedCode.normalize() 44 | 45 | XCTAssert(inlinedCode.isStaticallyValid()) 46 | 47 | let inlinedProgram = Program(with: inlinedCode) 48 | 49 | let u: Variable 50 | 51 | // Resulting program should be: 52 | a1 = b.loadBool(true) 53 | a2 = b.loadInt(1337) 54 | u = b.loadUndefined() 55 | r = b.loadUndefined() 56 | b.beginIf(a1) { 57 | b.reassign(r, to: a2) 58 | } 59 | b.beginElse { 60 | b.reassign(r, to: u) 61 | } 62 | b.endIf() 63 | b.unary(.BitwiseNot, r) 64 | 65 | let referenceProgram = b.finalize() 66 | 67 | XCTAssertEqual(inlinedProgram, referenceProgram) 68 | } 69 | } 70 | 71 | extension InliningTests { 72 | static var allTests : [(String, (InliningTests) -> () throws -> Void)] { 73 | return [ 74 | ("testBasicInlining", testBasicInlining), 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/FuzzilliTests/TestUtils.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | @testable import Fuzzilli 17 | 18 | extension Program: Equatable { 19 | // Fairly expensive equality testing, but it's only needed for testing anyway... :) 20 | public static func == (lhs: Program, rhs: Program) -> Bool { 21 | // We consider two programs to be equal if their code is equal 22 | let code1 = lhs.code.map({ $0.asProtobuf() }) 23 | let code2 = rhs.code.map({ $0.asProtobuf() }) 24 | return code1 == code2 25 | } 26 | } 27 | 28 | // Convenience variable constructor 29 | func v(_ n: Int) -> Variable { 30 | return Variable(number: n) 31 | } 32 | --------------------------------------------------------------------------------